CMU Bomb Lab with Radare2 — Phase 1
Hello world. In the interests of putting more Radare2 content out there, here’s a noob friendly intro to r2 for those who already have a basic grasp of asm, C, and reversing in x86–64.
This series will focus on CMU’s Binary Bomb challenge. There are various versions of this challenge scattered across the web, but I went with this one: https://github.com/ctfhacker/angr-examples/raw/master/cmu-binary-bomb/bomb
There are plenty of fantastic guides for this challenge, so the goal here is to cheat our way to victory. I’ll leave some links to actual guides and important articles at the end.
It should go without saying that you should do all your reversing in a virtual machine or container which you’ll discard afterwards. I’ve spent hours on this specific binary and haven’t seen anything fishy, but it’s never worth the risk. Better safe than sorry.
With that said, run the binary. You’ll see it reads from stdin, then blows up if you get the answer wrong.
Initial Setup
You can find radare2 in almost every package manager and can install it from there. That said, r2 is under constant development and worth keeping up to date by building it from source. The process is extremely simple, and is outlined on r2’s Github page
Assuming you have your distro’s build-essentials
installed, you can install/update r2 with:
$ git clone https://github.com/radare/radare2.git
$ cd radare2
$ ./sys/install.sh
Getting Started
We’ll start by opening the binary and printing the disassembled main function.
Run the binary with r2:
$ r2 ./bomb
To save time, r2 doesn’t run analysis automatically. To analyze a binary and get function names, type aaa
into r2’s command prompt:
[0x00400c90]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Use -AA or aaaa to perform additional experimental analysis.
aaa
refers to (A)nalyze (A)ll (A)uto Name Functions
If you’re mystified by r2’s syntax — relax — it’s not so bad. It’s beeter than GDB’s at least. The first letter of all commands refers to the ‘action’, and characters are appended to further specify it. Need to print something? The prefix is p
. Want to print hex? It’s p8
. Why? Because the hex pairs are 8bit.
You’re either nodding along or completely lost. Both are fine. Append ?
to any r2 command to see the help information. You’ll see usage, arguments and a description. Starting out, it’s good to have some sort of reference card, like this: https://radare.gitbooks.io/radare2book/refcard/intro.html
[0x00400c90]> aa?
Usage: aa[0*?] # see also ‘af’ and ‘afna’
| aa alias for ‘af@@ sym.*;af@entry0;afva’
| aaa[?] autoname functions after aa (see afna)
| aab abb across bin.sections.rx
| aac [len] analyze function calls (af @@ `pi len~call[1]`)
| aac* [len] flag function calls without performing a complete analysis
| aad [len] analyze data references to code
| aae [len] ([addr]) analyze references with ESIL (optionally to address)
| aaf[e|t] analyze all functions (e anal.hasnext=1;afr @@c:isq) (aafe=aef@@f)
To get a list of functions (after running aaa
), use afl
— (A)nalyze (F)unctions (L)ist Functions. This lists a number of functions, so we’ll grep the output for main
. Append a ~
(tilde) to a command, you can grep the result:
[0x00400c90]> afl~main
0x00400b70 1 6 sym.imp.__libc_start_main
Use pdf
— (P)rint (D)isassemble (F)unction — to print a function. The syntax is:
pdf @ [function_name]
By using the @
symbol, you tell r2 to temporarily seek to the function before printing. If you do not specify a function to print, pdf
will print from the current address.
Commands used:
aaa # Analyze — All — Auto Name Functions
afl # Analyze — Functions — List Functions
pdf @ [func_name] # Print — Disassemble — Functions
[command]~secret # Grep output
Phase 1
First let’s disassemble sym.phase_1
:
[0x00400c90]> pdf @ sym.phase_1
/ (fcn) sym.phase_1 28
| sym.phase_1 ();
| ; CALL XREF from main (0x400e3a)
| 0x00400ee0 4883ec08 sub rsp, 8
| 0x00400ee4 be00244000 mov esi, str.Border_relations_with_Canada_have_never_been_better. ;
| 0x00400ee9 e84a040000 call sym.strings_not_equal
| 0x00400eee 85c0 test eax, eax
| ,=< 0x00400ef0 7405 je 0x400ef7
| | 0x00400ef2 e843050000 call sym.explode_bomb
| `-> 0x00400ef7 4883c408 add rsp, 8
\ 0x00400efb c3 ret
[0x00400c90]>
Looks like some string comparison. You can print null terminated strings with ps @ [stringname]
. Try for yourself, and note that tab completion works:
ps @ str.Border…..
To view all strings in the data section, use iz
, or izz
for all strings in the binary.
We could get the string to pass the level, but we’re here to cheat. See the conditional jmp
at 0x00400ef0
? Let’s make that always jump, even with a wrong answer. r2 has a command for that: wao
— (W)rite (A)ssembly (O)peration.
Check the help for more information:
[0x00400c90]> wao?
| wao [op] performs a modification on current opcode
| wao nop nop current opcode
| wao jinf assemble an infinite loop
| wao jz make current opcode conditional (zero)
| wao jnz make current opcode conditional (not zero)
| wao ret1 make the current opcode return 1
| wao ret0 make the current opcode return 0
| wao retn make the current opcode return -1
| wao nocj remove conditional operation from branch (make it unconditional)
| wao trap make the current opcode a trap
| wao recj reverse (swap) conditional branch instruction
| WIP: not all archs are supported and not all commands work on all archs
We want wao nocj
, which will remove the condition for the jmp. To make this modification, we’ll need to reopen the file in Write mode. r2 can be run in Write mode by running it with the -w
flag, but you can also reload from within r2 with oo+
Open — Reopen Current File — Read/Write mode
oo+
Next, move the cursor to the jmp
instruction. Use the seek action, followed by the address to move the cursor:
s 0x00400ef0
Now patch it with wao
:
wao nocj
To avoid unwanted changes, run oo
from the r2 prompt to reopen the file in read only mode, then admire your handywork by running pdf
to disassemble sym.phase_1
:
[0x00400c90]> oo+
[0x00400c90]> s 0x00400ef0
[0x00400ef0]> wao nocj
[0x00400ef0]> oo
[0x00400ef0]> pdf @ sym.phase_1
/ (fcn) sym.phase_1 28
| 0x00400ee0 4883ec08 sub rsp, 8
| 0x00400ee4 be00244000 mov esi, str.Border_relations_with_Canada_have_never_been_better.
| 0x00400ee9 e84a040000 call sym.strings_not_equal
| 0x00400eee 85c0 test eax, eax
| ,=< 0x00400ef0 eb05 jmp 0x400ef7
| | 0x00400ef2 e843050000 call sym.explode_bomb
| `-> 0x00400ef7 4883c408 add rsp, 8
\ 0x00400efb c3 ret
Let’s see if it worked. Enter anything you want for the first level:
Great! Let’s start our answers text file so we don’t have to type things in.
$ cat answers.txtTaylor Swift is pretty fly
New commands used:
ps @ str.stringname # Print null terminated string
s function_name # Seek to address or function
wao # Write Assembly Opcode
oo+ # Reopean the file in write mode
oo # Reopen the file in read only mode
We’ve touched on some things you’ve seen in other debuggers — seeking, printing, writing, and a little bit of command syntax. Your main takeaway should be that every command has a ton of documentation, and that there’s a lot of thought behind the naming scheme, even if it seems cryptic.
Check out the help for every command run in this post. You’ll see a ton of useful functionality and start to appreciate the versatility of r2.
Next: CMU Bomb Lab with Radare2 — Phase 2
Links
Radare2 Book
Binary Bomb walkthrough from Zach Alexander
Archived Links of UNLogic’s fantastic r2 BB guide