Using a SCADALink IP100 SCADA Terminal Server to Analyze MODBUS RTU Traffic
Playing with MODBUS and a few different scenarios for a Machine in the Middle
MODBUS RTU Interaction
Is it Possible to Read and Manipulate an Existing MODBUS RTU Connection from the Command Line?
Recently I attended the SANS ICS410 course and during one of the labs we used some tools in Linux to read and write registers and coils within a simulated MODBUS environment. MODBUS TCP/IP is easy to analyze using tools like Wireshark or Arkime, but what about MODBUS RTU traffic?
Test Environment
- Arduino Micro Controller acting as a MODBUS Server (currently runs a walk in meat locker I have in my shop)
- Ignition Environment acting as a MODBUS Client and HMI (Virtual Machine)
- Digi IAPs acting as DB9 Serial Ports for the VM
- Microhard Nano 920 Radios providing wireless connectivity between Ignition and Arduino
- SCADALink IP100 SCADA Terminal Server
- ModPoll MODBUS Master Simulator
Initial Testing
MODBUS RTU utilizes serial communication, in my case TIA-232. I shouldn't be able to interrupt this connection from the same serial port if the connection is already open, but let's try anyways by running the following command from the MODBUS client:
.\modpoll -b 9600 -p none -m rtu -a 2 -r 1 -c 2 COM6
The modpoll syntax is as follows (full details can be pulled using modpoll -h):
- -b 9600 = baudrate of 9600
- -p none = no parity
- -m rtu = modbus rtu
- -a 2 = server address of 2
- -r 1 = starting register of 1
- -c 2 = read two registers
- COM2 = the comm port that the MODBUS client is using to poll the MODBUS server
As I suspected, the Serial Port is already open, and spamming that same command as fast as I can will not change that.
How Can I See This Traffic?
Depending on where I am situated on the network, I might be able to analyze this traffic from the Engineering Work Station, or perhaps the end device has some tools built in to view MODBUS traffic, but what if I don't have access to either of these pieces of equipment. What if I am somewhere in the middle of this traffic? Can I look at traffic 'on the wire'?
There are a couple tools I am aware of out there that will tap into serial traffic, all of which require us to break the line momentarially to install (similar to a network tap). There are also arbitrators that direct MODBUS traffic and may allow us to interact with the traffic while enroute. I am going to attempt to use a SCADALink IP100 SCADA Terminal Server. There are two reasons for this:
- I should be able to interact with the MODBUS traffic
- I have one sitting on the shelf
Configuration of the IP100 was fairly intuitive. For some reason I cannot connect to this device through the ethernet port, and instead had to use the serial protocol to talk to it.
I set it up as a Serial Mux as shown below:
Here is a diagram illustrating the serial traffic flow:
If we use the SCADALink GUI to monitor traffic on either COM3 or COM2, we should see the same traffic flowing through the SCADALink IP100 between the Ignition SCADA host and the Arduino. In the example below I am looking at COM2 and will be comparing it to what the Ignition platform sees.
Here is what the IP100 sees:
And here is what Ignition sees:
The tags we want to look at are: Analogpin0, Analogpin1, Cooler_Armed, and Relay1
So What Does That Mean?
Some of you will look at this and recognize that the data in the IP100 serial string matches the data in Ignition, but let's break it down just in case you aren't familiar with how to do that.
Reviewing the 2nd to last line of the above screenshot we see the following:
TX 02 03 00 00 00 02 C438
Breaking this down we can interpret it the request as:
- 02 - MODBUS server address
- 03 - MODBUS function code (03 = read holding registers)
- 0000 - first register required (30,000)
- 0002 - number of registers to read (2)
- C438 - cyclic redundancy check (will maybe cover in a different blog post)
In plain english, the MODBUS client is reaching out to the MODBUS network and saying:
"Can modbus server number 2 tell me what analog input values you are currently reading in registers 30,000 through 30,001?"
Looking at the last line of the screenshot we see the following:
RX 02 03 04 00 12 00 13 28 FB
This can be broken down as follows:
- 02 - MODBUS server address
- 03 - MODBUS function code
- 04 - the number of bytes to follow
- 00 - hex value of the first upper register read
- 12 - hex value of the first lower register read
- 00 - hex value of the second upper register read
- 13 - hex value of the second lower register read
- 28FB - cyclic redundancy check
Converting the combined first register (0012) from hex to decimal gives you a value of 18. Converting the combined second register (0013) from hex to decimal gives you a value of 18.
Or, in plain english, the MODBUS server is replying to the MODBUS client and saying:
"Register 30,000 is currently reading 18 and register 30,001 is currently reading 19."
The other traffic (MODBUS function code 01) could be broken down as well to show the statuses of the 'Cooler_Armed' tag and the 'Relay1' tag, but these will be less interesting as both of those values are currently 0, and you should be able to do that conversion quickly in your head. If you are interested in looking at this yourself, examine the rows of text from the IP100 monitor that start with TX 02 01 or RX 02 01.
So far we have gone to a not insignificant level of effort to prove that an existing serial tool and SCADA host behave like they should. So what next?
Returning to modpoll
So what happens now if we use the same modpoll request we tried earlier, now that the serial mux has been installed?
We will use the same command as before except we need to move to a new COM port (port 4 on the serial mux):
.\modpoll -b 9600 -p none -m rtu -a 2 -r 1 -c 2 COM6
Now we receive a different output:
Moving over to the IP100, still reviewing COM2, we should now see the traffic increase as we have two MODBUS servers polling the same device.
If you look at the screenshot above and compare it to the previous capture, you will notice that we now see a '1/1L' as a new addition to the data. This is the second data stream being send out to the modpoll tool.
If you dig a little further you will notice that the data is the same (as we would expect).
So What?
If you refer back to my initial question, we have now answered half of it. Can we read MODBUS RTU data from a CLI? Yes we can.
But can we manipulate it? Let's find out...
Writing to a MODBUS Register
Let's expand on this, can I use the same tool to write data to the MODBUS server and cause unexpected behaviors in my meat locker control?
Without going into all of the details of how the meat locker works, I'll provide a brief control narrative.
The controller has the following variables associated with it:
- Ambient Temperature Sensor - this determines what the temperature is inside the cooler
- Condensor Temperature - this monitors the temperature of the condensor
- Cooler Target Temperature - the setpoint of the temperature I want the cooler to be
- Condensor Low Temperature Cut Out - the setpoint that turns off the cooler if the condesor starts to freeze off
- ESD - when disengaged (discrete reading of 0) the cooler will not run
- Condensor Relay - turns the air cooling unit on or off
There are some deadbands and timers involved that aren't illustrated here, but at a basic level the cooler operates as follows:
If you decoded the MODBUS function 1 commands, or refer back to the tag snapshot from Ignition, you know that the cooler is currently ESD'd and the condensor is not running.
Based on the logic path above, the cooler should not run, but what happens if I try to tell it to run from modpoll?
Let's try the following command:
.\modpoll -b 9600 -p none -m rtu -a 2 -r 1 -t 0 -c 1 COM6 1
which breaks down like this:
- -b 9600 = baudrate of 9600
- -p none = no parity
- -m rtu = modbus rtu
- -a 2 = server address of 2
- -r 1 = starting register of 1
- -t 0 = discrete output data type
- COM6 = the comm port that the MODBUS client is using to poll the MODBUS server
- 1 = the value to write to the register
We see the following in powershell:
and we see this in the IP100 monitor (can you break it down?):
and finally we see this in Ignition:
Success!!
Takeaway
Referring back to the original question, can I view and manipulate MODBUS data from a command line interface, the answer is yes.
Now what can we learn from this?
Should the cooler have turned on even though the ESD wasn't satisfied? No, it shouldn't have. Behind the scenes there is some sloppy programming that should have accounted for this situation.
Can you confidently say that your own environments don't have similar sloppy programming?
What if we expand this out to the analog variables coming in? Because they update constantly from their intended MODBUS server, I could change them, but they would be over written again the next time that the MODBUS client polls the server. That may not seem like much of a problem but I can think of a few scenarios that could be problematic.
- SCADA communication on remote wellsites - I've seen sites that poll as seldom as once a day, what if I manipulated a variable and you didn't know about it until 24 hours later?
- Persistent MODBUS Spamming - What if you expected to see the number 10 for a variable and I kept sending you 1,000? Your system would likely bounce back and forth between the two repeatedly. (This is why you should assign upper and lower bounds specific to each variable, not what the register can hold). What if you had a shutdown set at 100?
I can think of a several nefarious examples that could be developed from what I learned during this exercise, but I won't outline them here. What I do find interesting is you could likely drop a device like this into a network and it seems reasonable it wouldn't be detected for a while, especially in remote networks spanning large geographical areas. If you consider the lower poll intervals of installations like this, you could likely get away without an arbitrator at all and simply hijack a portion of the serial radio network to accomplish the same thing.
Serial data can be a tough thing to secure. Considering what I discovered in this exercise, it really emphasizes the need for physical security and routine inspection of installations, and perhaps a judicious application of tamper tape.