Defcon 2023 rest-and-attest
字数 1474 2025-08-06 12:21:05

Defcon 2023 rest-and-attest Rust Pwn 题目分析与利用

题目概述

这是一个Rust编写的Pwn题目,模拟了TPM(Trusted Platform Module)安全模块的功能。题目包含多个组件,主要漏洞存在于SFM(Secure Firmware Module)模块中,通过精心构造的输入可以实现栈溢出攻击。

文件结构

bin/
launcher
run_challenge.sh
sfm
uploader
wrapper.sh
lib/
libcrypto.so.3
libc.so.6
libgcc_s.so.1
src/
Cargo.lock
Cargo.toml
sfm/
Cargo.toml
src/
lib.rs
main.rs
sfm_proto.rs
sfm-sys/
build.rs
Cargo.toml
src/
lib.rs
vendor/
uploader/
Cargo.toml
src/
main.rs
trusted_firmware.raw

程序流程分析

主程序入口 - Uploader

Uploader程序提供四个主要功能:

  1. upload - 上传一段shellcode二进制程序
  2. download - 下载现有的shellcode二进制
  3. run - 使用launcher运行对应的shellcode
  4. quit - 退出程序

Launcher程序 - 沙箱

Launcher是一个C编写的程序,主要功能:

  • 设置seccomp过滤器,只允许以下系统调用:
    • read
    • write
    • recvmsg
    • munmap
  • 通过mmap创建hollow_and_jump_buffer函数跳转到上传的shellcode

通信流程

+-launcher
| |
| raw
| |
| sfm
input | sock_client
output | sock_server

漏洞分析

1. Attest功能信息泄露

attest_quote函数中,存在算法ID验证不严格的问题:

let alg = cmd.alg_id;
if alg > SfmHashAlgorithm::HashAlgMax as u16 {
    return Err(SfmError::InvalidAlgorithmType);
}

当alg_id=4时,会泄露libcrypto.so.3的地址。

2. OwnershipRecord修改导致的栈溢出

关键点在于from_utf8_lossy函数对非UTF-8字符串的处理:

impl From<OwnershipRecordRaw> for OwnershipRecord {
    fn from(item: OwnershipRecordRaw) -> Self {
        Self {
            country_code: String::from_utf8_lossy(&item.country_code[..]).to_string(),
            owner_name: String::from_utf8_lossy(&item.owner_name[..]).to_string(),
            // ...
        }
    }
}

当输入非UTF-8字符时,from_utf8_lossy会添加额外的替换字符(FF FD),导致内存扩展,最终在certify_ownership_record函数中造成栈溢出。

利用步骤

1. 认证绕过

通过压缩原始固件并在运行时解压,绕过PCR校验:

  1. 将原始trusted_firmware.raw使用LZ4算法压缩
  2. 在自定义固件中包含压缩后的数据
  3. 运行时解压并分块更新bank

2. 信息泄露

使用alg_id=4调用attest功能,泄露libcrypto.so.3的地址和堆地址。

3. 堆布局构造

  1. 创建NvStorage对象作为ROP链存储
  2. 计算ROP链地址:exp_rop_addr = leak_heap - 0x11620 + 0x18610

4. 栈溢出利用

构造恶意OwnershipRecord:

  • 填充owner_name为51个0xff和1个'B'
  • device_name设置为pop_rsp_ret gadget地址
  • serial_number设置为ROP链地址

5. ROP链构造

使用ropper工具生成execve ROP链:

ropper -f ./libcrypto.so.3 --chain execve

关键gadget:

  • pop_rsp_ret
  • pop_rax_ret
  • pop_rcx_ret
  • mov_rcx_rax_ret
  • pop_rdi_ret
  • pop_rsi_ret
  • pop_rdx_ret
  • syscall

完整利用代码

#include <stddef.h>
#include <stdint.h>
#include <sys/socket.h>

// 系统调用定义
#define __NR_write 1
#define __NR_read 0
#define __NR_recvmsg 47
#define __NR_exit 60

// 结构体定义
typedef struct __attribute__((__packed__)) {
    unsigned int _reservered;
    unsigned short command_code;
    unsigned short pad_;
} SfmCommand;

// ... 其他结构体定义 ...

unsigned char blob[] __attribute__((section(".text")));

int _start(void) {
    int fd = 3;
    // 1. 握手
    // 2. 解压并更新bank
    // 3. 泄露地址
    // 4. 创建NvStorage存放ROP链
    // 5. 触发栈溢出
    // 6. 执行ROP链
    return 0;
}

调试技巧

  1. 使用socketpair创建通信管道:
import os
import socket
import subprocess

sock1, sock2 = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
os.set_inheritable(sock1.fileno(), True)
os.set_inheritable(sock2.fileno(), True)
os.environ['SFM_FD'] = str(sock2.fileno())
os.environ['FIRMWARE_FD'] = str(sock1.fileno())
subprocess.call(['bash', '-i'], env=os.environ, pass_fds=(sock1.fileno(), sock2.fileno()))
  1. 后台启动sfm:
./sfm &
  1. 使用patchelf修改二进制库路径

总结

本题展示了在Rust程序中由于与不安全C代码交互导致的漏洞,以及如何绕过TPM-like的安全验证机制。关键点包括:

  1. 利用UTF-8字符串处理特性造成内存扩展
  2. 通过压缩固件绕过完整性校验
  3. 在严格沙箱限制下实现代码执行
  4. Rust与C交互时的安全隐患

这种类型的漏洞在现实世界的安全模块中同样值得警惕,特别是在处理跨语言边界的数据转换时。

Defcon 2023 rest-and-attest Rust Pwn 题目分析与利用 题目概述 这是一个Rust编写的Pwn题目,模拟了TPM(Trusted Platform Module)安全模块的功能。题目包含多个组件,主要漏洞存在于SFM(Secure Firmware Module)模块中,通过精心构造的输入可以实现栈溢出攻击。 文件结构 程序流程分析 主程序入口 - Uploader Uploader程序提供四个主要功能: upload - 上传一段shellcode二进制程序 download - 下载现有的shellcode二进制 run - 使用launcher运行对应的shellcode quit - 退出程序 Launcher程序 - 沙箱 Launcher是一个C编写的程序,主要功能: 设置seccomp过滤器,只允许以下系统调用: read write recvmsg munmap 通过mmap创建hollow_ and_ jump_ buffer函数跳转到上传的shellcode 通信流程 漏洞分析 1. Attest功能信息泄露 在 attest_quote 函数中,存在算法ID验证不严格的问题: 当alg_ id=4时,会泄露libcrypto.so.3的地址。 2. OwnershipRecord修改导致的栈溢出 关键点在于 from_utf8_lossy 函数对非UTF-8字符串的处理: 当输入非UTF-8字符时, from_utf8_lossy 会添加额外的替换字符(FF FD),导致内存扩展,最终在 certify_ownership_record 函数中造成栈溢出。 利用步骤 1. 认证绕过 通过压缩原始固件并在运行时解压,绕过PCR校验: 将原始 trusted_firmware.raw 使用LZ4算法压缩 在自定义固件中包含压缩后的数据 运行时解压并分块更新bank 2. 信息泄露 使用alg_ id=4调用attest功能,泄露libcrypto.so.3的地址和堆地址。 3. 堆布局构造 创建NvStorage对象作为ROP链存储 计算ROP链地址: exp_rop_addr = leak_heap - 0x11620 + 0x18610 4. 栈溢出利用 构造恶意OwnershipRecord: 填充owner_ name为51个0xff和1个'B' device_ name设置为pop_ rsp_ ret gadget地址 serial_ number设置为ROP链地址 5. ROP链构造 使用ropper工具生成execve ROP链: 关键gadget: pop_ rsp_ ret pop_ rax_ ret pop_ rcx_ ret mov_ rcx_ rax_ ret pop_ rdi_ ret pop_ rsi_ ret pop_ rdx_ ret syscall 完整利用代码 调试技巧 使用socketpair创建通信管道: 后台启动sfm: 使用patchelf修改二进制库路径 总结 本题展示了在Rust程序中由于与不安全C代码交互导致的漏洞,以及如何绕过TPM-like的安全验证机制。关键点包括: 利用UTF-8字符串处理特性造成内存扩展 通过压缩固件绕过完整性校验 在严格沙箱限制下实现代码执行 Rust与C交互时的安全隐患 这种类型的漏洞在现实世界的安全模块中同样值得警惕,特别是在处理跨语言边界的数据转换时。