Elf x64 Stack buffer overflow advanced ( App-system )( Introduction to ROP x64)
I call this Introduction to ROP ( Return Oriented Programming ), But this challenge is not as easy as you think. It is statically linked binary in an x64 architecture. But I’ll try my best to explain the concept of ROP here. Feel free to skip the introduction part if you are good at Basic ROP.
Note: I will be writing more about x64 here. x86 is a bit different. Unfortunately, Root-me doesn’t have an easy ROP challenge. Anyways, let’s see the definition of ROP. You’ll have to learn a lot about ROP before starting the challenge.
Return-oriented programming (ROP) is a computer security exploit technique that allows an attacker to execute code in the presence of security defenses such as executable space protection and code signing.
I hope you have read my previous blogs to understand various memory protection. I have written about Ret2libc in my previous blog. You might have seen linking system() address and Libc addresses (/bin/bash) to get the shellcode. This was nothing but an ROP. Here, we will learn more about ROP chains.
For now, let us take ROP chains as a set of instructions that may or may not be useful. These are called ROP gadgets. But how do we know about the gadgets which are present in a given binary? How can we find a useful gadget?
We can find gadgets by using a Tool called ROPgadget or Ropper. You could download the tools from their respective GitHub repositories. And for the question on how to find useful gadgets, We can use grep.
Now I’ll explain about using the stack for our exploit. We first fill up our buffer, as usual, overwrite the padding and EBP. We will be forming our chain in the EIP region. Did you notice something wrong here? We are working with an x64 machine and we do not have EBP and EIP. Instead, these are called RBP and RIP. When we pop a register and return, we move one step down as usual.
So it is necessary to find a gadget ending with “ret” for our convenience. Or to put it this way, gadgets ending with “ret” are useful and we will be using them. We will see about searching for useful gadgets directly in the challenge.
Before going into the challenge, you need to know more about “pop” and “mov” instructions in the assembly code.
The pop instruction removes the 8-byte data element from the top of the hardware-supported stack into the specified operand
So to store anything in a particular register, we “pop” the register and then store the appropriate value (we can give the value explicitly in the stack).
The mov instruction copies the data item referred to by its second operand into the location referred to by its first operand
We can copy a word or even a pointer to a register or memory location specified. Oh, I almost forgot to tell you about this. You need to be good at C/C++ pointers. It does play an important role here.
Now that you know about “pop” and “mov”, let’s get to the challenge.
Let us try to analyze the source code.
Looks like a normal code where we can inject shellcode into the buffer and call it from RIP. But we forgot about one thing here.
It has ASLR and NX Bit ON. So we cannot use Ret2libc and normal Buffer overflow attack here. This is because the stack keeps changing address and it would be really difficult to find a stable address. Also, the Libc is not present in this challenge when we use “info proc map”. This is because the binary is statically linked.
So we have to find gadgets which can store the shell-code and other useful information about it. We need to know what registers and gadgets you’ll be needing for this challenge. First, we need to see the Linux system call table ( X64) for the most important command “sys_execve”. We will have to know the format of “execve” first.
execve(const char *pathname , char *const argv , char *const envp);
Now we need to know where the execve , pathname , argv, envp has to be stored.
The above link will tell the registers linked with the parameters.Below is the system call which we will be using the challenge.
So %RAX = 59 . %RDI = filename, which is a pointer and so we need to specify a pointer location to “/bin/bash”. “/bin/bash” itself has to be stored in the memory location where there is free space. %RSI and %RDX can be null for the approach I’ll be doing. There are 3 ways to approach this challenge. You may need to give value for %rsi and %rdx if you are following any other method.
So far so good? We need to find “pop RAX”, “pop RDI”, “pop RSI”, “pop RDX” all ending with a “ret”. For finding this, we will use a tool called ROPgadget. We straight away start searching for easy gadgets that we can use. As mentioned earlier, we will be using pop to store the value of RSI and other registers. Below are the easiest gadgets which we will use in our program
But there is an issue now. You need to store the value of “/bin/bash” into the memory location. We need to use the mov gadget. I have picked an easy one which we can use for this exploit. I grepped for “mov qword ptr”
This will be an easy gadget, as we need to copy a word to a pointed location ( the variable [RSI] will have pointer to an address)
Now we need the most important syscall, which again ROPgadget will help us
We will search for other gadgets as and when we need them. For now, this will do. One more thing to note: We have got perfect gadgets for our exploit. But sometimes you might need to combine a set of instructions that will be suitable for the exploit. Select a set of instructions where one or more instructions will be a dummy like “pop r15; pop rax; ret” where r15 has no use ( It may have, so be careful while choosing gadgets).
Initial Exploit Development
Let us develop our exploit now. The first step is to store “/bin/bash” in a location where it can be stable ( Not affected by any memory protection ). To do this we follow this. This can be done in multiple ways. I would like to show 2 methods and the difference between them.
This is the normal gdb without flavours. We do not get any detail of the read write permission. We just guess the above underlined is the starting and ending address.
In gef, everything is absolutely clear. The perms are set to rw, So we can write “/bin/bash” in any location inside this range.
To write “/bin/bash” into a memory location we need to follow these steps. “/bin/bash” is 9 bytes. Unfortunately, we can only store and write 8 bytes with mov and pop. So we will use the method twice to store 16 bits of data, but only 9 bytes will occupy the memory location, others will just be “\x00”. Drawing an image would make things simple.
So for this, we will use “pop rax; ret”, “pop rsi; ret”, and “mov qword ptr [rsi], rax”. We will store the free address to RSI after “pop rsi; ret”(6c0000, it can be anything between the given free-range). Then we “pop rax; ret” and store “/bin/bas”. Then we copy the value “/bin/bas” to location 6c0000 with,mov command. We repeat the steps to store “h” to the 6c0008 location.
I will reveal the exploit till this part. The buffer is filled with 280 random characters to overwrite RBP and padding. I have explained how to find the buffer size with drawing in my previous blog.
Exploit Code for storing “/bin/bash”
I hope you understood this part. Give it a read again if you don't feel confident.
PS: It took me a day to understand the above part.
Exploit Development for Execve
Now comes the easy part. As discussed earlier %RAX = 59 . %RDI = filename. RSI and RDX are null for this approach I am going to follow.
This is very easy as you can pop and store value. But before that, the execve has another format. execve ( 0 , 0 ) . We need to specify this too.
We use pop rax and store 59 in hex, then pop RDI and store the memory location of “/bin/bash”. The /bin/bash string will be read until it reaches \x00. But how do we now move stack pointer forward ???? We need to store 2 more values before syscall. For this, we need to search for an appropriate gadget. To save time I’ll tell you what gadget I used. I used “pop RDX; pop RSI; ret” As the values are NULL for those variables here. So this will move the stack forward and then we store 0x00. and finally, call the syscall
Exploit Code for Execve
So we combine the Exploit for storing /bin/bash and execve and run the code using python3
NO PRIVILEGED SHELL !!! UGHHHH
Exploit Development for Privileged Shell
We need to find another way to get our shell. We could use setreuid for getting a privileged shell. You can find this in the Linux system call table. So now RAX has to be set to 113 in hex and rdi to uid_t ruid and Rsi has to be set to uid_t euid.
But what are the values for this ? This can be found in file /etc/passwd. cat this file out .
we want cracked-ch34. So we look for it. Both uid_t ruid and uid_t euid have to be set to 1234 in hex. and then we call syscall function. This code will come before execve.
Exploit code for Setreuid in python
Note: this should come before execve.
Now we run the program with full exploit code.
AND WE GET THE PRIVILEGED SHELL!!!!
I would not spoil the fun for you guys :) So write the exploit on your own and get the flag.
Hope you understood ROP. I know it will be hard to understand at first, but try to do the challenge on your own. Also, draw a stack diagram like me.
Do give some claps if you reached here :). And feel free to ask your doubts in the comment section. It would help others too! Do follow for more challenges.