📜 ⬆️ ⬇️

Disable the bomb with Radare2


Good day,% username%! Today we will go to study the countless possibilities of the framework for the reverse - radare2. In the form of the test subject, I took the first bomb that fell from the site of Carnegie Mellon University.

Hashes
Since Binary can change leave just in case hashsum:
md5sum: 1f38d04188a1d08f95d8c302f5361e9f
sha1sum: 31022a4baa524f6275209f7424c616226bc9814b
sha256sum: 8849e033691d51426c0c91a76eeb0c346eddd37e8fdf21cd93acd16669f1b461

What is it all about?


Radare2 (aka r2) is an open source cross-platform framework for exploring binary files (initially, by the way, a hex editor). The main competitor of this is the well-known IDA Ilfak, but, alas, for the student it is expensive, and the free version with x86-64 is not friendly. And the radar seems to be cooler .
A binary bomb or just a bomb is an executable file for training that receives a certain number of lines, and, in case all lines are tested, congratulates the young analyst. These are the so-called levels or phases, we have 6 of them.

A little more about the framework


Radare2, as already mentioned, is a framework, not just a disassembler. It includes a bunch of different tools like debugger, hex editor, compiler, search for ROP gadgets and much more. For non console lovers, it also has two raw frontends, the WebUI ( $ r2 -c "=H" file ) and Bokken .
Manual as usual is in man, as well as for each command by adding after it "?". For example, “pd?” Will give descriptions of commands starting with pd.

Some links on the topic:

Here we go!


In addition to the executable file itself, we were kindly provided with a file with source codes. However, all that is there - initialization of input, call phases, and funny comments. The remaining functions are drawn from the corresponding headers, which we do not have.
The same file and it is big enough
 /*************************************************************************** * Dr. Evil's Insidious Bomb, Version 1.1 * Copyright 2011, Dr. Evil Incorporated. All rights reserved. * * LICENSE: * * Dr. Evil Incorporated (the PERPETRATOR) hereby grants you (the * VICTIM) explicit permission to use this bomb (the BOMB). This is a * time limited license, which expires on the death of the VICTIM. * The PERPETRATOR takes no responsibility for damage, frustration, * insanity, bug-eyes, carpal-tunnel syndrome, loss of sleep, or other * harm to the VICTIM. Unless the PERPETRATOR wants to take credit, * that is. The VICTIM may not distribute this bomb source code to * any enemies of the PERPETRATOR. No VICTIM may debug, * reverse-engineer, run "strings" on, decompile, decrypt, or use any * other technique to gain knowledge of and defuse the BOMB. BOMB * proof clothing may not be worn when handling this program. The * PERPETRATOR will not apologize for the PERPETRATOR's poor sense of * humor. This license is null and void where the BOMB is prohibited * by law. ***************************************************************************/ #include <stdio.h> #include <stdlib.h> #include "support.h" #include "phases.h" /* * Note to self: Remember to erase this file so my victims will have no * idea what is going on, and so they will all blow up in a * spectaculary fiendish explosion. -- Dr. Evil */ FILE *infile; int main(int argc, char *argv[]) { char *input; /* Note to self: remember to port this bomb to Windows and put a * fantastic GUI on it. */ /* When run with no arguments, the bomb reads its input lines * from standard input. */ if (argc == 1) { infile = stdin; } /* When run with one argument <file>, the bomb reads from <file> * until EOF, and then switches to standard input. Thus, as you * defuse each phase, you can add its defusing string to <file> and * avoid having to retype it. */ else if (argc == 2) { if (!(infile = fopen(argv[1], "r"))) { printf("%s: Error: Couldn't open %s\n", argv[0], argv[1]); exit(8); } } /* You can't call the bomb with more than 1 command line argument. */ else { printf("Usage: %s [<input_file>]\n", argv[0]); exit(8); } /* Do all sorts of secret stuff that makes the bomb harder to defuse. */ initialize_bomb(); printf("Welcome to my fiendish little bomb. You have 6 phases with\n"); printf("which to blow yourself up. Have a nice day!\n"); /* Hmm... Six phases must be more secure than one phase! */ input = read_line(); /* Get input */ phase_1(input); /* Run the phase */ phase_defused(); /* Drat! They figured it out! * Let me know how they did it. */ printf("Phase 1 defused. How about the next one?\n"); /* The second phase is harder. No one will ever figure out * how to defuse this... */ input = read_line(); phase_2(input); phase_defused(); printf("That's number 2. Keep going!\n"); /* I guess this is too easy so far. Some more complex code will * confuse people. */ input = read_line(); phase_3(input); phase_defused(); printf("Halfway there!\n"); /* Oh yeah? Well, how good is your math? Try on this saucy problem! */ input = read_line(); phase_4(input); phase_defused(); printf("So you got that one. Try this one.\n"); /* Round and 'round in memory we go, where we stop, the bomb blows! */ input = read_line(); phase_5(input); phase_defused(); printf("Good work! On to the next...\n"); /* This phase will never be used, since no one will get past the * earlier ones. But just in case, make this one extra hard. */ input = read_line(); phase_6(input); phase_defused(); /* Wow, they got it! But isn't something... missing? Perhaps * something they overlooked? Mua ha ha ha ha! */ return 0; } 


After starting r2, he meets us with a random phrase. Then he sets the current entry-point pointer and waits for the command. The -A flag when opening a file immediately analyzes it.
The same can be done with a block of commands starting with a ), for example afl - gets a list of functions from a binary. ~ - analogue grep-a (filter). We will look for our functions from him.
 $ r2 -A bomb -- In soviet Afghanistan, you debug radare2! [0x00400c90]> afl~phase 0x00400ee0 28 3 sym.phase_1 0x004015c4 149 8 sym.phase_defused 0x00400efc 71 8 sym.phase_2 0x00400f43 139 8 sym.phase_3 0x0040100c 86 7 sym.phase_4 0x00401062 146 9 sym.phase_5 0x004010f4 272 26 sym.phase_6 0x00401242 81 5 sym.secret_phase 

Level 1


Miraculously, all the functions from the source in place, but at the same time also found the secret phase. Let's finally see the contents of the first level. You can do this by moving the pointer to a specific address of the function, and then output the required number of opcodes for disassembling.
 [0x00400c90]> s 0x00400ee0 #    's' -   [0x00400ee0]> pd 8 #  8     

Since The output of r2 from the box is just perfect, for clarity, I will post assembler listings in the form of pictures.

Please note that the radar provided us with XREFs (that’s where the control can be transferred from) with jump mnemonics. In addition, I substituted a line at the specified address and, most importantly, in the form of ascii arrows, showed transitions in the block.
For comparison, the output from objdump
 0000000000400ee0 <phase_1>: 400ee0: 48 83 ec 08 sub rsp,0x8 400ee4: be 00 24 40 00 mov esi,0x402400 400ee9: e8 4a 04 00 00 call 401338 <strings_not_equal> 400eee: 85 c0 test eax,eax 400ef0: 74 05 je 400ef7 <phase_1+0x17> 400ef2: e8 43 05 00 00 call 40143a <explode_bomb> 400ef7: 48 83 c4 08 add rsp,0x8 400efb: c3 ret retq 


Even without delving into it, it becomes clear that the first line we need is “Border relations with Canada”. And by itself, when fed to her bomb, she lets us into the second phase.
 $ ./bomb Welcome to my fiendish little bomb. You have 6 phases with which to blow yourself up. Have a nice day! Border relations with Canada have never been better. Phase 1 defused. How about the next one? 

Level 2


The second variant of the output of the disassembled function is using absolute addressing / labels. For example, the second phase - it will look like this:
 [0x00400ee0]> pdf @ 0x00400efc 

or if the address is not known:
 [0x00400ee0]> pdf @ sym.phase_2 


Our ultimate goal is not to detonate a bomb, i.e. Do not make calls to call sym.explode_bomb , so we will make a start from this. Therefore, both je jumps should always work with us.
The first thing that stands in our way is a call to sym.read_six_numbers . Accordingly, after this call at the top of the stack should be one. Let's see what happens in this function.

Previously, r2 parsed files for functions based on the ret opcode, which often led to the output of several functions (for example, if there are exit () system calls). In such cases, when the radar incorrectly determines the function, you can do it manually.
 [0x00400ee0]> s 0x0040149e #       [0x0040149e]> af+ sym.read_six_numbers `?vi $$-sym.read_six_numbers` rsn #   rsn,    sym.read_six_numbers   . 

By the way, in this example, another command was used as the argument of the first one using paired brackets `` .
Nothing interesting happens in the function itself; it takes 6 numbers from the read string and writes them in order to the passed pointer. Then makes sure that the numbers are greater than 5.
In C, it would look something like this:
 void read_six_numbers(char *str, long long *p) { if (sscanf(str, "%d %d %d %d %d %d", p, p+1, p+2, p+3, p+4, p+5) <= 5) explode_bomb(); } 

')
Let's go back to our phase_2 function. A pointer to an array is passed a pointer to the top of the stack ( mov rsi, rsp ). Therefore, the first number in the line should be - 1.
As you can see there are a lot of transitions. The IDA user would most likely press the space bar and look at the transition graph. You will not believe, but here they are too. Like vim, there is visual-mode (command V ) and in it there is the same transition graph, also on command V (or VV at once). Exit from each mod - q
The output will be such a cute ASCII graph


It is displayed perfectly, with a minimum of intersections (compared to previous versions). You can move this miracle with arrows, or vim-like 'hjkl' . If you do not like the location of the blocks, you can also move the hotkeys Shift + 'hjkl' . This moves the selected block (blue), you can select it Tab / Shift-Tab .
In the first block, a pointer to the second number is written in rbx , and in rbp - at the end of the array of numbers. And control is thrown on a cycle in which neighboring numbers are compared in pairs.
 mov eax, dword [rbx - 4] ;     eax add eax, eax ;   cmp dword [rbx], eax ;    je 0x400f25 ;  ,   call sym.explode_bomb ;  ,    add rbx, 4 ;      cmp rbx, rbp ; ,      jne 0x400f17 ;  ,     jmp 0x400f3c ;   

It turns out that we need to enter a sequence of powers of two from 1 to 32. Excellent, but still check that this is what you need.
 $ ./bomb ... Phase 1 defused. How about the next one? 1 2 4 8 16 32 That's number 2. Keep going! 

Level 3



Probably now a person far from an assembler, in epileptic seizures, is trying to get a cross on the edge of the window. In fact, there is nothing to be afraid of, just like this is the most common switch-case block.
Numbers are read in the same way as in the previous case, only without calling a function and only two. They are stored in [rsp + 8] and [rsp + 0xc], respectively.
Then there are checks that both numbers are read successfully, as well as the first argument is not more than 7. After that, the switch goes to the address 0x402470 with an offset (the number entered) * 8.
It is not difficult to guess that the addresses of the case tags are there. In order not to be unfounded, let's see what really lies there. This can be done using the px command groups. In this case, we are interested in 8-byte words ( Q uad-word).
 [0x0040149e]> pxQ 72 @ 0x402470 0x00402470 0x0000000000400f7c sym.phase_3+57 0x00402478 0x0000000000400fb9 sym.phase_3+118 0x00402480 0x0000000000400f83 sym.phase_3+64 0x00402488 0x0000000000400f8a sym.phase_3+71 0x00402490 0x0000000000400f91 sym.phase_3+78 0x00402498 0x0000000000400f98 sym.phase_3+85 0x004024a0 0x0000000000400f9f sym.phase_3+92 0x004024a8 0x0000000000400fa6 sym.phase_3+99 

Actually, as expected, although not quite consistently. Next comes the verification of our second number with the magic one that was written in eax with the switch. Since the input in decimal basis will have to be converted from hex. You can calculate it using rax2 (analog calculator) through a shell call, as well as directly, through the built-in calculator (hotkey - ? ), Without creating new programs. And, in order not to constantly press enter, you can group commands just like in a bash.
 [0x0040149e]> !rax2 0xcf 207 [0x0040149e]> ?vi 0x2c3; ?vi 0x100; ?vi 0x185; ?vi 0xce; ?vi 0x2aa; ?vi 0x147; ?vi 0x137 707 256 389 206 682 327 311 

Total possible solutions will be:

And once again make sure, on an arbitrary version, that everything works:
 $ ./bomb ... That's number 2. Keep going! 4 389 Halfway there! 

Level 4


sym.phase_4


sym.func4


Recursion appears at this level. In addition to the built-in ASCII-graphs, it is also possible to get graphs as files for the dot utility, and then, for example, convert to png.
 [0x0040149e]> ag sym.func4 > func4.dot [0x0040149e]> dot -Tpng -o func4.png func4.dot 


If all this is translated into C, it will look almost not so scary.
 void phase_4(char *str) { int x, y; if (sscanf(str, "%d %d", &x, &y) != 2 || x > 14 || func4(x, 0, 14) || y != 0) explode_bomb(); } int func4(int x, int y, int z) { unsigned diff = (z - y)/2; int p = y + diff; if (p > x) { func4(x, y, p-1); return diff * 2; } else if (p < x) { func4(x, p + 1, z); return diff * 2 + 1; } return 0; } 

The simplest and most obvious solution is that the func4 function returns 0 when it does not go inside an else-if. User input controls only x , and p = 7 at the first entry. Accordingly, when x = 7, the function simply returns 0, without recurrent calls. The second variable is strictly set to zero. We will see this.
 $ ./bomb in.tmp ... Halfway there! 7 0 So you got that one. Try this one. 

Level 5



It will be more difficult with this, a lot of code is piled up here.
In 0x00401073 the canary is written on the stack. Similar information about the binary can be obtained using the i command. For example, i ~ canary will return true in this case.
After that, the return value of string_length is compared with 6 and the hard-to-analyze spaghetti code begins. Dealing without a debugger with this is long and difficult, so it would be a sin not to use it. To do this, open the file with the debug flag or at startup:
 $ r2 -Ad bomb 

or simply reopening the file:
 [0x0040149e]> ood 

The address of the pointer will automatically change to the first opcode in the entry-point, which is already loaded into memory. As usual we set breakpoints and continue execution to them.
 [0x7f2960b99d80]> db sym.phase_5 #  s sym.phase_5; db $$ [0x7f2960b99d80]> dc #    1  

Then there are 2 ways to analyze: the first is to use the d / db command block, the second is to switch to Visual-mode. The first method is not so visual, so let's stop on the second.
As before, go to Visual-mode, and then select the required debug-layout for debugging hotkeys p / P.
You can mix by code using n / N - next / previous function; j / k is the next previous opcode.
And the most interesting: b / F2 - set breakpoint, s / F7 - step in 1 opcode, S / F8 - step in 1 opcode without entering the call, F9 - continue until breakpoint.
This is how it all looks.


In total, after feeding several lines to the debugger, it is not difficult to guess what is happening there.
For each character from our string modulo 16, the corresponding character is taken from the string char *s = "maduiersnfotvbyl" and the resulting string is compared to "flyers".
Actually, our task is to find such characters whose indices in str give the search string.
A string of flyers can be obtained uniquely: {s [0x9], s [0xe], s [0xf], s [0x5], s [0x6], s [0x7]}. I think that not everyone keeps the ASCII table in his head, so you can again refer to the rax2 utility for help. With the -s option, it converts from hex to string. Since characters are taken modulo 16, then for aesthetics, you can choose printable values.
 $ rax2 -s 49 4e 4f 45 46 47 INOEFG 

 $ ./bomb ... So you got that one. Try this one. INOEFG Good work! On to the next... 

Line digression
I have been thinking for a very long time and still think that there can be some meaningful word. So if suddenly someone will find I will be extremely grateful :)

The last one


Since the article was too big, I will not consider all the phases. In particular, I will omit the 6th phase, because there is a lot of painstaking understanding without the direct participation of the framework. Let it remain the most inquisitive homework.
Getting into the secret phase is not so easy, so simplify your life by patching the binary. We will have to reopen the file, issuing write permissions with the -w flag, or rediscover it using oo + without leaving r2.
 $ r2 -Aw bomb -- Did you ever ordered a pizza using radare2? [0x00400c90]> s 0x00400ec6 # call sym.phase_6 [0x00400ec6]> wa call sym.secret_phase #    [0x00400ec6]> pdf @ sym.secret_phase; pdf @ sym.fun7 

secret_phase


fun7


Again, a recursive function appears and this time it is not so easy to bypass it, because without recursive calls the desired value is as simple as it was last time, not to receive. ASCII graph can simplify life again.

The analysis gives approximately the following analogue in C:
 void secret_phase() { long num = strtol(read_line()) - 1; if (num > 0x3e8 || fun7((long*)(0x6030f0), num) != 2) explode_bomb(); puts("Wow! You've defused the secret stage!"); phase_defused(); } int fun7(long *array, int num) { if (array == 0) return -1; if (*array <= num) { if (*array == num) return 0; // 1 else { return 2 * fun7(array + 1, num); // 2 } } else { return 2 * fun7(array + 2, num) + 1; // 3 } } 

So, to get exactly two at the output of fun7 , we need to first call ret on line 2 , then at 3 and finally at 1 . There remains only one mystery - that is stored at 0x6030f0 .
480 bytes
 [0x00401204]> px 480 @ 0x6030f0 - offset - 0 1 2 3 4 5 6 7 8 9 ABCDEF 0123456789ABCDEF 0x006030f0 2400 0000 0000 0000 1031 6000 0000 0000 $........1`..... 0x00603100 3031 6000 0000 0000 0000 0000 0000 0000 01`............. 0x00603110 0800 0000 0000 0000 9031 6000 0000 0000 .........1`..... 0x00603120 5031 6000 0000 0000 0000 0000 0000 0000 P1`............. 0x00603130 3200 0000 0000 0000 7031 6000 0000 0000 2.......p1`..... 0x00603140 b031 6000 0000 0000 0000 0000 0000 0000 .1`............. 0x00603150 1600 0000 0000 0000 7032 6000 0000 0000 ........p2`..... 0x00603160 3032 6000 0000 0000 0000 0000 0000 0000 02`............. 0x00603170 2d00 0000 0000 0000 d031 6000 0000 0000 -........1`..... 0x00603180 9032 6000 0000 0000 0000 0000 0000 0000 .2`............. 0x00603190 0600 0000 0000 0000 f031 6000 0000 0000 .........1`..... 0x006031a0 5032 6000 0000 0000 0000 0000 0000 0000 P2`............. 0x006031b0 6b00 0000 0000 0000 1032 6000 0000 0000 k........2`..... 0x006031c0 b032 6000 0000 0000 0000 0000 0000 0000 .2`............. 0x006031d0 2800 0000 0000 0000 0000 0000 0000 0000 (............... 0x006031e0 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x006031f0 0100 0000 0000 0000 0000 0000 0000 0000 ................ 0x00603200 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x00603210 6300 0000 0000 0000 0000 0000 0000 0000 c............... 0x00603220 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x00603230 2300 0000 0000 0000 0000 0000 0000 0000 #............... 0x00603240 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x00603250 0700 0000 0000 0000 0000 0000 0000 0000 ................ 0x00603260 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x00603270 1400 0000 0000 0000 0000 0000 0000 0000 ................ 0x00603280 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x00603290 2f00 0000 0000 0000 0000 0000 0000 0000 /............... 0x006032a0 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0x006032b0 e903 0000 0000 0000 0000 0000 0000 0000 ................ 0x006032c0 0000 0000 0000 0000 0000 0000 0000 0000 ................ 


Here 8-byte variables are stored in blocks of 4. The first is used to compare with our number; the second and third are pointers to the following characters for comparison; the fourth is just padding, not used.
Now you can expand all the conditions and find the desired variable.

 $ ./bombSec in.tmp ... Good work! On to the next... 22 Wow! You've defused the secret stage! Congratulations! You've defused the bomb! 


Ehu, we made it! Humanity can live in peace . Despite the fact that the article came out a bit too big, quite a lot of framework features were not covered, so those interested can still discover a lot for themselves. Also, I will be glad to hear ideas for possible continuation.

Source: https://habr.com/ru/post/274617/


All Articles