Description
You think you can reverse engineer? Let's test out your speed. Connect to the server - it sends you 20 binary files one at a time (1 second each) and you must extract the secret from each one.
Setup
Launch the challenge instance and connect via netcat.
The server sends a large hex-encoded binary, then prompts for the secret. You have 1 second per binary.
Solution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1
Understand the binary structureObservationI noticed the server sends a hex-encoded binary per round and prompts for a secret within 1 second, which suggested I needed to understand the binary's structure and find where the hardcoded secret is stored before writing any automated solver.Redirect the server output to a file. Convert the hex to binary. Disassemble it and look at main(). The binary actually prints the secret value to stdout before prompting (one verified approach observed this debug leak but used the disassembly route instead). The binary then prints "What's the secret?", reads your answer with scanf, and compares it against a hardcoded value. The secret is the immediate value stored via a 'mov' instruction referencing rbp.bashnc <HOST> <PORT_FROM_INSTANCE> | head -1 | xxd -r -p > /tmp/binarybashchmod +x /tmp/binarybashobjdump -d /tmp/binary | grep -A5 'main'bash# Look for: cmp DWORD PTR [rbp-0x8], 0x<HEXVALUE>What didn't work first
Tried: Run the binary directly and read what it prints to stdout, hoping the debug leak gives the secret
The binary does print the secret before prompting, but you only have 1 second per round and 20 rounds, so manual inspection is impossible at that speed. The automated solver must extract the value from the disassembly without executing the binary, because execution in a loop introduces timing overhead that causes timeouts.
Tried: Use 'strings /tmp/binary' to find the hardcoded secret value
The secret is stored as a 4-byte integer immediate in a 'mov' instruction, not as a printable ASCII string. 'strings' only surfaces null-terminated character sequences, so a numeric constant like 499839010 never appears in its output. Disassembly with objdump or capstone is needed to decode the raw instruction bytes into the immediate value.
Learn more
The binary's main function prints a prompt, then reads your answer with scanf, and compares it against a hardcoded value in the form
cmp [rbp-4], eaxorcmp [rbp-8], imm.The secret value appears as a hex immediate in the disassembly. Converting from hex to decimal gives the answer. For example,
0x1DD93C22= 499839010 decimal.Step 2
Write an automated solverObservationI noticed the 1-second-per-round and 20-round constraint made manual inspection impossible, which suggested writing an automated Python script using pwntools and capstone to disassemble in-process and extract the immediate value from the rbp-relative 'mov' instruction without touching disk.Write a Python script that connects, receives each hex-encoded binary string, disassembles the binary bytes using a library such as capstone, then applies a regex to the disassembly text to find the 'mov' instruction that stores an immediate into an rbp-relative slot (e.g. [rbp-0x8]). Send the decimal value of that immediate back. Repeat for all 20 rounds.bashpip install pwntoolspythonpython3 << 'EOF' from pwn import * import re, struct r = remote("<HOST>", <PORT_FROM_INSTANCE>) for _ in range(20): # Receive until "bytes" appears in the prompt r.recvuntil(b"bytes") hex_data = r.recvline().strip() binary = bytes.fromhex(hex_data.decode()) # Disassemble and search for the mov instruction storing the secret # One approach: disassemble with capstone, then regex for the immediate from capstone import Cs, CS_ARCH_X86, CS_MODE_64 md = Cs(CS_ARCH_X86, CS_MODE_64) secret = 0 for insn in md.disasm(binary, 0x0): # Look for mov targeting rbp-0x8 (or rbp-0x4) with an immediate value m = re.search(r'mov dword ptr \[rbp - (?:8|4|0x8|0x4)\], (0x[0-9a-f]+|\d+)', insn.mnemonic + ' ' + insn.op_str) if m: secret = int(m.group(1), 0) break r.recvuntil(b"secret") r.sendline(str(secret).encode()) print(r.recvall(timeout=5).decode()) EOFExpected output
picoCTF{4u7o_r3v_g0_brrr_...}What didn't work first
Tried: Use objdump inside the Python loop instead of capstone to extract the immediate
objdump requires writing the binary to disk with a subprocess call, then parsing its stdout. Under the 1-second-per-round constraint this adds two process-spawn overheads per iteration and reliably causes timeouts by round 5 or 6. Capstone operates purely in-process on the bytes already in memory, making it fast enough to finish extraction well within the time window.
Tried: Match only 'mov dword ptr [rbp - 4]' in the regex and miss rounds where the slot is [rbp - 8]
The server generates binaries where the compiler may place the secret in either [rbp-0x4] or [rbp-0x8] depending on the variant. A regex anchored to a single offset silently returns 0 for mismatched rounds, causing those answers to be wrong without any visible error. The correct pattern must use an alternation covering both offsets, e.g. '(rbp - (?:8|4|0x8|0x4))'.
Learn more
One approach: disassemble the binary bytes with a library like capstone, then apply a regex to the mnemonic output looking for a
mov dword ptr [rbp - 8](or[rbp - 4]) instruction that carries an immediate value. The opcode formov [rbp-8]isc7 45 f8, whilemov [rbp-4]isc7 45 fc. That approach describes the key instruction as moving a value into[rbp-8], so the correct opcode prefix may bec7 45 f8, notc7 45 fc.All 20 rounds share this structure with only the immediate (the secret) differing. Converting the extracted immediate to a decimal string and sending it back earns a point for that round.
Interactive tools
- Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.
- Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.
- File Magic IdentifierIdentify file types from magic numbers. Paste hex bytes or drop a file to detect PNG, JPEG, ZIP, PDF, ELF, PCAP, SQLite, and dozens of other formats.
Flag
Reveal flag
picoCTF{4u7o_r3v_g0_brrr_...}
The server sends 20 hex-encoded binaries. Each contains a hardcoded secret in a 'mov DWORD PTR [rbp-0x8], imm' (or similar rbp-relative) instruction. Disassemble with capstone, extract the immediate via regex, and send the decimal result back. Repeat 20 times to get the flag.