2026CISCN半决赛minidb详解
字数 4125
更新时间 2026-04-22 13:01:55
2026CISCN 半决赛 minidb 漏洞分析与利用教学文档
1. 概述
本教学文档基于一篇关于2026年CISCN(全国大学生信息安全竞赛)半决赛题目 minidb 的二进制安全分析文章。minidb 是一个模拟简易数据库的题目,涉及堆利用、未初始化变量、引用计数等漏洞的综合利用。文档将详细拆解题目的关键数据结构、漏洞原理及完整的利用过程。
2. 核心数据结构分析
理解程序的结构体是分析漏洞的基础。题目中定义了四个关键结构体。
2.1 DbValue 结构体
作用:存储数据库中每个键(key)对应的值(value)的实际数据。
关键成员:
refcount:引用计数,记录有多少个key共享此value。clone操作会使其增加;set、abort、exec操作会使其减少,当计数为0时会free该值。length:记录value的实际长度。data:指向真正的value内容。
2.2 DbEntry 结构体
作用:对用户暴露的、存储键值对映射的主要结构,进行了简化。
关键成员:
key:用户通过SET命令设置的变量名。value:指向一个DbValue结构体的指针。
2.3 TxContext 结构体
作用:在事务(Transaction)模式下,保存单个键修改的上下文信息,主要充当“回滚快照”。
关键成员:
key:当前事务正在修改的键。old_value:该键在事务开始前的旧值指针。new_value:事务中试图写入的新值指针。has_snapshot:标志位,记录是否为该键保存过快照。
2.4 全局数据库引擎结构体(文中未命名)
作用:连接并保存整个数据库的全局状态,是程序的核心全局对象。
关键成员:
DbEntry *buckets[64]:哈希桶数组。数据库通过哈希表管理所有DbEntry。TxContext *current_tx:指向当前事务上下文的指针。in_tx:标志位,表示是否处于事务模式。
哈希桶机制补充说明:
- 哈希函数:对键名(key)进行哈希,并只取低6位,因此结果范围是0-63,正好对应64个桶。
- 冲突处理:哈希到同一桶的
DbEntry以链表形式连接。 - 示例:假设有键
a,cat,tmp,user,flag,经过哈希后可能分布如下:- 3号桶:
flag->cat->a - 8号桶:
tmp - 20号桶:
user
- 3号桶:
- 查找时,先计算键的哈希值找到对应桶,再遍历链表进行精确匹配。
3. 程序主要逻辑与漏洞分析
程序支持 SET、GET、CLONE、MULTI、EXEC、ABORT 等命令。
3.1 主逻辑与命令解析
main函数读取用户输入,并根据空格进行两次切割来解析命令和参数。- 需要输入如
SET key value这样的格式。
3.2 漏洞点详解
程序存在多个安全漏洞,以下是详细分析:
1. SET 命令中的释放后重用(UAF)
- 在事务模式下执行
SET时,存在一个释放路径。 - 漏洞代码:在释放
old_value时,代码没有检查其refcount(引用计数),而是直接调用free。 - 产生条件:如果这个
old_value被其他DbEntry通过CLONE共享(即refcount > 1),那么直接free会导致其他指针变成悬垂指针(Dangling Pointer),形成UAF。 - 核心影响:释放后,攻击者依然可以通过持有该指针的
CLONE对象对已释放的内存进行读写操作。
2. CLONE 命令的浅拷贝
CLONE操作实现的是浅拷贝。- 它不复制实际的
DbValue数据(data字段),而是让新的DbEntry指向同一个DbValue结构体,并增加其refcount。 - 这与上述SET的漏洞结合,是构造UAF利用链的关键。
3. MULTI 命令的未初始化堆漏洞
MULTI命令会malloc一个大小为0x38的TxContext结构体,并将其设置为当前事务上下文。- 漏洞:
malloc后没有对该内存进行初始化。TxContext结构体内的字段(如key、old_value、has_snapshot)将包含堆上的残留数据(脏数据)。 - 后续的
EXEC和ABORT命令会盲目信任这些未初始化的值。
4. EXEC 命令的任意地址释放
EXEC函数完全信任从当前事务上下文(TxContext *tx)中读取的数据。- 它会使用
tx->key找到记录,并根据tx->old_value等字段进行回滚和释放操作。 - 漏洞利用:通过堆风水等手段,可以控制未初始化的
TxContext结构体中的old_value指针,使其指向任意地址。当EXEC被调用时,会尝试释放该地址,造成任意地址释放(Arbitrary Address Free)。
5. ABORT 命令的任意地址泄露
- 类似地,
ABORT函数也信赖未初始化的TxContext数据。 - 它会根据
tx->old_value等指针进行一些操作,这可以被利用来读取特定地址的内存内容,实现信息泄露(如泄露libc地址、堆地址、栈地址)。
3.3 漏洞总结
- UAF漏洞:
SET在事务模式中不检查引用计数直接释放old_value,与CLONE的浅拷贝结合,形成释放后重用。 - 未初始化堆漏洞:
MULTI分配的TxContext未初始化,其内容为堆上的残留数据。 - 逻辑漏洞:
EXEC和ABORT盲目信任未初始化的TxContext,导致:- 通过控制脏数据,利用
EXEC可实现任意地址释放。 - 通过控制脏数据,利用
ABORT可实现任意地址读(信息泄露)。
- 通过控制脏数据,利用
4. 漏洞利用思路与步骤
利用过程需要结合堆风水(Heap Feng Shui)进行精细布局。
4.1 利用准备:理解堆布局
SET命令会申请两个堆块,分别对应DbEntry和DbValue结构体。需要先分析清楚这两个结构体在内存中的布局和大小。- 目标是控制这些堆块,最终劫持
TxContext结构体。
4.2 利用步骤
第一阶段:泄露堆地址
- 利用
SET的UAF和CLONE的浅拷贝,在释放一个DbValue后仍保持对其的引用。 - 通过
GET命令读取悬垂指针指向的内存,可以泄露堆管理器的元数据(如fd、bk指针),从而计算出堆的基地址。
第二阶段:泄露libc地址与栈地址
- 在获取堆控制能力后,下一步是劫持一个
DbEntry结构体。 - 通过伪造
DbEntry的内容,可以使其value指针指向全局数据库引擎结构体(g_db_engine)或其他已知结构。 - 利用
GET或程序的其他输出功能,读取这些结构体中的指针。例如,g_db_engine可能包含指向libc中全局变量的指针(用于泄露libc基址),或者包含栈地址(用于泄露栈布局)。
第三阶段:劫持控制流(以劫持栈为例)
- 目标:通过任意地址写,在栈上伪造返回地址或函数指针,最终劫持程序控制流。
- 利用任意地址释放(
EXEC)和堆溢出等技术,在堆上精心布局,伪造一个TxContext结构体。 - 控制伪造的
TxContext中的old_value等指针,使其指向栈上的某个关键位置(例如某个函数的返回地址)。 - 调用
EXEC,程序会根据伪造的TxContext对栈地址进行释放和写入操作,从而破坏栈数据。 - 结合信息泄露得到的栈地址和libc中的gadget地址(如
system地址、one_gadget),最终实现代码执行。
4.3 利用难点
- 题目没有提供直接的
EDIT功能,增加了修改内存的难度,必须完全依靠SET、CLONE、MULTI、EXEC、ABORT的原生命令来构造利用链。 - 需要对堆内存布局有非常精准的预测和控制。
- 逆向工程是前提,必须清晰理解所有结构体关系和整个状态机。
5. 核心要点总结
- 逆向定基础:成功利用的前提是完整逆向出
DbValue、DbEntry、TxContext及全局引擎等核心结构体,并理解其生命周期和交互逻辑。 - 漏洞本质:
- 主漏洞是事务模式下
SET对引用计数的管理缺失导致的UAF。 - 漏洞放大器是
MULTI的未初始化堆分配,使得攻击者可以通过堆喷等手段控制关键数据结构。 - 漏洞触发点是
EXEC/ABORT对不可信输入(未初始化的TxContext)的无校验使用。
- 主漏洞是事务模式下
- 利用链条:
CLONE(制造多引用) ->SETin tx(触发UAF释放) -> 堆布局/风水 -> 控制未初始化的TxContext-> 利用EXEC进行任意地址写或利用ABORT进行信息泄露 -> 最终劫持控制流。 - 防御思路(扩展知识):
- 始终初始化动态分配的内存。
- 对引用计数进行原子操作和严格检查,确保减到零才释放。
- 对来自不可信源(如未初始化内存、用户输入)的指针进行有效性校验。
- 使用更安全的内存分配器(如Scudo, GWP-ASan)增加利用难度。
通过本题的分析,可以深入理解现代堆漏洞利用中如何将多种低级漏洞(UAF、未初始化数据、逻辑缺陷)串联成一条完整的攻击链,这对二进制安全攻防研究具有典型意义。
相似文章
相似文章