ROP attacks via Buffer Overflow using Pwntools-Part 2
In this article, I am going to talk about how to get code execution by building a ROP chain and exploiting a buffer overflow vulnerability.
In the previous article I explained about what ROP is and changing the execution flow of a program with this it. If you are not familiar with ROP binary exploitation, I recommend you to go check that out first.
ROP attacks via Buffer Overflow using Pwntools - Part 1.
In this article, I am going to talk about what ROP is and how to exploit a buffer overflow vulnerability with it using…
You can get the source code and the exploit scripts from my GitHub if you want to follow along from here.
What’s next ?
As we were able call the main function twice, now we can go ahead and find a ROP chain for this. But wait… What is a ROP chain ?
“A ROP chain is chaining together small snippets of assembly with stack control to cause the program to do more complex things”
It might be a bit difficult to visualize what it is just from a small description. So let’s apply this to our program in order to understand what this is.
Till now, we were able to make the program execute main function twice by pointing the RIP to the address of the main. But that is not what we need right? We need a way to execute code. So that’s when ROP chains come in.
Why ROP chains ?
In C we can use the system() function to execute code by passing the command as an argument. In a ROP chain what we do is that instead of calling a pre-defined function like main, we are calling the system() function to be able to get command execution.
Well, it’s not that easy. You can’t just call the system as we did main. Because in the system function we have to pass an argument to be executed. To understand this, you have to have a basic understanding about how x64-bit calling conventions and assembly instructions works.
Linux x64 Calling Convention: Stack Frame
Below is a screenshot that shows where the 9 arguments 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9 passed to the…
X86-64 Architecture Guide
This handout only mentions a small subset of the rich possibilities provided by the x86-64 instruction set and…
In x64-bit architecture, the arguments we pass in to the functions are stored in registers on the stack, specifically in this case the RDI register (only the first argument)
So in order for system function to be called, we will have to find a ROP gadget with POP RDI; RET instruction which will copy the RDI register to RSP (Stack Pointer) and then return.
How do we find it ?
To make a rop chain we have to find a ROP gadget first, which is just a snippet of instructions that lives inside the program itself.
We can use ROPgadget to automatically find these for us. You can install it with pip.
pip install ROPgadget
If you run ROPgadget against our binary with
--binary option, you will see all the ROP gadgets it finds.
ROPgadget — binary bof
Here we get a lot of gadgets with it’s addresses. We also have a “pop rdi; ret” instruction as well.
Now we can use this to build a ROP chain. But… Instead of doing it manually, we can try to use ROPgadgets for that. You can run it with
--ropchain option and let it do it’s magic.
ROPgadget — binary bof — ropchain
If you look down at the bottom you will see it’s automatically finding the “pop rdi; ret” gadgets.
Also this analyzes the binary and gives us the exploit code for the ropchain as we used the
Bur remember, this will only work if it finds a “mov qword ptr [r64], r64” instruction. Well, what if it doesn’t. Can’t we exploit such as binary ? Yes we can. In that case we will have to build a ROP chain manually. But that’s an article for another day.
Let’s get a shell, shall we ?
Now that we have a ROP chain to work with, let’s see how we can use it.
Here I have done a couple of things. First, I am running the ROPgadget tool with the subprocess library and saving the output as rop. After, I check if it has given us the python code for the ROP chain or not. And then I am just doing some python magic and just carving out the part where we have our exploit code for the ROP chain.
This will generate our ROP chain. Now what we have to do is, we have to run this script and get the output. Then we have to add it to our payload.
This will exactly do what I said before. This will run the script, save the output to a file called payload and read the output as binary data. Notice that for this to work you will need python2 as well.
Now we have a the payload that looks like some garbage bytes for the ROP chain to work and execute system(). Let’s use hexedit and try to see what this actually does.
And right off the bat we can see /bin/sh. Maybe it’s passing /bin/sh as an argument and executing it. Let’s find out.
I have just added the ropchain to the payload after our “A”s so that it will fill up the stack till the RIP and then trigger our ropchain which will then execute /bin/sh.
Lastly, I made this process interactive with p.interactive() so that we can keep the shell open and interact with it. Let’s run our final exploit and see what we get.
And BOOM !!! We have a shell.
Now you should have some kind an understanding about how the ROP attack works against buffer overflows and how we can exploit them to get an interactive shell.
But this method is not a very common method of using ROP attacks. In a future article I will talk about other methods and how we can build the ROP chain manually instead of letting ROPgadgets do it.
If you have any questions make sure leave it down in the comments and I will try my best to answer.
Happy hacking !!!