diff --git a/OT/Operating_Room/README.md b/OT/Operating_Room/README.md index 9842761..0669bd5 100644 --- a/OT/Operating_Room/README.md +++ b/OT/Operating_Room/README.md @@ -1,62 +1,133 @@ -# Operating Room -- Solution +# Operating Room -## 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. +| Field | Value | +|-------|-------| +| Category | OT | +| Difficulty | Medium-Hard | +| Points | 500 | +| Author | Eun0us | +| CTF | Espilon 2026 | -## 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. +## Description + +The operating room at Clinique Sainte-Mika runs on an industrial Modbus-based control system +managing HVAC, pressure, O2, ventilation, and lighting. + +A maintenance backdoor was left in the system by the contractor. Find it. Unlock it. + +You will need standard Modbus tools to interact with this challenge. + +- Modbus TCP: `tcp/:502` + +Format: **ESPILON{flag}** + +--- + +## TL;DR + +Scan Modbus unit IDs to find unit 13. Map all holding registers: spot XOR key `0x0D13` at +registers 19 and 105, and the state machine at registers 100-110. Decode each hint, write the +correct value to register 110 before the timer expires. Execute all 6 state transitions +(state 5 requires special value `0x1337`). Read the flag from registers 200-215. + +--- + +## Tools + +| Tool | Purpose | +|------|---------| +| Python 3 + `pymodbus` | Modbus TCP client | +| XOR arithmetic | Decode hints from register 101 | + +--- + +## Solution + +### Step 1 — Discover the correct unit ID + +Default unit ID 1 returns Modbus exceptions. Scan IDs 1-20: ```python from pymodbus.client import ModbusTcpClient -client = ModbusTcpClient("HOST") + +client = ModbusTcpClient("") 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!") + break ``` -### 2. Map Registers +**Unit ID 13 responds.** (Reference to Room 013.) -Read holding registers 0-255 on unit 13: +### Step 2 — Map registers 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) +```python +regs = client.read_holding_registers(0, 256, slave=13).registers +``` -### 3. Understand the State Machine +Key registers: -- 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 +| Register | Value | Meaning | +|----------|-------|---------| +| 0-19 | Telemetry | HVAC, humidity, pressure, O2, fan RPM, lux... | +| 13 | `0x4C4E` | "LN" — Lain easter egg | +| 19 | `0x0D13` | XOR key | +| 100 | current state | State machine (starts at 0) | +| 101 | encoded hint | `expected_value = reg_101 XOR reg_105` | +| 102 | countdown timer | Must write before timer = 0 | +| 105 | `0x0D13` | XOR key copy (breadcrumb) | +| 110 | write target | Trigger register | +| 200-215 | zeros → flag | Populated after completion | -### 4. Execute Transitions +> 📸 `[screenshot: register dump highlighting XOR key at reg 19 and 105, and state machine at 100-105]` -| 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 | +### Step 3 — Decode the state machine logic -### 5. Read Flag +- Register 100 = current state (0–6) +- Register 101 = XOR-encoded expected value +- Register 105 = XOR key = `0x0D13` +- Decode: `expected = reg[101] XOR 0x0D13` +- Write `expected` to register 110 to advance the state +- Each transition must complete before register 102 (timer) reaches 0 +- Wrong values or timeouts reset the state machine to 0 -After all 6 transitions, register 100 = 7 (complete). -Read registers 200-215 and decode uint16 pairs to ASCII. +### Step 4 — Execute all 6 transitions + +| State | Subsystem | Decoded Value | Formula | +|-------|-----------|--------------|---------| +| 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` (hardcoded hacker constant) | + +```python +import time + +for expected_state in range(6): + regs = client.read_holding_registers(100, 6, slave=13).registers + state = regs[0] + hint_enc = regs[1] + timer = regs[2] + xor_key = regs[5] + + expected = hint_enc ^ xor_key + print(f"State {state}: write {expected} (timer={timer})") + + client.write_register(110, expected, slave=13) + time.sleep(0.3) +``` + +> 📸 `[screenshot: script executing each transition and state advancing from 0 to 6]` + +### Step 5 — Read the flag + +After state 6 (complete), read registers 200-215: ```python regs = client.read_holding_registers(200, 16, slave=13).registers @@ -68,17 +139,10 @@ for val in regs: print(flag) ``` -## Key Insights +> 📸 `[screenshot: flag registers decoded to ASCII]` -- 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