Modbus was designed in 1979 for serial communication between programmable logic controllers. DNP3 was developed in 1993 for SCADA communication in the electric utility sector. Neither protocol was designed with authentication, integrity protection, or confidentiality in mind — the assumption was a physically isolated network where security derived from the difficulty of reaching the wire, not from the protocol itself.

That assumption has not survived contact with modern OT network architecture. IT/OT convergence, remote access requirements, and supply chain connections mean that reaching the Modbus or DNP3 wire — or the Ethernet network carrying it as Modbus/TCP and DNP3/IP — is increasingly achievable for a motivated attacker. The protocols themselves provide no defence.

Modbus: The Attack Surface

Modbus is a master-slave protocol: the master (typically an HMI or SCADA server) sends read or write commands; the slave (PLC, RTU, meter) responds. Modbus/TCP encapsulates these commands in TCP, typically on port 502.

There is no authentication in Modbus. Any device that can send a Modbus PDU (Protocol Data Unit) to a slave device’s port 502 can issue commands. Function codes include:

  • 0x01 / 0x02: Read coils/discrete inputs (sensor states)
  • 0x03 / 0x04: Read holding/input registers (process values)
  • 0x05 / 0x06: Write single coil/register (control)
  • 0x0F / 0x10: Write multiple coils/registers (bulk control)
  • 0x2B: Encapsulated Interface Transport (device identification)

An attacker with network access to a Modbus slave can:

  1. Enumerate device configuration using function code 0x2B to retrieve device ID, vendor, firmware version
  2. Read all process values using function codes 1-4 to map the process being controlled
  3. Issue control commands using function codes 5, 6, 15, 16 to modify setpoints, open/close valves, toggle circuit breakers
  4. Replay captured packets: Captured legitimate Modbus traffic can be replayed directly — there is no nonce, sequence number, or timestamp in standard Modbus that would distinguish a replayed command from a legitimate one

Modbus Attack Tools

Modbus exploitation is accessible. Publicly available tools include:

# Modbus-cli (Ruby): enumerate and write to Modbus slaves
modbus read 192.168.1.100 %MW0 100    # Read 100 words from holding registers
modbus write 192.168.1.100 %MW100 999 # Write value 999 to register 100

# PLCscan: Modbus device discovery and enumeration
python plcscan.py 192.168.1.0/24 -p 502

# Scapy: craft arbitrary Modbus packets
from scapy.contrib.modbus import ModbusADURequest, ModbusPDU03ReadHoldingRegistersRequest
pkt = ModbusADURequest()/ModbusPDU03ReadHoldingRegistersRequest(referenceNumber=0, wordCount=10)
send(pkt, dst="192.168.1.100", dport=502)

These tools are included in Kali Linux and are used by both legitimate OT security assessors and attackers.

DNP3: Extended Surface, Extended Exposure

DNP3 is more complex than Modbus: it supports time-stamped event data, data integrity checking (CRC per data block), and configurable unsolicited reporting. It is the dominant protocol for electric utility SCADA, water systems, and pipeline monitoring. DNP3/IP uses port 20000.

DNP3 authentication exists but is not widely deployed. DNP3 Secure Authentication (SA) Version 5 (defined in IEEE 1815-2012) provides challenge-response authentication using HMAC. However, SA adoption in the field is low: it requires device firmware support, shared key management infrastructure, and operational testing — none of which are trivial in ageing SCADA environments. Most deployed DNP3 devices operate without SA.

Without SA, DNP3 attacks include:

Master spoofing: Sending commands with a valid master address (DNP3 addresses are 2 bytes; the master address is configuration data on the device). A spoofed master can issue unsolicited poll responses, trigger demand resets, or issue Direct Operate (CROB) commands to binary outputs.

Function code injection: DNP3’s Application Control Block (ACB) identifies the function code. An attacker on the network can inject:

  • FC 0x03: Direct Operate (immediate command execution)
  • FC 0x04: Direct Operate No Ack (command with no confirmation request)
  • FC 0x81: Response (inject false data into the master’s view)
  • FC 0x82: Unsolicited Response (force false event data into the SCADA display)

False data injection: DNP3 carries process values that the SCADA system displays to operators. Injecting false DNP3 responses creates false readings on the operator HMI — potentially masking an actual process anomaly or triggering unnecessary operator action.

This is the technique used by the Pipedream/INCONTROLLER toolkit (attributed to a state actor by CISA and Mandiant in 2022): Pipedream included a module called MOUSEHOLE specifically designed to intercept and modify DNP3 traffic to inject false data into EMS/SCADA displays while issuing control commands to underlying devices.

Real-World Exploitation Patterns

Ukraine power grid attacks (2015, 2016): Both the Black Energy/Sandworm attacks used HMI access to issue legitimate SCADA commands through compromised operator stations, but demonstrated deep familiarity with DNP3 and Modbus protocol operation. The 2016 Industroyer malware included dedicated communication modules for Modbus, DNP3, and IEC 60870-5-104.

Pipedream (INCONTROLLER), 2022: The toolkit disclosed by CISA included components for Modbus (TAGRUN) and DNP3 (MOUSEHOLE), designed for OT environment reconnaissance and manipulation. MOUSEHOLE intercepted DNP3 traffic at layer 2, modified it, and re-injected it — a man-in-the-middle capability for industrial protocols.

Water utility intrusions, 2021-present: Multiple water utility attacks (Oldsmar, FL; Bay Area facilities) involved adversaries with access to HMI systems that communicated with PLCs via Modbus. In several incidents, attackers modified setpoints directly through the HMI interface rather than raw protocol manipulation — but the protocol offered no barrier if they had chosen to bypass the HMI.

Detection Without Disruption

Modbus and DNP3 run on production networks where a false positive triggering an alert for every anomalous packet could create operational friction. Detection needs to be passive and rely on behavioural baselines rather than signatures that generate noise.

Recommended passive detection rules:

Write commands from unexpected sources

# Zeek (formerly Bro) Modbus policy
event modbus_message(c: connection, headers: ModbusHeaders, is_orig: bool) {
    if (headers$function_code in [5, 6, 15, 16]) {  # Write function codes
        local src = c$id$orig_h;
        if (src !in expected_masters) {
            NOTICE([$note=Modbus::Unexpected_Write_Source,
                    $msg=fmt("Write command from non-master IP: %s", src),
                    $conn=c]);
        }
    }
}

DNP3 Direct Operate from unexpected master

# Zeek DNP3 detection
event dnp3_application_request_header(c: connection, is_orig: bool, fc: count) {
    if (fc == 3 || fc == 4) {  # Direct Operate, Direct Operate No Ack
        if (c$id$orig_h !in approved_dnp3_masters) {
            NOTICE([$note=DNP3::Unexpected_Direct_Operate,
                    $msg=fmt("Direct Operate from unapproved master: %s", c$id$orig_h),
                    $conn=c]);
        }
    }
}

Volume anomaly: bulk read followed by write

A common pre-attack reconnaissance pattern is bulk reading of coils/registers (mapping the process) followed by targeted writes. Alerting on unusual volumes of read requests from an IP that has not historically issued reads is effective:

# Suricata rule: Modbus function code 3 (Read Holding Registers) high volume
alert tcp any any -> $OT_NETWORK 502 (
    msg:"MODBUS Excessive Read Holding Registers - Possible Reconnaissance";
    flow:to_server,established;
    content:"|00 03|"; offset:7; depth:2;
    threshold: type threshold, track by_src, count 100, seconds 60;
    classtype:policy-violation;
    sid:9000001; rev:1;
)

Commercial OT network detection platforms (Dragos Platform, Claroty xDome, Nozomi Networks Guardian, Tenable OT Security) include Modbus and DNP3 behavioural analytics as built-in capabilities, with process baseline learning that distinguishes normal engineering station queries from anomalous scan-like activity.

Hardening Options

Authentication is the correct long-term fix, but deployment constraints limit what’s immediately achievable:

  • DNP3 SA v5: Deploy on new equipment and on devices receiving firmware updates. Require vendors to include SA in future purchase specifications.
  • Modbus/TCP ACL at the network layer: Enforce firewall rules that permit Modbus/TCP only between defined master and slave pairs. This does not protect against a compromised master but eliminates external network access.
  • OT DMZ for IT/OT data exchange: Data historians and ERP integrations should access OT data via a read-only OPC-UA proxy in a DMZ, not via direct Modbus/DNP3 connections that cross the IT/OT boundary.
  • Protocol-aware firewall inspection: Next-generation firewalls with OT protocol support (Fortinet FortiGate with OT module, Palo Alto Networks with ICS/SCADA signatures) can enforce function code allowlisting at the network layer — blocking write function codes from non-authorised sources without requiring protocol changes on the devices themselves.
Tags
ModbusDNP3SCADAprotocol-securityOTICSunauthenticatedreplay-attackspoofingenergywaterPipedreamINCONTROLLERnetwork-detectionDragosClaroty