Platform: BYUCTF Challenge: Angr Management Category: Reversing Difficulty: Medium
Flag
byuctf{g3t_w1th_th3_c0ntr01_fl0w}
Target
<remote-host>:1368. Remote serves the real flag; local angr_management_test ships a dummy flag (byuctf{test_flag}) so you can develop offline.
- ELF 64-bit PIE, x86-64, not stripped,
pwn.red/jail(JAIL_TIME=900). - Prompt: "I built a maze using goto statements! Navigate through it successfully to get the flag." The name baits you into reaching for angr, which is the slow path here.
Recon
Functions: get_input, main (huge: 0x1317→0xee14, ~56 KB, the maze), flush_buf.
get_input (0x1270):
So each input is a number (0 to 9999). Running it:
main is 624 repeated "room" blocks. Per-room pattern:
It's a directed graph: each room prints its id, reads a number, and that number selects which room you jump to. Wrong number → exit. Tower of gotos = a maze with one or two exits per room.
Win room = room 0x270 (624) at 0xede5: no get_input, just puts(flag); return:
Why not angr
The challenge name screams symbolic execution, but driving angr through 72 chained fgets/strtol rounds (ASCII→int modeling, deep state) is slow and fiddly. The structure is a static, fully-recoverable CFG: every edge is a literal cmp $imm → jmp addr. So just parse the graph from the disassembly and BFS. Exact, instant, no SMT.
Solution
Dump disassembly, parse rooms + edges, BFS room 0 → room 624.
Verify locally then replay on the remote:
Path = 72 moves: 256 423 495 307 39 250 391 119 105 499 123 104 536 257 608 253 74 365 543 300 571 506 595 192 383 112 17 556 93 318 114 276 18 216 449 414 124 503 71 407 78 285 481 66 381 531 82 337 600 86 230 327 472 393 348 331 14 207 402 548 528 168 530 490 378 408 518 202 87 342 329 624
Local run ends Arrived at 624 / byuctf{test_flag}; remote gives the real flag.
Takeaways
- Read the structure before reaching for the heavy tool. A "maze of gotos" with literal
cmp imm→jmpedges is a static CFG. Recover it directly. angr is for when constraints aren't trivially readable; here every edge is a constant. - Anchor the parse on a stable signal (the
# f019"Arrived at %d" format ref) to reliably segment 624 near-identical blocks. - The flag is the lesson:
g3t_w1th_th3_c0ntr01_fl0w. Control-flow recovery beats brute symbolic execution. - Dev against the dummy-flag local binary; only the final
moves.txtreplay needs the network.
0xAdham
