Post

Hacking a Modbus TCP Smart Street Light PLC – OT Pentesting Lab

Hacking a Modbus TCP Smart Street Light PLC – OT Pentesting Lab

📢 Introduction

Operational Technology (OT) environments rely heavily on legacy industrial protocols such as Modbus TCP for communication between PLCs, sensors, and SCADA systems. While these protocols remain widely deployed across critical infrastructure, they lack modern security controls.

The absence of authentication and encryption in Modbus TCP introduces significant risk when network segmentation is weak or misconfigured.

In this lab, I design and implement a simulated Smart Street Light PLC environment and perform a structured security assessment against it. The objective is to analyze how industrial process logic can be enumerated, understood, and ultimately manipulated through direct register and coil interaction.

This demonstration highlights the real-world implications of unsecured industrial communication protocols.


📚 Understanding Modbus TCP

Modbus TCP is one of the most widely deployed industrial communication protocols in Operational Technology (OT) environments. Originally developed in 1979 for serial communication, it was later adapted to run over TCP/IP networks, typically operating on port 502.

The protocol follows a simple client-server architecture:

  • The client (master) initiates requests
  • The server (slave/PLC) responds with requested data

In industrial systems, Modbus TCP is commonly used to exchange data between:

  • Programmable Logic Controllers (PLCs)
  • Sensors and actuators
  • Human Machine Interfaces (HMIs)
  • SCADA systems

📋 How Modbus Stores Data

Modbus devices organize process data into structured memory areas:

Data TypeAccessPurpose
CoilsRead/WriteBinary values (0 or 1), typically used for ON/OFF states
Discrete InputsRead OnlyBinary sensor data
Holding RegistersRead/Write16-bit values used for configuration and control
Input RegistersRead Only16-bit sensor values

In this lab, the Smart Street Light PLC relies primarily on:

  • Coils to represent control states (e.g., light status, manual override)
  • Holding registers to store threshold and ambient light values

🛠 Setting Up Your Lab Environment

This lab requires running several Python scripts to simulate a control system environment.

  • Some scripts create the target environment (PLC simulator)
  • Others are used to connect, probe, and analyze the system

⚠️ Note: These labs have been tested on Windows 11.
They may work on other systems, but compatibility is not guaranteed.


🖥 System Preparation (Windows)

1️⃣ Install Python

Download the latest version of Python from:

👉 https://www.python.org

During installation:

  • ✔️ Ensure “Add Python to PATH” is selected
  • This will also install pip (Python package manager)

2️⃣ Verify Installation

Open Command Prompt and run:

1
python --version

If installed correctly, you will see the Python version number.

3️⃣ Install Modbus Library

Install the required Modbus emulator module:

1
pip install pymodbus==3.2.0

4️⃣ Install Serial Dependency

Run the following command:

1
pip install pyserial-asyncio==0.6

5️⃣ Install Nmap

Download and install the latest version of Nmap:

👉 https://nmap.org/download/

Nmap will be used for network scanning and reconnaissance.

6️⃣ Download Lab Files

Clone or download the repository:

👉 https://github.com/harsh020100/Modbus-TCP-Security-Testing-Lab

You can use:

1
git clone https://github.com/harsh020100/Modbus-TCP-Security-Testing-Lab.git

Or download as ZIP from GitHub.


🚀 Launching Simulated OT Environment

To start the simulated control system, open a Command Prompt in the project directory and run the following command:

1
python Smart Street Light Controller.py

The first control system in this lab was intentionally designed to be simple and easily recognizable. When the environment is launched, a graphical interface representing a Smart Street Light Control Panel appears on the screen. This interface is known as a Human Machine Interface (HMI).

Smart Street Light HMI

The HMI is connected to a simulated Programmable Logic Controller (PLC) that emulates how a real-world street lighting controller would operate in an urban environment. In production systems, similar PLCs are used to control physical infrastructure such as street lamps, traffic systems, and other smart city components.

The HMI continuously retrieves values from the PLC using Modbus TCP to monitor and display key process parameters, including:

  • The current status of the street light (ON or OFF)
  • The configured lux threshold (setpoint) used to determine when the light should activate
  • The ambient light level, which is dynamically simulated to represent real environmental conditions
  • The operating mode, indicating whether the system is running automatically or under manual override

Through this interface, an operator can observe how changes in sensor values or configuration registers directly influence the control logic executed by the PLC.

For the purposes of this lab, it is important to ensure that the HMI window remains visible at all times, as it provides real-time feedback on how Modbus register and coil manipulation impacts the physical process being simulated.


🔍 Discovering the Modbus TCP Service

To determine whether the simulated OT environment exposes a Modbus TCP service, the local system is scanned for services listening on TCP port 502, the default port used by Modbus over TCP/IP.

1️⃣ Initial Network Scan

By default, Nmap scans only the top 1,000 most common ports. Since Modbus TCP (port 502) is not included in this default set, the port must be specified manually. Run the following command to scan the local host:

1
nmap 127.0.0.1 -p 502

⚠️ Note: Your Nmap results may differ depending on what services are currently running on your system.

2️⃣ Interpreting the Results

If the scan shows that TCP port 502 is open, this indicates that a service is actively listening on that port.

nmap scan result

However, an open port alone does not definitively confirm that the service is Modbus. Technically, any application can be configured to listen on any port. Further probing and protocol interaction are required to verify the presence of a Modbus service and to begin analyzing the exposed industrial process.

3️⃣ Identifying Modbus Service Details

To gather additional information about the service running on TCP port 502, Nmap can be used with a Modbus-specific Nmap Scripting Engine (NSE) script.

Run the following command:

1
nmap 127.0.0.1 -p 502 --script modbus-discover

This script attempts to communicate using the Modbus protocol and retrieve identifying information from the target service. If the service supports Modbus TCP, the output may include details such as device identification data and supported functionality.

nmap script for Modbus

Successful interaction confirms that the exposed service is responding to Modbus protocol requests, indicating the presence of a Modbus-capable device within the simulated environment.


📈 Modbus Memory Enumeration

1️⃣ Launch the Modbus Polling Tool

Start the polling interface using:

1
python Mbap_Polling_Tool.py

The Modbus Polling Tool window should appear.

Mbap Polling Tool GUI

2️⃣ Connect to the Target

In the Modbus Host IP field, enter the IP address of the target host. For this lab, use:

1
127.0.0.1

Select the checkbox for Read Coils.

Click Start Polling

Mbap Polling Tool For Reading Coil

3️⃣ Observing Coil Values

The polling tool connects to the simulated PLC and reads the first 10 coils. In this environment, the PLC contains 10 coils and 10 holding registers, though real industrial PLCs typically contain significantly larger memory areas.

Observe the coil values for approximately 30 seconds.

At first glance, the displayed values may appear to be random binary data with no obvious meaning. This reflects a common challenge in OT/ICS security analysis — raw process data must be interpreted before it becomes meaningful.

4️⃣ Identifying Control Logic

One value should immediately stand out: a single coil periodically changes state while others remain constant.

This value corresponds to a control state reflected directly in the HMI. Since coils represent binary control signals, the value indicates an operational condition of the street lighting system rather than a sensor measurement.

5️⃣ Reading Holding Registers

Next, enable Read Registers in the Modbus Polling Tool.

The 10 holding registers stored in the PLC will now be displayed alongside the coil values.

Mbap Polling Tool for Reading HR

Observe the register values for approximately 30 seconds and compare them with the parameters displayed in the HMI.

6️⃣ Mapping Registers to Process Values

By correlating register values with the HMI display, the following process variables can be identified:

  • One register represents the configured lux activation threshold

  • One register represents the ambient light sensor value

  • These values change dynamically as part of the simulated environment

The ambient light value fluctuates over time, simulating real-world lighting conditions that influence whether the street light turns ON or OFF.

NOTE: Modbus addressing is zero-based. While interfaces may display register positions sequentially, internal addressing begins at index 0.


💻 Attacking the Street Light Control Process

1️⃣ Understanding the Target Control Variable

The street light is controlled by a comparison between two values stored in PLC memory:

  • Holding Register 4 → Lux Threshold (setpoint)
  • Holding Register 6 → Ambient Light Sensor Value

The PLC logic operates as follows:

IF ambient light < threshold → street light ON
IF ambient light ≥ threshold → street light OFF

Since the ambient light value continuously fluctuates between approximately 300 and 900 lux, modifying the threshold value provides a reliable method to influence system behavior.

2️⃣ Launching the Modbus Client

Connect to the PLC using the Modbus client utility:

1
python Mbap_client.py 127.0.0.1

A menu of read and write operations will appear.

Mbap_cient.py

3️⃣ Verifying Current Process Values

Select:

1
3 → Read Registers

When prompted:

1
2
3
Starting address → 0

Number of registers → 10

This displays the PLC’s current process values, including the threshold and ambient light level.

Read Register

4️⃣ Forcing the Street Light OFF

To ensure the street light remains OFF, the threshold can be set to a very small value. Since the ambient light value is always higher than a near-zero threshold, the PLC logic will never activate the light.

Select:

1
4 → Write Registers

When prompted:

1
2
3
Starting address → 4

New value → 10

Write Register

Observe the HMI after applying the change.

HMI Changes

5️⃣ Result

By setting the lux threshold to an extremely low value:

  • Ambient light is always greater than the threshold
  • The PLC logic continuously evaluates the environment as sufficiently bright
  • The street light remains OFF regardless of actual conditions

🚨 Security Impact

This demonstration highlights a critical weakness in Modbus-based industrial control systems: the absence of authentication and integrity protection for control commands.

Because Modbus TCP does not verify the origin or authorization of requests, any system with network access to the PLC can modify process-critical parameters. In this lab, altering a single configuration register permanently changed the behavior of the street lighting process.

By lowering the lux threshold value, the control logic continuously evaluated environmental conditions as sufficiently bright, preventing the street light from activating. This manipulation required no exploitation of software vulnerabilities, no credential access, and no privileged system interaction — only network-level communication with the PLC.

In real-world environments, similar actions could result in:

  • Disruption of safety-critical infrastructure
  • Persistent denial of operational functionality
  • Unauthorized control over physical processes
  • Loss of visibility or monitoring accuracy
  • Operational downtime or public safety risk

This demonstrates that in OT environments, process manipulation is often achieved through legitimate protocol use rather than traditional exploitation techniques.


🏁 Conclusion

This lab demonstrated how a simulated Smart Street Light control system can be discovered, analyzed, and manipulated using standard Modbus TCP communication. Through structured reconnaissance, memory enumeration, and targeted register modification, the industrial process was successfully influenced without disrupting system availability.

The exercise reinforces a core reality of Operational Technology security: legacy communication protocols prioritize reliability and interoperability over security. When these systems are exposed to untrusted networks, they become susceptible to direct process manipulation.

Understanding how industrial devices communicate — and how their internal logic maps to physical outcomes — is essential for both defenders and security researchers working in OT environments.


🧪 Educational Purpose

This environment was developed for educational and research purposes to demonstrate how industrial communication protocols operate and how process logic can be analyzed from a security perspective.

The techniques shown in this lab should only be performed in controlled environments designed for testing and learning.


Break responsibly. Secure deeply. 🔐

This post is licensed under CC BY 4.0 by the author.