HTB: Watch Tower Challenge
Watch Tower - HackTheBox Challenge Writeup
Challenge Information
| Field | Value |
|---|---|
| Challenge Name | Watch Tower |
| Category | Forensics (Misc) |
| Difficulty | Very Easy |
| Author | d3vn0mi |
Description
An infrastructure monitoring system flagged abnormal behavior and triggered a network capture. The goal is to review the capture, identify what information the intruders collected, and — more importantly — what they altered in the process, since that’s where the flag is hidden.
Solution
The provided artifact was a network capture (tower_logs.pcapng) from an industrial control system (ICS/SCADA) environment. Loading it into tshark showed the entire capture was Modbus/TCP traffic — a common OT/ICS protocol — exchanged between an HMI (Human-Machine Interface) host and a PLC (Programmable Logic Controller).
Breaking the traffic down by Modbus function code revealed three distinct request types:
- Function 1 — Read Coils (304 occurrences): the “collection” activity the prompt hinted at, the intruder reading device state.
- Function 16 — Write Multiple Registers (114 occurrences): the “alteration” activity, i.e. actual writes to PLC registers.
- Function 15 — Write Coils (2 occurrences): a handful of additional writes.
Since the challenge specifically calls out data that was collected and altered, the Write Multiple Registers (func code 16) requests originating from the HMI were the natural place to dig deeper. Rather than the register values being interesting, the register addresses (the reference_num field of each write request) turned out to be the exfiltration channel: read as a sequence and decoded as ASCII, the addresses spelled out the flag. The register values themselves were decoy noise designed to distract from the real payload.
Key Steps
Step 1: Recover the real capture file
The handed-out challenge file was a 0-byte stub. The actual pcapng was inside a password-protected zip in the solve output directory.
# Extract the real capture (password: hackthebox)unzip -o -P hackthebox a12c73aa-897e-4875-816f-d5cc577609bf.zip# -> yields tower_logs.pcapngStep 2: Confirm the protocol and get basic capture info
# Sanity-check the capture is readable and get summary statscapinfos tower_logs.pcapngStep 3: Inspect the protocol hierarchy / traffic
# Dump src/dst IPs plus Modbus function code and reference for every frametshark -r tower_logs.pcapng -T fields \ -e frame.number -e ip.src -e ip.dst \ -e modbus.func_code -e modbus.reference_numThis confirmed the capture was 100% Modbus/TCP between the HMI and the PLC.
Step 4: Tally function codes to spot “collect vs. alter” activity
# See distribution of Modbus function codestshark -r tower_logs.pcapng -T fields -e modbus.func_code | sort | uniq -c | sort -rnOutput showed:
1→ 304 hits (Read Coils — data collection)16→ 114 hits (Write Multiple Registers — data alteration)15→ 2 hits (Write Coils)
Step 5: Isolate the Write Multiple Registers (func 16) traffic from the HMI
# Filter to write-multiple-register requests from the HMI hosttshark -r tower_logs.pcapng \ -Y "modbus.func_code==16 && ip.src==192.168.1.150" \ -T fields -e frame.number -e modbus.reference_numStep 6: Compare register addresses vs. register values
# Dump the actual register VALUES for the same write requests, for comparisontshark -r tower_logs.pcapng \ -Y "modbus.func_code==16 && ip.src==192.168.1.150" \ -T fields -e modbus.reference_num -e modbus.regval_uint16The register values looked like arbitrary noise, while the register addresses, taken in packet order and converted from decimal to ASCII, spelled out a readable HTB-flag-shaped string — confirming the addresses were the actual exfiltration/alteration channel.
Step 7: Decode the addresses as ASCII and reconstruct the flag
# Pseudocode for the decode: each modbus.reference_num value maps# to an ASCII character code; concatenating them in frame order# reconstructs the flag string.python3 -c "addrs = [...] # reference_num values extracted from Step 5, in frame orderprint(''.join(chr(a) for a in addrs))"printf 'HTB{REDACTED}\n' > flag.txtTools Used
| Tool | Purpose |
|---|---|
unzip | Extract the password-protected capture archive |
tshark | Read and filter the Modbus/TCP pcapng, extract per-field data |
capinfos | Quick sanity-check / summary of the capture file |
python3 | Decode the extracted register addresses from decimal to ASCII |
Key Learnings
- Industrial protocols like Modbus/TCP have almost no built-in confidentiality or integrity protection — every field, including ones not normally considered “data” (like a register address), is fair game for abuse as a covert channel.
- When a challenge distinguishes between data collected vs. data altered, map that directly onto protocol semantics: Modbus function codes cleanly separate reads (
1= Read Coils) from writes (16= Write Multiple Registers), which is a strong signal for where to focus. - Don’t assume the “obvious” field carries the payload — here the register value was a decoy, and the real signal was hiding in the address field of the write requests. Always check adjacent fields (addresses, counts, unit IDs) before concluding a field is just noise.
- Filtering with
tshark -Ydisplay filters (modbus.func_code,ip.src, etc.) is fast enough to iterate through several hypotheses directly at the command line without needing a GUI.
Flag
HTB{REDACTED}