pentesting-modbus.md (7830B)
1 --- 2 title: "502/tcp - Pentesting Modbus Protocol" 3 section: "Network Services" 4 sectionSlug: "network-services-pentesting" 5 sourcePath: "src/network-services-pentesting/pentesting-modbus.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-modbus.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # 502/tcp - Pentesting Modbus Protocol 14 15 ## Basic Information 16 17 Modicon introduced **Modbus** in 1979 for communication among industrial devices. Current specifications use client/server terminology, although many tools and deployed serial systems still say master/slave.<sup>[[3]](#references)</sup> 18 19 **Default Modbus/TCP port:** 502. The separate Modbus Security profile wraps Modbus in TLS with X.509 authentication and uses port 802; do not assume every Modbus deployment is necessarily plaintext.<sup>[[3]](#references)[[4]](#references)</sup> 20 21 ```text 22 PORT STATE SERVICE 23 502/tcp open modbus 24 ``` 25 26 **Modbus TCP** carries a **seven-byte MBAP header** followed by the Modbus PDU:<sup>[[3]](#references)</sup> 27 28 - **Transaction ID**: matches requests and responses 29 - **Protocol ID**: normally `0x0000` 30 - **Length**: remaining bytes in the frame 31 - **Unit ID**: important when a Modbus/TCP gateway forwards traffic to serial/RTU devices 32 - **Function Code + Data**: the actual read/write/diagnostic operation 33 34 The **Unit ID** is frequently ignored by native Modbus/TCP devices, but it becomes critical when the target is a **TCP-to-RTU gateway**. In that case, you usually need to enumerate multiple unit/slave IDs to reach the device behind the bridge. 35 36 Legacy Modbus/TCP traffic on port 502 is typically **plaintext and unauthenticated**, so passive captures often reveal: 37 38 - The in-use **unit/slave IDs** 39 - Which **function codes** the process actually accepts 40 - The **register map** being polled by the HMI/SCADA server 41 - Whether writes are rare enough that replaying one stands out immediately 42 43 ## Enumeration 44 45 ### Automatic 46 47 ```bash 48 nmap -sV --script modbus-discover -p 502 <IP> 49 nmap --script modbus-discover --script-args='modbus-discover.aggressive=true' -p 502 <IP> 50 msf> use auxiliary/scanner/scada/modbusdetect 51 msf> use auxiliary/scanner/scada/modbus_findunitid 52 ``` 53 54 `modbus-discover` is useful because it tries to enumerate **legal slave IDs** and extract **device identification** data such as vendor and firmware strings.<sup>[[1]](#references)</sup> 55 56 ### Passive Recon 57 58 If you can sniff traffic, extract the parameters you need before sending anything intrusive: 59 60 ```bash 61 tshark -r modbus.pcap -Y modbus \ 62 -T fields -e ip.src -e ip.dst -e modbus.unit_id \ 63 -e modbus.func_code -e modbus.reference_num -e modbus.word_cnt 64 ``` 65 66 Useful Wireshark display filters: 67 68 ```bash 69 modbus 70 modbus.func_code == 3 71 modbus.func_code == 16 72 modbus.exception_code 73 ``` 74 75 ### Manual Enumeration with PyModbus 76 77 ```bash 78 python3 -m pip install pymodbus 79 ``` 80 81 ```python 82 from pymodbus.client import ModbusTcpClient 83 84 host = "10.10.10.10" 85 unit = 1 86 client = ModbusTcpClient(host, port=502) 87 client.connect() 88 89 print(client.read_coils(address=0, count=16, device_id=unit)) 90 print(client.read_holding_registers(address=0, count=10, device_id=unit)) 91 print(client.read_device_information(device_id=unit)) 92 93 client.close() 94 ``` 95 96 The current PyModbus client uses zero-based protocol addresses and names the unit argument `device_id`; older releases used `slave` or `unit`, so match the example to the installed version.<sup>[[5]](#references)</sup> 97 98 Notes: 99 100 - **Addressing is a common pitfall**: many operators document holding register `400001`, while libraries expect the **zero-based offset** (`0`). 101 - When crossing a **gateway**, keep the same TCP endpoint and iterate the **Unit ID**. 102 - Some servers expose only a subset of function codes and return Modbus exception responses for unsupported ones. 103 104 ### Offensive Tooling 105 106 ```bash 107 git clone https://github.com/TacticalGator/modbuster 108 cd modbuster && pipx install . 109 modbuster getfunctions <IP> 110 modbuster read -s 1 <IP> 400001 10 111 modbuster diag -s 1 <IP> 112 ``` 113 114 Other useful frameworks/tools:<sup>[[6]](#references)</sup> 115 116 - `smod`: function-code enumeration, UID brute force, fuzzing modules 117 - `scapy.contrib.modbus`: packet crafting for unsupported or custom workflows 118 119 ## Interesting Function Codes 120 121 The following standardized function codes and the `0x2B/0x0E` device-identification MEI are defined in the Modbus application protocol specification.<sup>[[3]](#references)</sup> 122 123 ### Read / Recon 124 125 - `0x01` / `0x02`: read coils / discrete inputs 126 - `0x03` / `0x04`: read holding / input registers 127 - `0x11`: report server ID 128 - `0x2B/0x0E`: **Read Device Identification** (vendor, product code, revision, optional objects) 129 - `0x08`: diagnostics, especially interesting on serial devices and some gateways 130 131 ### Write / Impact 132 133 These are the first function codes to validate carefully during authorized testing because they can directly alter process state: 134 135 - `0x05`: write single coil 136 - `0x06`: write single holding register 137 - `0x0F`: write multiple coils 138 - `0x10`: write multiple holding registers 139 - `0x16`: mask write register 140 - `0x17`: read/write multiple registers 141 142 ### Less Common but Worth Testing 143 144 These are not universally implemented, but when present they are valuable because defenders often monitor them less: 145 146 - `0x14`: read file record 147 - `0x15`: write file record 148 - `0x18`: read FIFO queue 149 150 A practical workflow is: 151 152 1. Find the valid **Unit ID**. 153 2. Verify read-only access with `0x01`-`0x04`. 154 3. Pull **device identification** with `0x2B/0x0E`. 155 4. Enumerate supported function codes. 156 5. Only then validate whether write functions are accepted. 157 158 ## Scapy Packet Crafting 159 160 Scapy ships Modbus packet definitions for common read/write primitives and **Read Device Identification**.<sup>[[6]](#references)</sup> 161 162 ```python 163 from scapy.contrib.modbus import ModbusADURequest, ModbusPDU2B0EReadDeviceIdentificationRequest 164 from scapy.all import sr1 165 166 pkt = ModbusADURequest(transId=1, unitId=1) / ModbusPDU2B0EReadDeviceIdentificationRequest() 167 resp = sr1(pkt, timeout=2) 168 if resp: 169 resp.show() 170 ``` 171 172 This is useful when you need to: 173 174 - Replay a packet seen in a PCAP with only minimal edits 175 - Test uncommon function codes not exposed cleanly by higher-level clients 176 - Validate how a target reacts to malformed length/function combinations during lab work 177 178 ## What Usually Leads to Impact 179 180 In real assessments, attackers rarely start with memory corruption. The usual path is: 181 182 - Sniff or enumerate the correct **Unit ID** and register ranges 183 - Learn the meaning of the values from traffic patterns or vendor docs 184 - Replay or slightly modify legitimate **write** requests 185 - Abuse weak process assumptions such as "this register is only written by the HMI" 186 187 Recent Modbus research and datasets keep emphasizing the same attacker behaviors: **query flooding, false data injection, brute-force writes, replay, and malformed length/frame handling**. Those are usually more realistic test cases than hunting for a single vendor-specific CVE.<sup>[[2]](#references)</sup> 188 189 ## References 190 191 - [1] [Nmap NSE: modbus-discover](https://nmap.org/nsedoc/scripts/modbus-discover.html) 192 - [2] [MOSTO: A toolkit to facilitate security auditing of ICS devices using Modbus/TCP](https://zaguan.unizar.es/record/127649) 193 - [3] [Modbus Organization - Specifications and implementation guides](https://www.modbus.org/modbus-specifications) 194 - [4] [Modbus/TCP Security Protocol Specification](https://www.modbus.org/file/secure/modbussecurityprotocol.pdf) 195 - [5] [PyModbus client documentation](https://pymodbus.readthedocs.io/en/latest/source/client.html) 196 - [6] [Scapy Modbus contrib-layer documentation](https://scapy.readthedocs.io/en/latest/api/scapy.contrib.modbus.html)