// BTLO  ·  Incident Response

Testa

BTLO Easy OT, Modbus

Scenario

Jurassic Resourcing is an independent upstream and midstream oil company operating in the Persian Gulf region. Terminal JRL-OT-01 at Bandar Al-Hajar sits approximately 65 km north of the Strait on the eastern coast. The terminal receives crude and refined product from onshore gathering fields via the PP-101 trunk pipeline, holds product in storage, and loads it onto product tankers at Berth B-01 for export. Oil shipment services have been suspended — the task is to investigate the packet capture, cross-reference the operations handbook, and determine what the attacker did and when.


Methodology

Protocol and Environment Orientation

The operations handbook establishes the OT segment as 172.20.0.0/24 with three authorised devices:

IP System Should Write?
172.20.0.2 Ship Cargo Controller Responds only
172.20.0.3 Terminal TLCS Yes, to ship only
172.20.0.4 Engineering WS No, reads only

Any other IP on the segment is unauthorised. The handbook also flags that Modbus/TCP has no authentication — any host that reaches the segment can read or write any register on either controller. Access to the OT segment is equivalent to standing at the control panel.

Modbus function codes relevant to this investigation:

FC Purpose Analyst Note
FC01 Read coils Expected for shore stop polling
FC04 Read input registers Expected for process value monitoring
FC05 Write single coil Review carefully
FC06 Write single holding register Review carefully
FC16 Write multiple holding registers Review carefully

Identifying the Unauthorised Host

Opening ot-forensic-inc.pcap in Wireshark, the Conversations view immediately surfaces the attacker. All traffic is within 172.20.0.0/24 so the handbook IP table is required to identify what doesn’t belong — 172.20.0.5 is not an authorised device.

Decoding Modbus Traffic

Wireshark’s Modbus dissector defaults to port 502. This environment runs Modbus on non-standard ports — 5020 (Terminal TLCS) and 5021 (Ship Cargo Controller). Both ports need to be added in Preferences → Protocols → Modbus/TCP before the mbtcp filter will work. With dissection active, filtering on attacker traffic reveals the full picture:

ip.src == 172.20.0.5 && (tcp.port == 5020 || tcp.port == 5021) && mbtcp

Reconnaissance Phase

The attacker’s session opens with read operations — FC04 (Read Input Registers) and FC01 (Read Coils) against both controllers. This maps the process: confirming the loading operation is active, establishing which coils correspond to which actuators, and identifying the TLCS holding register layout before issuing any writes.

Write Phase — Terminal TLCS Register

The first write is an FC16 (Write Multiple Registers) to the Terminal TLCS (172.20.0.3). Expanding the Modbus layer reveals the target:

Function Code: Write Multiple Registers (16)
Reference Number: 0
Register 0 Value (UINT16): 9000

The operations handbook maps HR 40001 (Reference Number 0 in the Modbus PDU) as the Terminal Command Register. Writing value 9000 to this register is flagged in the event log as an unknown command — the system rejected it, but the intent was process disruption via an invalid setpoint.

Write Phase — Cargo Tank Valve Opens

Filtering specifically on attacker traffic to the Ship Cargo Controller on port 5021 exposes the coil write sequence:

ip.src == 172.20.0.5 && tcp.port == 5021 && mbtcp

Five FC05 (Write Single Coil) frames with Data: ff00 target the ship controller. Cross-referencing the handbook coil map for 172.20.0.2:5021:

|Coil Addr|Tag|Written| |—|—|—| |00001|XV_MANIFOLD_OPEN_CMD|✓ (manifold, not cargo)| |00003|LV_C1_OPEN_CMD|✓| |00004|LV_C2_OPEN_CMD|✓| |00005|LV_C3_OPEN_CMD|✓| |00006|LV_C4_OPEN_CMD|✓| Five coil writes in total, but only four target cargo tank valves (LV-C1 through LV-C4, coils 00003–00006). The manifold write (coil 00001) opens the pipeline path — it is a separate actuation, not a cargo tank valve.

Operations Shutdown — SHORE_STOP_ACTIVE

With four cargo tank valves forced open simultaneously, tanks begin filling uncontrolled. The ship-side High-High level alarm triggers the shore stop relay. SHORE_STOP_ACTIVE (coil 00000 on 172.20.0.2, R only) asserts to 1, signalling the terminal to halt all loading operations.

The HTTP event log from the terminal API confirms the sequence:

{"event_type":"ESD","message":"Shore stop received from MV STEGOSAURUS — HH level alarm","ts_utc":"2026-04-30T12:52:37+00:00"}

To find the precise Modbus-layer transition timestamp, filter FC01 responses from the ship controller and find the first frame where Bit 0 flips from 0 to 1:

ip.src == 172.20.0.2 && tcp.srcport == 5021 && modbus.func_code == 1

Frame 4479 — Bit 0: 1, Bit Value: True — Arrival Time: Apr 30, 2026 12:52:37.255230 UTC.


Attack Summary

Phase Action
Reconnaissance FC04/FC01 reads enumerate process state across Terminal TLCS and Ship Cargo Controller
Process Manipulation FC16 write pushes value 9000 to Terminal TLCS HR 40001 (rejected as invalid)
Actuation FC05 writes open XV_MANIFOLD (coil 1) and LV-C1 to LV-C4 (coils 3–6) on ship controller
Impact Uncontrolled tank fill triggers HH alarm, SHORE_STOP_ACTIVE asserts, loading halted

IOCs

Type Value
IP (Unauthorised Host) 172[.]20[.]0[.]5
Target (Terminal TLCS) 172[.]20[.]0[.]3:5020
Target (Ship Cargo Controller) 172[.]20[.]0[.]2:5021
Protocol Modbus/TCP (non-standard ports 5020/5021)
Written Register HR 40001 (Reference 0) — value 9000
Written Coils 00001, 00003, 00004, 00005, 00006
SHORE_STOP_ACTIVE Transition 12:52:37.255230 UTC, Apr 30 2026

MITRE ATT&CK

Technique ID Description
Network Service Discovery T1046 FC01/FC04 read sweeps enumerate coil and register state across both controllers
Manipulate I/O Image T1565.001 FC16 write to TLCS command register; FC05 writes assert cargo valve coils
Valid Accounts T1078 Presence on OT segment implies use of compromised or stolen access credentials
Endpoint Denial of Service T1499.004 SHORE_STOP_ACTIVE assertion forces emergency shutdown of Berth B-01 operations

Defender Takeaways

Modbus has no authentication — the OT segment perimeter is the only control. Once 172.20.0.5 reached the segment, it had unrestricted read and write access to both controllers. Strict network allowlisting at the OT boundary — permitting only .2, .3, and .4 to communicate over ports 5020 and 5021 — would have blocked the attacker entirely before a single Modbus frame was issued.

Write function codes from unrecognised sources should trigger immediate alerts. In normal operation, FC05 and FC16 writes originate from a known, small set of engineering workstations during defined maintenance windows. A passive OT monitoring solution (Claroty, Dragos, Nozomi) baselining this behaviour would have flagged the first FC05 from .5 in real time rather than after operations had already been disrupted.

High-consequence coil writes should not be instantaneous. The attacker opened four cargo tank valves in rapid sequence with no operator confirmation required. A two-step confirmation requirement for safety-critical actuations — manifold opens, loading valve commands — would break automated tooling that issues writes sequentially and provide an operator the opportunity to intervene.

Non-standard Modbus ports create dissection blind spots. Running Modbus on 5020/5021 instead of 502 means default IDS Modbus signatures and Wireshark dissection rules won’t fire without explicit configuration. Document all non-standard protocol ports and ensure monitoring tooling is tuned to match the actual environment, not the assumed standard.


Which unauthorised source IP appears on the OT segment?
Click flag to reveal 172.20.0.5
What Modbus function codes does the unauthorised host use, and which are write operations?
Click to reveal answer FC01(R),FC04(R),FC05(W),FC16(W)
What value did the unauthorised host attempt to write to Terminal TLCS holding register 40000?
Click flag to reveal 9000
How many cargo tank valves did the attacker open?
Click to reveal answer 4
At what time does the ship-side SHORE_STOP_ACTIVE coil transition from 0 to 1 bringing down operations for the facility?
Click flag to reveal 12:52:37:255230
🔒
// active lab
writeup locked
withheld in accordance with platform guidelines
to avoid spoiling live challenges.
password provided to recruiters on request.