Flow Override
hackthebox
Task: Exploit S7comm protocol to disrupt at least 3 pieces of equipment in a water treatment plant. Solution: Used python-snap7 to fuzz PLC Data Block memory mapping, enabled manual_mode to bypass safety logic, then caused overflow/overheat conditions via persistent writes.
$ ls tags/ techniques/
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
Flow Override — HackTheBox
Description
A trusted friend gives you full access to his water treatment plant for a security test. The Siemens PLCs use S7comm — Can you break in and disrupt at least three pieces of equipment?
Two endpoints provided:
- 154.57.164.65:30088 — S7comm PLC (Siemens CPU 315-2 PN/DP)
- 154.57.164.65:32570 — Web HMI (Flask/Werkzeug)
Analysis
Web HMI Reconnaissance (Port 32570)
The web interface served an SVG diagram of a water treatment plant with a /status JSON API endpoint. JavaScript on the page polled /status every second and displayed real-time plant state. Crucially, the code contained:
if (ret.flag) { alert(ret.flag); }
This revealed that the flag would be returned via the status API when win conditions were met.
Plant Components
The water treatment plant has 5 pieces of equipment, each with a health status:
| # | Equipment | Key Parameters |
|---|---|---|
| 1 | In-Water Tank | input/output valves, fill percentage |
| 2 | Chlorine Tank | input/output valves, fill percentage |
| 3 | Heat Exchanger | hot/cold side valves, temperature readings |
| 4 | Mixer Tank | mixing speed, duration, volume, fill percentage |
| 5 | Storage Tank | input/output valves, fill percentage |
The /status endpoint returned JSON with all parameters including manual_mode, valve states, fill percentages, temperatures, and equipment status strings (e.g., "healthy", "over flow", "over heat"). The flag field was empty until win conditions were met.
S7comm PLC Connection (Port 30088)
Connected to the Siemens PLC using python-snap7:
import snap7 client = snap7.client.Client() client.connect('154.57.164.65', 0, 1, 30088) # rack=0, slot=1
PLC Memory Mapping via Byte-by-Byte Fuzzing
...
$ grep --similar
Similar writeups
- [hardware][free]Factory— HackTheBox
- [hardware][free]Steel Mountain— HackTheBox
- [pwn][free]0xDiablos— hackthebox
- [pentest][free]Interpreter (Mirth Connect → f-string Injection)— hackthebox
- [hardware][free]Dressrosa Reactor— hackthebox