CMU Bomb Lab with Radare2 — Phase 6
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]
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