Now we have our plan, let's develop and test our attack strategy.
We'll use Scapy, but instead of running it from the command line,
we'll import the library to a Python 3 script and use the
functionality that offers Let's take a look at what our Python
script might look like if we implement our plan.
As is usual when reviewing Python scripts, it's easiest to start at the end.
The main part of our script sits in an infinite while True loop.
We first create a variable called frames which holds six sniffed packets.
We then test to see if the function code,
reference number,
and the bit and byte counts that we identified are found in packet 4 or index 3.
It's useful to note that the bit count of hexadecimal 0a,
which is ASCII 10, is the new line or line feed count to /n,
which is how Scapy interprets it.
If we find this string in packet 4,
then we know we have the Write Multiple Coils packet,
and the ACK we want is at packet 6 or index 5.
We then use sendp to send the malicious payload
generated by the build_packet function.
The build_packet function defines each layer of the packet in turn,
starting with layer 2, the Ethernet layer,
which we specify here with the default values that Scapy grabs from the network.
The IP layer, which specifies the source and destination IP addresses,
is built from the ack_packet IP layer at index 1.
Layer 4, the TCP layer, which specifies the source and destination ports,
the sequence and acknowledge numbers, and the TCP window,
is built from the ack_packet TCP layer at index 2.
As already mentioned, Scrapy doesn't natively support Modbus,
but it does allow us to specify our own layers.
To do this, we can create a Modbus class that specifies the Modbus layer.
A layer is comprised of a concatenated list of fields with a list of
fields being held in an attribute named fields_desc.
Scapy supports a number of data types,
including a ShortField representing a 2‑byte hexadecimal number,
such as ca01, and a ByteField which is a 1‑byte hexadecimal number,
such as 0A.
In the Modbus layer, we therefore specify all the fields needed,
such as the server ID or unit identifier,
the function code, the bit count, and so on.
The class definition specifies default values for each field,
but these can be overridden when the Modbus object is created.
Of particular interest here is the Data field.
This is the field that sets the bit values of each of the coils; 1 is on,
0 is off.
Without extensive testing,
which is likely to be detected or cause system faults
while reading the system documentation,
which we may be able to find on the network somewhere,
we have no way of telling what control element each bit controls.
We'll therefore trust Douglas Adams and go with the
answer to the ultimate question of life,
the universe, and everything, 42.
Forty‑two in binary is 101010.
So whichever control element is connected to coil one is turned on,
or activated, the control element at coil two is turned off,
or deactivated, and so on.
This is done for demonstration purposes only.
Don't do this on real or live systems.
Switching unknown control elements on or off randomly can be dangerous.
Layer 5 is the Modbus layer created from the class.
In this instance, we used the default values provided in the class definition.
Finally,
we build a malicious packet by contaminating the layers
from 2 to layer 5 with a forward slash.
We're now ready to launch our attack.
Well, that's pretty underwhelming.
We get confirmation that the attack has been launched
and that we're injecting packets,
but we have no idea what's happening or even if we're having any impact at all.
Let's take a look at Wireshark to see if we can figure out what's going on.
In this packet, we see we have a normal Write Multiple Coils packet,
which has its Data field set to ca01.
This is one of the four standard data payloads that can be
identified during normal operations for this process.
Following the Write Multiple Coils response in the ACK, we see some
interesting ARP traffic. In our Python script,
Scapy builds the Ethernet layer using values it grabs from the network.
So when our script launches, an ARP request is made asking the host with IP
address 192.168.48.136, which is our target to tell the host at
192.168.48.140, which is our attacking machine. Scapy then uses the MAC
address of the target to build the Ethernet layer.
The next packet is our injected malicious packet.
How do we know this?
Well, firstly, in the normal order of things,
we should see a Read Discrete Inputs packet next, but we see a Write
Multiple Coils packet, so something's not right. Secondly,
we see a shorter Data field of hexadecimal 2a, which is not one of
the four standard data payloads we see during normal operations.
Instead, hexadecimal 2a is 42 in decimal, the payload we set in
our malicious Modbus packet.
However,
what follows this tells us that all is not well. The
next packet has the FIN, ACK flag set,
which indicates the end of our data transmission, and our TCP
connection is reset. In the subsequent packets, the TCP connection
reset appears to be caused by the fact that are injected packet is
seen as a spurious retransmission,
which means that Wireshark believes it has seen the ACK to our Write
Multiple Coils request before. But our code is in an infinite loop,
and it keeps on trying to inject packets.
This could go on for some time, but eventually the traffic will get
completely tied up, and the target will get fed up,
stop listening to us, and terminate the connection. This really goes to
illustrate the point that ICS attacks are hard. They're not like IT
systems. Their behavior isn't predictable, and it's difficult to infer or
anticipate behavior. And this is why the ICS extension to the Lockheed
Martin kill chain is so valuable.
We're currently in the test and develop stage, and it seems to have some way
to go before we can deploy our attack in a live system.
This is interesting insofar as it goes, but we still don't know what effect
are injected packets are having on the target system.
Well, fortunately for us during our reconnaissance activities,
we found an open VNC connection.
Let's assume that we were able to obtain some valid user credentials and we
were able to gain access to an engineering workstation desktop environment
which shows real‑time images of the factory operations.
We can see a control panel with start and stop buttons.
When the Start button is pressed,
the green lamp turns on, the conveyor starts, and boxes begin to move.
When the parcels reach the diffuse sensor near the end of the conveyor,
the conveyor stops and the picker loads its arm, grabs the box, and
swings through 90° to place the box on the pallet.
The conveyor then restarts. Boxes continue to move along the conveyor,
being lifted off one at a time, and placed onto the pallet.
However, when we launch our attack, things change.
The conveyor doesn't stop when the diffuse sensor is activated,
and a box falls off the end of the conveyor.
However, the picker arm lowers and tries to pick up a box that isn't there.
This then causes subsequent boxes to be pushed around the picker
arm and then to fall off the end of the conveyor.