- Remove undeployed challenges: Phantom_Byte, Cr4cK_w1f1, Lain_Br34kC0r3 V1, Lain_VS_Knights, Lets_All_Love_UART, AETHER_NET, Last_Train_451, Web3/ - Sync 24 solve/ files from main CTF-Espilon repo - Update all READMEs with real CTFd final scores at freeze - Add git-header.png banner - Rewrite README: scoreboard top 10, edition stats (1410 users, 264 boards, 1344 solves), correct freeze date March 26 2026
85 lines
2.5 KiB
Markdown
85 lines
2.5 KiB
Markdown
# Operating Room -- Solution
|
|
|
|
## Overview
|
|
Modbus TCP server simulating a hospital operating room control system.
|
|
The player must discover the correct unit ID, map the registers, reverse
|
|
the XOR-encoded state machine, and execute 6 timed transitions.
|
|
|
|
## Steps
|
|
|
|
### 1. Discover Unit ID
|
|
Scan Modbus unit IDs (slave addresses). The server only responds to **unit ID 13**.
|
|
Default unit ID 1 returns Modbus exceptions.
|
|
|
|
```python
|
|
from pymodbus.client import ModbusTcpClient
|
|
client = ModbusTcpClient("HOST")
|
|
client.connect()
|
|
for uid in range(1, 20):
|
|
r = client.read_holding_registers(0, 1, slave=uid)
|
|
if not r.isError():
|
|
print(f"Unit ID {uid} responds!")
|
|
```
|
|
|
|
### 2. Map Registers
|
|
|
|
Read holding registers 0-255 on unit 13:
|
|
|
|
- **Registers 0-19**: Operating room telemetry (temperature, humidity, pressure, O2, etc.)
|
|
- **Register 13**: `0x4C4E` ("LN" -- Lain easter egg)
|
|
- **Register 19**: `0x0D13` -- this is the XOR key
|
|
- **Registers 100-105**: State machine (state, encoded hint, timer, transitions, error, key)
|
|
- **Register 105**: `0x0D13` -- XOR key copy (confirms reg 19)
|
|
- **Register 110**: Write target (trigger register)
|
|
- **Registers 200-215**: All zeros (flag area, populated after completion)
|
|
|
|
### 3. Understand the State Machine
|
|
|
|
- Register 100 = current state (starts at 0)
|
|
- Register 101 = encoded hint
|
|
- Register 105 = XOR key (0x0D13)
|
|
- Decode: `expected_value = reg_101 XOR 0x0D13`
|
|
- Write `expected_value` to register 110 to advance the state
|
|
- Each transition must happen before register 102 (timer) reaches 0
|
|
|
|
### 4. Execute Transitions
|
|
|
|
| State | Subsystem | Decoded Value | Source |
|
|
|-------|-----------|--------------|--------|
|
|
| 0 | HVAC | 220 | reg 0 (temperature) |
|
|
| 1 | Pressure | 15 | reg 2 (pressure) |
|
|
| 2 | O2 | 50 | reg 3 (O2 flow) |
|
|
| 3 | Ventilation | 1200 | reg 4 (fan RPM) |
|
|
| 4 | Lighting | 800 | reg 5 (lux) |
|
|
| 5 | Safety | 4919 (0x1337) | special |
|
|
|
|
### 5. Read Flag
|
|
|
|
After all 6 transitions, register 100 = 7 (complete).
|
|
Read registers 200-215 and decode uint16 pairs to ASCII.
|
|
|
|
```python
|
|
regs = client.read_holding_registers(200, 16, slave=13).registers
|
|
flag = ""
|
|
for val in regs:
|
|
if val == 0:
|
|
break
|
|
flag += chr((val >> 8) & 0xFF) + chr(val & 0xFF)
|
|
print(flag)
|
|
```
|
|
|
|
## Key Insights
|
|
|
|
- XOR key `0x0D13` is stored in two places (reg 19 and reg 105) as a breadcrumb
|
|
- The decoded values for states 0-4 match the current telemetry readings
|
|
- State 5 uses the special value `0x1337` (hacker reference)
|
|
- Wrong writes or timeouts reset the state machine to 0
|
|
|
|
## Flag
|
|
|
|
`ESPILON{m0dbu5_0p3r4t1ng_r00m}`
|
|
|
|
## Author
|
|
|
|
Eun0us
|