Learning Modbus TCP with ControlThings.IO

Modbus TCP is a layer 7 communications protocol and a de facto standard in the field of industrial control systems. Having recently moved into the utilities sector I wanted to learn more about it. In this post I'll be taking a look at the Modbus TCP challenge included in the ControlThings.io ICS pentesting distro and hopefully learning a little about Modbus along the way. So let's get started!

The challenge itself is located at ~/Samples/Protocols/ModbusTCP. We have some pcaps, some reference material and some instructions as follows:

Plant1-ModbusTCP.pcap
This is an export of the ModbusTCP packets from the Plant1.pcap file
Source: https://www.cloudshark.org/captures/76038eaa4a3b

Modbus Challenges:
- Which IP address is the master on?
- How many slaves is the master talking to?
- Is the master writing any data to the slaves?
- Does the traffic spike in the middle related [SIC] to modbus?

So first of all, we need to understand what are these masters and slaves being referred to? I found an excellent write up with some diagrams on the Schneider Electronics Website (Schneider in fact invented Modbus in 1979, as it turns out). As they note:

 "The Modbus protocol exchanges information using a request-reply mechanism between a master (client) and a slave (server). The master-slave principle is a model for a communication protocol in which one device (the master) controls one or more other devices (the slaves). "

Additionally, we learn that: 
  • Only 1 master is connected to the network at a time.
  • Only the master can initiate communication and send requests to the slaves.
  • The master can address each slave individually using its specific address or all slaves simultaneously using address 0.
  • The slaves can only send replies to the master.
  • The slaves cannot initiate communication, either to the master or to other slaves.

Okay, this seems like enough information to get started, let's open the Plant1-ModbusTCP.pcap file in Wireshark and take a look:

So we can immediately see that we have a .10 host issuing queries and a whole bunch of others responding. Let's confirm our suspicion that 148.81.0.10 is the master by applying some display filters. First, let's see all the packets where .10 is the source:


Notice how everything it's sending is a Query. Now let's see the packets where it's the destination:


And as suspected, everything else is responding to it. So there's a definite answer to question 1 of the challenge. Remember that only the master can can initiate communications, and that all the slaves can do is reply to the master. What we can clearly see is 141.81.0.10 is the master and is issuing queries to multiple slaves that are responding to it. Okay next question, how many slaves are there? We can quickly find this using the Statistics->Endpoints options in Wireshark and looking at the IPv4 traffic:

So in addition to the master we have a total of 13 slaves it's talking to. (Notice the huge difference in the volume of traffic sent from the master compared to each individual slave). I found another great description of Modbus TCP here where it's stated that:

"Modbus will support up to 247 slaves from addresses 1 to 247 (JBUS 1 to 255) - address 0 is reserved for broadcast messages. In practice, the number of slave addresses that can be used is determined by the communications link that is chosen. For example, RS485 is limited to a total of 31 slaves."

So that's question 2 answered. The next question asks us whether the master is writing any data to the slaves. Let's have a look!

Well that seems pretty clear, we seem to have several different 'Read' operations being performed but also at least one 'Write' operation ('Write Multiple Coils'). But lets have a quick look at the Modbus specification to confirm this:

Okay, so here we see that Modbus supports many different read and write operations and the specification provides full details of each. Notice how the function number in the spec also corresponds to what is actually displayed in the packet itself: 'Write Multiple Coils' is function 15, 'Read Discrete Inputs' is function 2 etc. Nice!

On closer inspection, the majority of traffic in the capture appears to consist of the following 4 functions:

  • Function 1: Read Coils
  • Function 2: Read Discrete Inputs
  • Function 4: Read Input Registers
  • Function 15: Write Multiple Coils

So next, the final part of the challenge asks us to identify whether the spike in traffic in the middle of the capture is related to Modbus. Let's take a look at that now. First of all where is this spike? We can use the I/O Graph in Wireshark to analyse the traffic in this capture and this is what we get:

Hmmm ... no obvious spike there that I could see anyway. I reached out to ControlThings.io on their Discord and queried this. Shortly after I got the following reply from Justin Searle:

"Correct. There is no Modbus spike. And sorry, that question originates from the ~/Samples/Protocols/Combined/Plant1.pcap capture which is a superset of that Modbus capture and several other captures. If you check out that Plant1.pcap file, you will see there is a traffic spike in another protocol."

Okay, so sounds like we're done with Modbus for now, but let's check out that other pcap that Justin is talking about:

So immediately we can see multiple protocols are in use here, not just Modbus. Let's retry that traffic analysis and see if we can find the spike:

And there we have it! So now let's try and figure out what's causing it. By clicking directly at the top of the spike we can see that the traffic spikes at packet no. 28254, so let's take a look at that:

As we can see, this is indeed not a Modbus spike but instead appears to be caused by 148.81.0.10 sending CIP (Common Industrial Protocol) Multiple Service Packets to a range of other hosts that are then responding with Success messages.

After checking back in with Justin, I was reminded of the "Try Harder" days of OSCP. There is more to this part of the challenge than meets the eye as it turns out and CIP is not what's actually causing the spike! Using Justin's hints I modified my approach as follows:

First up, I changed the I/O graph to display bytes instead of the default, which is packets. This resulted in a much more prominent visualisation of the traffic spike as shown below:

To change the display to bytes you need to double-click on the the word "Packets" in the "Y Axis" column and this then brings up a drop-down control that lets you choose what you want shown on that axis.

Another improvement that lets us see the spike in even more detail is to change the default time-interval down from 1 second to 10ms or 100ms. The screenshot below shows the spike displayed with a 100ms interval:

So if it's not CIP, what is actually causing this spike? 

The first thing I wanted to do was to reduce the size of the problem space. I identified the packet numbers of the approximate start and end of the spike and used editcap to create a new pcap file containing only those packets I was interested in:

With only around 3000 packets, my new file (Spike.pcap) was much more manageable to work with. I took a look at the I/O graph of the new file, showing all the traffic:

Next, I created a bunch of traffic filters, one for each protocol in the new PCAP file and applied them:

As can be seen, the TDS protocol traffic matches the profile of the spike, accounting for significantly more traffic than the other protocols at this timestamp. So let's dig a little deeper and look at the traffic stats for the capture:


Interestingly, in my new capture file there were only 12 more TDS packets than Modbus packets during the period of the spike and the TDS packet count was only 2.5% higher than Modbus. Looking at this, I began to wonder whether TDS was really the source of the spike. However, returning to the I/O graph and viewing the size of the TDS traffic (red) compared to the Modbus traffic (black) it was pretty clear what's going on:

Although the packet count was similar, the overall TDS traffic size is much bigger than the Modbus traffic, with the Modbus peaking at just under 5000 bytes/100ms compared to over 25000 bytes/100ms for TDS.

I really enjoyed this exercise and it was a great way to dip a toe in the water with some of these industrial protocols for the first time. It was also great to learn some new features of Wireshark and to see how easy it is to fall into the trap of making incorrect assumptions about network traffic. 

Many thanks to Justin and the team at ControlThings.IO! Check out their Discord server and download ControlThings.IO here


Comments

Popular posts from this blog

Buffer Overflow Fun with Brainpan 1

Real World Web Application Security Testing

Modelling Security Concepts with Archimate