I’ve recently finished reading a book called Practical Binary Analysis which I consider a state of the art book (review will come soon) and I would like to post my solution to the crackme found in chapter 5.
1. A New CTF ChallengeComplete the new CTF challenge unlocked by the oracle program!You can complete the entire challenge using only the tools discussedin this chapter and what you learned in Chapter 2. After completingthe challenge, don’t forget to give the flag you found to the oracle tounlock the next challenge
To do the crackme and view the code covered in the book you don’t have buy it, you can download it for free from the official website. If you’re not on a Linux host system, I recommend downloading the VM which comes with the book from here:
In chapter 5 the author analyzes the first level of the crackme using only Linux tools and explaining every step in detail, I will move past the first level and go straight to level 2.
Let’s get started, download the code or the VM and navigate to the chapter5 directory if you don’t see the lvl2 binary you can unlock it by executing the following command in the terminal:
Let’s let’s see what’s this binary is all about, we can identify that it’s an elf binary with the file command. Knowing that it’s an elf, can can do a readelf -a lvl2 > lvl2_readelf.txt on it, which gives us the following output:
ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x400540 <- this is what we need. Start of program headers: 64 (bytes into file) Start of section headers: 4624 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 9 Size of section headers: 64 (bytes) Number of section headers: 29 Section header string table index: 28 ...
If we run the binary we just get a random byte on the screen, let’s open gdb and set a breakpoint to the entry point address by typing the following commands:
gdb lvl2 b * 0x400540 r # To run the program. It will now stop at the breakpoint we set. set disassembly-flavor intel # To show the disassembly in intel syntax. disassemble # Under normal circumstances this would show us the disassembly # In this case it doesn't work, we can view the instruction on which we are # by using x/i $pc. $pc is the program counter register. x/i $pc # => 0x400540: xor ebp,ebp display/i $pc # This makes GDB automatically display the next instruction to be executed, each time your program stops. si step instruction si si ... q # This doesn't seem to work, let's quit and see what else can we do.
If we do a ltrace ./lvl2 we can see the library calls the program does:
__libc_start_main(0x400500, 1, 0x7fff5c6b0ec8, 0x400640 <unfinished ...> time(0) = 1543667140 srand(0x5c027dc4, 0x7fff5c6b0ec8, 0x7fff5c6b0ed8, 0) = 0 rand(0x7ff89538f620, 0x7fff5c6b0dac, 0x7ff89538f0a4, 0x7ff89538f11c) = 0x6e66a24a puts("d6"d6) = 3 +++ exited (status 0) +++
If we google for __libc_start_main() we’ll find that:
int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end));
The __libc_start_main() function shall initialize the process, call the main function with appropriate arguments, and handle the return from main().
Which means the first address is the pointer to the main function. From our ltrace call we have: 0x400500
If we disassemble the binary with lvl2 with: objdump -d -M intel lvl2 > lvl2_dasm.txt we can see that the address we’re hunting is at the beginning of the .text section.
0000000000400500 <.text>: 400500: 48 83 ec 08 sub rsp,0x8 400504: 31 ff xor edi,edi 400506: e8 c5 ff ff ff call 4004d0 <time@plt> 40050b: 89 c7 mov edi,eax 40050d: e8 ae ff ff ff call 4004c0 <srand@plt> 400512: e8 c9 ff ff ff call 4004e0 <rand@plt> 400517: 99 cdq 400518: c1 ea 1c shr edx,0x1c 40051b: 01 d0 add eax,edx 40051d: 83 e0 0f and eax,0xf 400520: 29 d0 sub eax,edx 400522: 48 98 cdqe 400524: 48 8b 3c c5 60 10 60 mov rdi,QWORD PTR [rax*8+0x601060] 40052b: 00 40052c: e8 6f ff ff ff call 4004a0 <puts@plt> 400531: 31 c0 xor eax,eax 400533: 48 83 c4 08 add rsp,0x8 400537: c3 ret 400538: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0] 40053f: 00
What does the binary do? It does a srand(time(0)) and calls rand(), does some computation and prints something from QWORD PTR [rax*8+0x601060] if we go back to the debugger or look at the readelf output again we can identify that 0x601060 is not far from the .data section which starts at 0x601040. If we dump the section we get the following:
binary@binary-VirtualBox:~/code/chapter5$ objdump -s --section .data lvl2 lvl2: file format elf64-x86-64 Contents of section .data: 601040 00000000 00000000 00000000 00000000 ................ 601050 00000000 00000000 00000000 00000000 ................ # This is were we should be looking 601060 c4064000 00000000 c7064000 00000000 ..@.......@..... # The addresses are in hexadecimals thus if we add 16 bytes (0x10 in hex) to 601060 we get 601070, a byte is represented as two adjacent hexadecimals like 0xCA 601070 ca064000 00000000 cd064000 00000000 ..@.......@..... 601080 d0064000 00000000 d3064000 00000000 ..@.......@..... 601090 d6064000 00000000 d9064000 00000000 ..@.......@..... 6010a0 dc064000 00000000 df064000 00000000 ..@.......@..... 6010b0 e2064000 00000000 e5064000 00000000 ..@.......@..... 6010c0 e8064000 00000000 eb064000 00000000 ..@.......@..... 6010d0 ee064000 00000000 f1064000 00000000 ..@.......@.....
This section looks like it’s containing pointers to something followed by a null: ca064000 00000000. The address ca064000 is in little endian, to make it in big endian we rewrite it as: 004006ca which is an address that is closed to the .rodata section. Let’s dump it:
 .rodata PROGBITS 00000000004006c0 000006c0 0000000000000034 0000000000000000 A 0 0 4
binary@binary-VirtualBox:~/code/chapter5$ objdump -s --section .rodata lvl2 lvl2: file format elf64-x86-64 Contents of section .rodata: 4006c0 01000200 30330034 66006334 00663600 ....03.4f.c4.f6. 4006d0 61350033 36006632 00626600 37340066 a5.36.f2.bf.74.f 4006e0 38006436 00643300 38310036 63006466 8.d6.d3.81.6c.df 4006f0 00383800 .88.
And this is most likely the flag we’re searching for. Thanks for reading 😀