CMU Bomb Lab with Radare2 — Phase 6

Mark Higgins
7 min readJul 15, 2019

--

This level gets a little rough. We’ve decided to use r2 for good, so patching is not an option.

Previous posts:

Load the binary, perform analysis, seek to Phase 6, and have a look at your task.

Pull up the function in Graph mode with VV, press p to cycle between views, and select the minigraph. Now you can see there are a few loops.

Let’s start with when it calls sym.read_six_numbers

0x00401100      4989e5         mov r13, rsp
0x00401103 4889e6 mov rsi, rsp
0x00401106 e851030000 call sym.read_six_numbers
0x0040110b 4989e6 mov r14, rsp
0x0040110e 41bc00000000 mov r12d, 0

rsp is copied into both r13 and rsi. rsi doesn’t get used again until later, but r13 is used for comparison in the loop.

0x00401106      e851030000     call sym.read_six_numbers
0x0040110b 4989e6 mov r14, rsp
0x0040110e 41bc00000000 mov r12d, 0

The result of sym.read_six_numbers goes into rsp, which is then copied into r14. rsp, then r12d is set to zero. Since this happens just before a loop, this means r12d is might a loop counter.

Skip to the end of the loop, and notice that it adds 4 to r13 every time the loop runs. This suggests r13 is being used to cycle through the numbers.

Knowing this, let’s start the loop itself

0x00401114      4c89ed         mov rbp, r13 
0x00401117 418b4500 mov eax, dword [r13]
0x0040111b 83e801 sub eax, 1
0x0040111e 83f805 cmp eax, 5
0x00401121 7605 jbe 0x401128
0x00401123 e812030000 call sym.explode_bomb:

As the loop increments, each number is compared to 6 (well, technically 5 since 1 is first subtracted from 6), and if that number is greater than 6, boom.

0x00401128      4183c401       add r12d, 1
0x0040112c 4183fc06 cmp r12d, 6
0x00401130 7421 je 0x401153

Next we check if we’ve read the last digit. If not, continue the loop.

0x00401132      4489e3         mov ebx, r12d  
0x00401135 4863c3 movsxd rax, ebx
0x00401138 8b0484 mov eax, dword [rsp + rax*4]
0x0040113b 394500 cmp dword [rbp], eax
0x0040113e 7505 jne 0x401145
0x00401140 e8f5020000 call sym.explode_bomb;[2]
0x00401145 83c301 add ebx, 1
0x00401148 83fb05 cmp ebx, 5
0x0040114b 7ee8 jle 0x401135

Following the flow, the next section compare each digit from our input against the next. If any number appears more than once, boom.

As pseudocode, it looks something like this:

input = stdin
for digit in input:
for i in range(0, 6):
if (input[i] < 1) || (input[i] > 6):
fail()
for j in range(i , 6):
if (input[i] == input[j]:
fail()

First loop done. Onto the next.

    0x00401153      488d742418     lea rsi, [var_18h]  
0x00401158 4c89f0 mov rax, r14
0x0040115b b907000000 mov ecx, 7
.-> 0x00401160 89ca mov edx, ecx
: 0x00401162 2b10 sub edx, dword [rax]
: 0x00401164 8910 mov dword [rax], edx
: 0x00401166 4883c004 add rax, 4
: 0x0040116a 4839f0 cmp rax, rsi
`=< 0x0040116d 75f1 jne 0x401160

rax is set to the first digit from our input, and as the loop progresses, each digit is subtracted from 7, transforming the input. Something like this:

input = stdin
for i in range(0,6):
input[i] = 7 - input[i]
Loops like this area easy to spot in Graph mode

Try using the cursor to follow conditional statements to help wrap your head around code. In Visual mode move the cursor to a conditional statement, press ENTER and it will always jump.

You can also add comments in Visual/Graph mode with the semi colon ; command.

Linked Lists

As you’ve probably noticed, there’s a obj.node in the disasssembly. Whenever I see something interesting, I try printing the hexdump. Type px @ obj.node, then TAB completion to reveal node1–6.

You can also use pxa — (P)rint he(X) (D)ump (A)nnotated — to label the information.

At each offset, you can see the numbers 1–6 at at +0x4. At +0x8 you can see another address, which is a pointer to the offset of the next item in the list. This is a classic linked list, and in C looks something like:

struct node {
int value;
int index;
struct node *next
};

We will use r2’s pf — (P)rint (F)ormatted data — to define and print these structures. The syntax is:

pf.[name of new object] [assign data types] [assign data names]

To get a list of all data types for pf, check the help by running pf??.We will use i for ints, and *? for the pointer to the next item.

pf.node ii*? value index (node)next

Shoutout to Unlogic for his awesome writeup that covered this.

Now print the node with:

pf.node @ obj.node1

r2 will not only print out the node, it will get the address from next, then print that node too, until it finally reaches a null.

Neat! Now what?

Let’s add 1 2 3 4 5 6 to our answers.txt, load r2 in Debug mode, seek to sym.phase_6 and set a breakpoint after the second loop which transformed our input. Place another breakpoint after mov qword [rdx + 8], 0 , which is terminates the linked list by placing a NULL in the next address offset.

Continue to the first breakpoint. Verify the transform has been applied by printing the stack with px 32 @ rsp. You can also see this information in Visual mode’s debug view

As long as you’ve been paying attention, the final loop is a piece of cake.

     0x004011da      bd05000000     mov ebp, 5
.-> 0x004011df 488b4308 mov rax, qword [rbx + 8]
: 0x004011e3 8b00 mov eax, dword [rax]
: 0x004011e5 3903 cmp dword [rbx], eax
,==< 0x004011e7 7d05 jge 0x4011ee
|: 0x004011e9 e84c020000 call sym.explode_bomb
`--> 0x004011ee 488b5b08 mov rbx, qword [rbx + 8]
: 0x004011f2 83ed01 sub ebp, 1
`=< 0x004011f5 75e8 jne 0x4011df

The loop counter is ebp, counting down from 5 and ending the loop when it hits 0. rbx is a pointer to the current node.

.->  0x004011df      488b4308       mov rax, qword [rbx + 8]
: 0x004011e3 8b00 mov eax, dword [rax]

By setting rax to be the pointer to rbx+8, we’re creating pointer to the next item in the linked list. This allows us to compare each item to the next.

 :   0x004011e5      3903           cmp dword [rbx], eax
,==< 0x004011e7 7d05 jge 0x4011ee
|: 0x004011e9 e84c020000 call sym.explode_bomb
`--> 0x004011ee 488b5b08 mov rbx, qword [rbx + 8]
: 0x004011f2 83ed01 sub ebp, 1
`=< 0x004011f5 75e8 jne 0x4011df

This checks to make sure the current node’s value is greater than the next one’s. If not, boom. Since we control the order of the indexes with our stdin, we need to put the nodes in descending order to beat this level, based on their value, not index.

Once again, here is our linked list.

:> pf.node @ obj.node6
value : 0x00603320 = 443
index : 0x00603324 = 6
next : (*0x603310)
struct<node>
value : 0x00603310 = 477
index : 0x00603314 = 5
next : (*0x603300)
struct<node>
value : 0x00603300 = 691
index : 0x00603304 = 4
next : (*0x6032f0)
struct<node>
value : 0x006032f0 = 924
index : 0x006032f4 = 3
next : (*0x6032e0)
struct<node>
value : 0x006032e0 = 168
index : 0x006032e4 = 2
next : (*0x6032d0)
struct<node>
value : 0x006032d0 = 332
index : 0x006032d4 = 1
next : (*0x0) NULL

If we want these in descending order based on value, we need:

3 4 5 6 1 2

Fail. Did you forget about the transform? You need to take each number and subtract it from 7. I wrote it out in pseudo code earlier, but I would hope you can do this in your head. I believe in you!

New commands:

pxa                          # Hex dump with annotations
pf.[struct name] # Define a format
pf?? # View format options
pf @ [struct.name] # Use a format template to print

Well that was a bit more intense. I guess despite our best efforts, we’ve gotten sucked into a few of these challenges.

One more to go, and it’s a doozy.

Next: CMU Binary Bomb with Radare2 — Secret Phase

Links

Linked Lists — Computerphile

--

--

Mark Higgins
Mark Higgins

Written by Mark Higgins

OSCE | OSCP | I like computers

No responses yet