Description
A robot communicates with its controller over an authenticated radio protocol using Diffie-Hellman key exchange and HMAC-signed commands. Three weaknesses - address spoofing, nonce reuse across sessions, and a cost-free sync packet - let you replay captured commands and take control of the robot.
Setup
Launch the instance and grab the provided source (server.py / client.py) from the challenge page so you can read the protocol exactly instead of guessing from packet dumps.
Skim the source for three things: where addresses are checked (or not) in incoming packets, how the per-session nonce is initialized, and which packet types increment the move counter.
Connect to the service and pick the debug option from the menu so you can watch packets stream by while you experiment.
nc <INSTANCE_HOST> <PORT_FROM_INSTANCE># In another terminal, keep python3 running so you can paste signed packets backpython3Solution
Walk me through it- Step 1Understand the three vulnerabilitiesThree independent bugs chain into a full exploit. The controller-address field sits outside the HMAC, so you can claim to be the controller. Nonces reset to 0 at session start, so a packet signed at nonce 5 in session 1 verifies at nonce 5 in session 2. And
secure_data_requestadvances the nonce without incrementing the move-limit counter.Learn more
Diffie-Hellman (DH) key exchange lets two parties derive a shared secret over a public channel without ever transmitting the secret itself. The protocol is sound in isolation, but it only establishes a shared key, it does not authenticate who you exchanged keys with. Without certificate-based authentication, a man-in-the-middle can establish a DH session with each party separately, forwarding messages between them while reading all traffic. The challenge's DH implementation is cryptographically correct but lacks this identity verification layer.
A nonce (number used once) prevents replay attacks in authenticated protocols: each message includes a unique, incrementing counter value covered by the HMAC. If the receiver has already seen nonce N, it rejects any future message with the same nonce. The reason nonces reset here is that each new session does a fresh DH handshake and starts the counter back at 0; from the controller's perspective, nonce 5 of session 2 is cryptographically identical to nonce 5 of session 1 because both are signed under a key derived from a fresh handshake but with the same nonce input. The replay protection is only within a session, not across sessions.
The move-limit bypass is a logic vulnerability: the protocol specification counts movement commands toward the limit but omits sync/data-request commands from the counter. By sending
secure_data_requestpackets to advance the nonce counter to the right position, you can replay any captured movement command without spending the limited move budget. - Step 2Collect HMAC-signed packets from debug messagesThe debug menu prints every packet exchanged between robot and controller along with its HMAC tag. Open one session, drive the robot through every direction you might need (up/down/left/right), and copy each signed movement packet into a notebook keyed by direction so you can replay it in the next session.bash
# Inside the netcat session, pick option 4 (or whatever the menu calls 'debug messages') # For each direction, send 'move <dir>' once and copy the printed packet bytes + HMAC tag # Packet wire format: <nonce:4 bytes><move:1 byte><HMAC:32 bytes> # Hex example: 00000005 | 01 | 9f3a...c0d2 (37 bytes total, no delimiters on the wire) # Save them as a Python dict keyed by direction: # captured = {b'up': bytes.fromhex('000000050' + '1' + '9f3a...c0d2'), b'down': ..., ...}Learn more
HMAC (Hash-based Message Authentication Code) provides integrity and authenticity: only someone who knows the secret key can produce a valid tag for a given message. The security guarantee holds only if the key is secret and each message is unique (enforced by the nonce). When the nonce resets across sessions, an HMAC computed over
message + nonce_5in session 1 is identical to the HMAC the receiver expects for session 2's message at the same nonce position - the HMAC cannot distinguish between the two sessions.Debug endpoints in production protocols are a common real-world vulnerability. If a debug mode emits plaintext or authenticated traffic that an attacker can observe, it turns an active attack into a passive one: collect valid signed messages first, then replay them later. Systems that expose debug ports in production (telnet, JTAG, serial consoles) have been the entry point for several high-profile IoT and industrial control system compromises.
- Step 3Replay packets to control the robotOpen a fresh session, claim the controller address in your handshake, then for every move you want to send: fire
secure_data_requestuntil the session nonce matches the captured packet's nonce, paste the captured bytes, and the robot accepts the move without decrementing the budget. Walk it to the flag tile and read the state.python# Pseudo-code for the replay loop using pwntools (see linked guide) from pwn import remote io = remote('<INSTANCE_HOST>', <PORT_FROM_INSTANCE>) # 1. Do DH handshake claiming controller address # 2. For each move in path: # while session_nonce < captured_nonce: io.sendline(b'secure_data_request') # io.send(captured[direction]) # 3. io.sendline(b'get_state') and parse the response for picoCTF{...}Learn more
The exploit chain combines all three bugs: address spoofing positions you as the controller, nonce reuse makes your captured packets valid in the new session, and the cost-free sync lets you reach the exact nonce value needed without burning moves. Any one bug alone would be insufficient; all three together give full robot control within the move budget. pwntools and netcat are both useful here; netcat for hand-driving the menu, pwntools when you script the replay loop.
This mirrors real-world attacks on industrial control systems (ICS) and IoT device protocols. Many legacy protocols (Modbus, DNP3, older Zigbee implementations) lack message authentication entirely and rely on network isolation for security. When these systems are exposed to the internet or when an attacker gains network access, replay attacks are trivial. Adding HMAC authentication is necessary but insufficient: nonce management, session isolation, and address authentication must all be implemented correctly for the protection to hold.
Flag
picoCTF{r1c0ch3t_...}
The full flag string is held back here; the rest of this writeup walks through the exploit so you can recover it on your live instance. Approach: spoof controller address, collect HMAC-signed packets from debug output, advance the nonce with cost-free sync packets, then replay captured movement commands.