// HackTheBox  ·  writeup

ICS_FuelStation

HackTheBox Medium tsharkWireshark
ICS/OT

Scenario

Analyze the ICS-FuelStation compromise using PCAP network traffic analysis.

Executive Summary

An internal host on the fuel station subnet swept the network with a rapid SYN scan and found a tank gauge controller exposing an FTP service and the Veeder-Root automated tank gauge port. The operator then opened a TCP session to the gauge and ran the vendor’s plain-text query commands in sequence: inventory, delivery, leak test, shift and alarm status reports. Having mapped the station, they wrote new product names to the gauge, replacing the tank labels with a ransom message and a contact address. The gauge protocol has no authentication, so every read and write rode in clear text and is fully recoverable from the capture.

Artifacts

Timestamp note: Q1 and Q2 are capture times in UTC (frame.time_utc). The tank gauge prints its own clock inside each report, and Q10, Q11 and Q12 use those device times.

Discovery

Q1 — The Attacker started off by scanning the subnet. When did port scanning activity start?

Filter for bare SYNs (no ACK) and group by source. One internal host, 10.10.32.130, fires them at sequential addresses and ports. The first SYN is the scan start, shown in UTC.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -Y 'tcp.flags.syn==1 && tcp.flags.ack==0 && ip.src==10.10.32.130' -T fields -e frame.time_utc -e ip.dst -e tcp.dstport | head -1
Click flag to reveal 2025-08-27 07:27:31
Q2 — When did the port scanning activity stop?

The same SYN list shows a burst of about a thousand probes that ends within three seconds. Four further SYNs appear later from the same host, but those are the follow-up connections to the services found open, not part of the sweep. The end of the burst is the stop time.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -Y 'tcp.flags.syn==1 && tcp.flags.ack==0 && ip.src==10.10.32.130' -T fields -e frame.time_utc -e ip.dst -e tcp.dstport | sed -n '1000,1005p'

Gotcha: The last SYN from the scanner is not the end of the scan. The later SYNs (07:28:46 onward) are service connections to open ports, so stop at the end of the burst.

Click to reveal answer 2025-08-27 07:27:34
Q3 — Please identify the IP Address of the internal asset used by the threat actor?

From Q1 the scanner is the host sending the SYN burst. It sits on the same subnet as the gauge controller it later queries.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -Y 'tcp.flags.syn==1 && tcp.flags.ack==0 && ip.src==10.10.32.130' -T fields -e frame.time_utc -e ip.dst -e tcp.dstport | head -1
Click flag to reveal 10.10.32.130
Q4 — Which port other than the tank gauge system was open on the system?

Look at the SYN-ACKs returned to the scanner. The target 10.10.32.128 answers on two ports only: the gauge port (Q5) and FTP on port 21.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -Y 'tcp.flags.syn==1 && tcp.flags.ack==1 && ip.dst==10.10.32.130' -T fields -e ip.src -e tcp.srcport | sort | uniq -c
Click to reveal answer 21
Q5 — Which port was being used by the ICS system?

The other open port on the same target is TCP 10001. The scanner’s later connections go there, and the payload is tank gauge protocol text, which confirms it.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -q -z follow,tcp,ascii,1004
Click flag to reveal 10001
Q6 — Can you confirm the Vendor of the Automated tank gauge system being used on the field?

The TCP 10001 stream opens with commands such as I20100, the in-tank inventory request. That command format is the Veeder-Root TLS serial protocol. The vendor was reasoned from the command set and is not printed anywhere in the traffic.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -q -z follow,tcp,ascii,1004
Click to reveal answer Veeder
Q7 — What is the Name of the petroleum pump that was being attacked?

Every report returned by the gauge carries the station name as its header line, below the date and time.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -q -z follow,tcp,ascii,1005
Click flag to reveal LD Refinery
Q15 — What was the hostname of the machine running the tank gauge software?

The gauge host 10.10.32.128 announces itself in its DHCP Request, and the hostname option holds the answer.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -Y 'dhcp && ip.src==10.10.32.128' -T fields -e dhcp.option.hostname
Click flag to reveal LD-PETROLEUM-SITE-6

Collection

Q8 — What is the function code used by the attacker for unauthorized access of data for delivery reports?

In the second TCP 10001 stream (tcp.stream 1005) the client sends a series of SOH-prefixed commands. The one returning a DELIVERY REPORT is I20200.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -q -z follow,tcp,ascii,1005
Click to reveal answer I20200
Q11 — The Attacker got access to the Delivery reports for the station. When was the time for the last delivery?

The I20200 response lists the delivery for tank 1 with START and END rows. The last delivery is the END timestamp. This is the gauge’s own clock, so it differs from the capture time of the request.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -q -z follow,tcp,ascii,1005
Click flag to reveal 2025-08-27 02:24
Q9 — At what time was a leak detected from one of the tanks?

The leak test report is I20300. Tank 2 (FUELOIL) shows TEST STATUS: ON and a LEAK DETECTED AT line. Tank 1 has no leak data, and tanks 3 and 4 read HEALTHY.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -q -z follow,tcp,ascii,1005 | grep -A2 'TANK 2'
Click flag to reveal 03:14 AM
Q10 — What was the status of the 1s product in the inventory?

The alarm and status report is I20500, listing a status per tank. The first tank (gasoline) reads as a fill in progress.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -q -z follow,tcp,ascii,1005 | grep -A6 I20500
Click to reveal answer FILLING-IN-PROGRESS
Q12 — When did the attacker gain access to the site’s shift reports?

Shift reports are I20400. The report header carries the date and time of the request, 08/27/2025 07:35, which matches that command’s position in the stream.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -q -z follow,tcp,ascii,1005 | grep -A3 I20400
Click to reveal answer 2025-08-27 07:35
Q13 — What was the temperature of the 2nd Gasoline product at the time of the incident?

The inventory (I20100) lists four tanks, and the second is not gasoline but FUELOIL. Its TEMP column is the value wanted, and it is unchanged in the post-tamper inventory.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -q -z follow,tcp,ascii,1005 | grep -A6 'IN-TANK INVENTORY'

Gotcha: The question says “2nd Gasoline product”, but tank 2 is FUELOIL. Read the second row of the table, not the label.

Click flag to reveal -34.66
Q14 — What is the name of the product in the Fourth tank?

Tank 4 in the original inventory is the jet fuel tank. The same row later carries a truncated ransom string, so read the earlier report.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -q -z follow,tcp,ascii,1005 | grep -E '^ +4 '
Click to reveal answer JetFuel

Impact

Q16 — The Attacker manipulated the tank gauge values and changed the product names to deliver a message to the organization and staff, demanding a ransom payment. What was the ransom amount demanded by the attacker?Answer Format: (2 dogecoin)

Late in the stream the client sends four S602 commands that set the product labels for tanks 1 to 4. They read, in order, a hacked notice, a demand for payment, a contact line and an address. The 07:39 inventory shows the new names in place of the products. The amount is in the second label.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -q -z follow,tcp,ascii,1005 | grep S602
Click to reveal answer 50 Ethereum
Q17 — What was the email address associated with the attacker?

The fourth S602 write carries the contact address. The inventory view truncates it to 20 characters, so take the full string from the write command.

tshark -r LD-ICS-FUELSTATION-COMPROMISE.pcapng -q -z follow,tcp,ascii,1005 | grep S60204
Click flag to reveal [email protected]

Attack Summary

PhaseAction
DiscoverySYN scan of the subnet from an internal host, about a thousand probes in three seconds
DiscoveryService connections to FTP (21) and the tank gauge port (10001) on the gauge host
CollectionInventory, delivery, leak test, shift and alarm status reports read over the unauthenticated gauge protocol
ImpactS602 writes replace the four product names with a ransom message and contact address
ImpactA later inventory request shows the tampered names

Forensic Timeline

Timestamp (UTC)Event
2025-08-27 07:27:31First SYN of the scan from 10.10.32.130
2025-08-27 07:27:34Scan burst ends
2025-08-27 07:28First I20100 inventory request on TCP 10001
2025-08-27 07:32Second session to the gauge opens; I20100 repeated
2025-08-27 07:34Delivery report (I20200) and leak test (I20300) requested
2025-08-27 07:35Shift report (I20400) and alarm status (I20500) requested
2025-08-27 07:39Inventory shows the ransom text in the product names

IOCs

TypeValue
Attacker host10.10.32.130
Target10.10.32.128 (LD-PETROLEUM-SITE-6), TCP 21 and 10001
Emaillegiongroup@protonmail[.]com
Ransom textYOU_ARE_HACKED, PAY_US_50_ETH, CONTACT_US_AT

MITRE ATT&CK

TechniqueIDDescription
Network Service DiscoveryT1046SYN scan finding FTP and the gauge port
Remote System DiscoveryT1018Sweep of the subnet for live hosts
Monitor Process State (ICS)T0801Inventory, delivery, leak and shift reports read from the gauge
Modify Parameter (ICS)T0836S602 writes changing the product labels
Defacement: Internal DefacementT1491.001Ransom text shown in the tank inventory

Defender Takeaways

The tank gauge protocol on TCP 10001 has no authentication. Anyone who can reach the port can read every report and change labels. Put it behind a firewall that admits only the site’s management systems, so an ordinary internal host cannot reach it.

A thousand SYNs in three seconds from one internal host is an easy alert. The scan came from inside the subnet, so perimeter controls saw nothing. East-west scan detection or flow baselining on the OT segment would have flagged it at 07:27.

Write commands to the gauge deserve their own detection. Read commands (I20x00) are routine for monitoring, while S602 is a configuration change. A sensor that parses the protocol can alert on any write from a host that is not the known head-end.

The gauge host also runs FTP. The FileZilla service on port 21 is an extra way in and a way to move files. Close it unless there is a documented need.

Conclusion

The attacker needed no exploit: an internal foothold, a port scan and plain-text commands were enough to read the station’s reports and rewrite the tank labels into a ransom note. Anything that can reach an unauthenticated ICS port can change what operators see.