Skip to content

Latest commit

 

History

History
55 lines (43 loc) · 3.69 KB

ret2libc.md

File metadata and controls

55 lines (43 loc) · 3.69 KB

What is ret2libc?

Basically, a ret2libc attack requires you to create a short ROP chain that leaks one or more libc addresses, returns to main to allow additional input, then calls an additional ROP chain that leverages functions and offsets within the libc, such as system. You will typically need to leak libc every time that the program is run because ASLR will cause the base offset to change every time that the program is run, just like addresses on the stack.

A great target for leaking libc addresses is GOT and PLT entries. These are used in the program to provide links within the code section to the libc section, so in a non-PIE binary, they will be at known, constant locations. There should be one of each for every function in the program from libc; in this case, these include functions like fgets, puts, and printf. When the program initially starts, all the GOT entries are set to the addresses of the second instruction in PLT entries.

When functions are called, it is actually the PLT functions that are called. The first instruction of the PLT function is to jump to whatever is pointed to by the contents of the GOT entry. If the function has never been called before, this simply pings it back to the next line of PLT, which, presumably by dark magic, will locate the corresponding function in libc, execute it, and add its address to the GOT entry. If the function has already been executed before, the GOT entry will contain the corresponding libc address, and there will be no need for the dark magic linking ritual. For reference, here is what the GOT table looks like in GDB-GEF towards the start of the program, before most of the libc functions have ever been executed.

gef➤  got

GOT protection: Partial RelRO | GOT functions: 7

[0x405018] puts@GLIBC_2.2.5  →  0x401036
[0x405020] setbuf@GLIBC_2.2.5  →  0x7ffff7e55c50
[0x405028] printf@GLIBC_2.2.5  →  0x401056
[0x405030] fgets@GLIBC_2.2.5  →  0x401066
[0x405038] strcmp@GLIBC_2.2.5  →  0x401076
[0x405040] fopen@GLIBC_2.2.5  →  0x401086
[0x405048] exit@GLIBC_2.2.5  →  0x401096

And here is what it looks like toward the end of the main function:

gef➤  got

GOT protection: Partial RelRO | GOT functions: 7

[0x405018] puts@GLIBC_2.2.5  →  0x7ffff7e4e5a0
[0x405020] setbuf@GLIBC_2.2.5  →  0x7ffff7e55c50
[0x405028] printf@GLIBC_2.2.5  →  0x7ffff7e2be10
[0x405030] fgets@GLIBC_2.2.5  →  0x7ffff7e4c7b0
[0x405038] strcmp@GLIBC_2.2.5  →  0x401076
[0x405040] fopen@GLIBC_2.2.5  →  0x401086
[0x405048] exit@GLIBC_2.2.5  →  0x401096

Here is what the before and after addresses point to:

gef➤  x/5i 0x401036
   0x401036 <puts@plt+6>:       push   0x0
   0x40103b <puts@plt+11>:      jmp    0x401020
   0x401040 <setbuf@plt>:       jmp    QWORD PTR [rip+0x3fda]        # 0x405020 <[email protected]>
   0x401046 <setbuf@plt+6>:     push   0x1
   0x40104b <setbuf@plt+11>:    jmp    0x401020
gef➤  x/5i 0x7ffff7e4e5a0
   0x7ffff7e4e5a0 <__GI__IO_puts>:      endbr64
   0x7ffff7e4e5a4 <__GI__IO_puts+4>:    push   r14
   0x7ffff7e4e5a6 <__GI__IO_puts+6>:    push   r13
   0x7ffff7e4e5a8 <__GI__IO_puts+8>:    push   r12
   0x7ffff7e4e5aa <__GI__IO_puts+10>:   mov    r12,rdi

All of this means that the GOT contains libc addresses, so if we can just print them to the console, we have an effective libc leak. Typically, we can accomplish this by using any libc function for which a PLT address is available that writes to the console; puts and printf are preferred since they can do it with control over the rdi register (this sets your parameter for a function in x86-64). Essentially, you can just pop a GOT entry into libc, add the PLT entry for puts, then call main again so that you can actually use your leak in a fresh chain.