PoC Verification

Fermat Review Board 上的每个漏洞都经过 PoC(Proof of Concept)验证。 本页介绍我们的验证方法、OpenHarmony 编译基础设施、以及如何阅读漏洞报告。

1. PoC 验证框架

PoC(Proof of Concept)的本质不是"让程序崩溃",而是证明安全属性被违反。不同漏洞类型需要不同的验证 Oracle(判定准则)。

Fermat 针对每种漏洞类型设计了对应的验证策略。静态分析引擎(Joern、Infer、IKOS)发现疑似漏洞后,根据漏洞类型选择对应的 Oracle 生成 PoC,通过运行时信号确认漏洞真实性。

漏洞类型验证 Oracle判定信号状态
内存空间安全 CWE-125/787/120/416ASan (AddressSanitizer)SIGABRT + ASan report 含完整调用栈和分配追踪✓ 已实现
未初始化内存读取 CWE-457/908MSan (MemorySanitizer)use-of-uninitialized-value 精确到首次读取位置✓ 已实现
未定义行为 / 整数溢出 CWE-190/191UBSan (UndefinedBehaviorSanitizer)runtime error: signed integer overflow 或 shift exponent too large✓ 已实现
竞态条件 / TOCTOU CWE-362/367TSan + 并发竞争 HarnessTSan: data race detected 或竞争窗口内 symlink 替换成功◐ 规划中
权限 / 认证缺失 CWE-862/306断言式 PoC (Assertion Oracle)受保护操作在无权限上下文中 成功执行 → [VULN] marker◐ 规划中
路径遍历 CWE-22文件系统沙箱 Oracle沙箱外文件被成功读写 (../../ 逃逸检测)✓ 已实现

仅静态分析

扫描源码 → 报告疑似漏洞
可能存在,也可能是误报
无法确认实际可利用性

Oracle 驱动的 PoC 验证

编译真实代码 → 构造触发输入 → Oracle 捕获违规
每种漏洞类型有对应的判定准则
运行时信号证明安全属性被违反

1.5 验证策略详解

A. Sanitizer 家族 — 内存与未定义行为(已实现)

编译时插桩,运行时检测。Sanitizer 在内存分配周围插入 red zone,在释放后标记为 poisoned,在未初始化区域标记为 uninitialized。任何违规访问立即触发报告并终止。

# 编译命令
gcc -fsanitize=address,undefined -fno-omit-frame-pointer -g -O0 target.c poc.c -o poc
# 判定: 程序以非零退出码终止 + stderr 包含 Sanitizer 报告
grep 'ERROR: AddressSanitizer\|runtime error:' stderr → 漏洞确认

B. 竞态条件 / TOCTOU 验证(规划中)

TOCTOU 漏洞的 check 和 use 之间存在时间窗口。验证策略是在该窗口内并发修改目标资源,证明竞争可被利用。

// TOCTOU PoC 结构 (CWE-367)
Thread A: while(1) { symlink("/tmp/target", "/etc/shadow"); unlink("/tmp/target"); }
Thread B: target_function("/tmp/target"); // access() + open()
// Oracle: 检测 open() 实际打开的文件是否为 symlink 目标
readlink(/proc/self/fd/N) != "/tmp/target" → 竞争成功 → 漏洞确认

备选方案: TSan 编译 + 多线程 harness,检测 data race 报告。

C. 权限 / 认证缺失验证(规划中)

逻辑漏洞不会触发 crash。验证策略是构造不满足前置条件的调用上下文,证明受保护操作仍然成功执行。参考 POC-GYM (arXiv:2602.04165) 的 assertion-based oracle 方法。

// Assertion Oracle (CWE-862 权限缺失)
setup_context(uid=UNPRIVILEGED_USER, no_capability=true);
int ret = ProtectedOperation(params); // 本应返回 PERMISSION_DENIED
// Oracle: 操作成功 = 安全属性违反
if (ret == SUCCESS) printf("[VULN] operation succeeded without authorization");
// 对照验证: 修复版本上同样调用应返回错误
// patched: ret == PERMISSION_DENIED → 修复有效

D. 路径遍历验证(已验证)

构造包含 ../ 序列的路径输入,在受限沙箱环境中调用目标函数,检测是否成功逃逸到沙箱外。

// 文件系统沙箱 Oracle (CWE-22)
mkdir("/tmp/sandbox/allowed/");
write_file("/tmp/sandbox/canary.txt", "SHOULD_NOT_READ");
char *result = target_read_file("../../canary.txt"); // 相对于 allowed/
// Oracle: 成功读取沙箱外文件 = 路径遍历确认
if (strcmp(result, "SHOULD_NOT_READ") == 0) printf("[VULN] path traversal");

已通过 OH-2026-FS-001(customization_config_policy GetOneCfgFile)验证。PoC 使用 ../../etc/passwd 序列成功探测沙箱外文件存在性。

2. 漏洞类型速查

漏洞类型CWE技术描述
堆越界读取CWE-125
Out-of-bounds Read
程序读取超出堆缓冲区分配范围的内存,可泄露相邻堆块中的敏感数据
堆越界写入CWE-787
Out-of-bounds Write
程序写入超出堆缓冲区分配范围的内存,可覆盖堆元数据或相邻对象
整数溢出CWE-190
Integer Overflow or Wraparound
算术运算结果超出数据类型表示范围,导致分配大小计算错误
Null 终止符缺失CWE-170
Improper Null Termination
字符串缺少 \0 结束标记,后续字符串操作越界读取直到遇到随机 \0
空指针解引用CWE-476
NULL Pointer Dereference
通过空指针访问内存,内核映射 NULL 页时可能被利用
释放后使用CWE-416
Use After Free
访问已释放的堆内存,若该区域被重新分配给其他对象则可控制其内容
未初始化内存使用CWE-908
Use of Uninitialized Resource
通过 malloc 分配但未初始化的内存被后续代码读取,堆上残留数据被泄露给调用方
路径遍历CWE-22
Path Traversal
路径参数未过滤 ../ 序列,攻击者可拼接路径访问受限目录外的文件
数组索引越界CWE-129
Improper Validation of Array Index
数组索引未校验范围,外部输入可控的索引值导致越界数组访问

3. 什么是 Target-Compile(目标编译)?

Target-Compile 是 Fermat 的核心验证方法。我们不提取代码片段或模拟头文件——而是将目标项目的真实生产源码通过其自身的构建系统(GN + Ninja)编译为静态库(.a),然后链接测试驱动程序构造恶意输入,触发漏洞。

# Target-Compile Pipeline
目标源码(.c/.cpp)
↓ GN 构建系统
静态库(.a)— 真实编译产物
↓ 链接测试驱动(PoC)
可执行文件 + ASan/UBSan
↓ 运行
ASan 报告 → 漏洞确认

#include 模拟(我们不这样做)

  • - 提取漏洞函数到独立文件
  • - 模拟或简化依赖关系
  • - 编译环境与真实环境不同
  • - 可能引入或消除真实 bug

Target-Compile(我们的方法)

  • - 编译目标项目的真实源码
  • - 仅提供最小桩代码解决链接问题
  • - 使用目标项目自身的构建系统
  • - PoC 触发的是真正的代码路径

自动化 PoC 生成管线

Fermat 的 L4 验证层将 PoC 生成从手动编写升级为全自动管线:从漏洞函数自动追溯入口点、生成攻击场景计划、编写 PoC 代码、编译验证、生成补丁并验证补丁有效性。

# L4 PoC 生成管线
漏洞函数
↓ PoC Path Resolver — 自动入口追溯
Public API 入口 + IPC 序列化格式 + CWE 触发值
↓ Scenario Planner — 攻击场景规划
结构化攻击计划(SCENARIO / SETUP / TRIGGER / EXPECTED_SIGNAL)
↓ Code Generator — 按场景写代码
PoC 测试驱动 + Build Agent 自动编译
↓ Oracle 验证 — ASan/MSan/Assertion
漏洞确认 → 补丁生成 → 补丁有效性验证

PoC Path Resolver:两级入口追溯

PoC Path Resolver 从漏洞函数向上 BFS 追溯调用链,自动确定测试驱动应调用的入口函数、IPC 序列化格式(Read* 调用序列 + size-prefix 关系)、以及 CWE 特定的触发值。

Public API Entry(首选)

模块公开头文件中声明的函数(如 interfaces/kits/native/include/*.h)。测试驱动直接调用此函数,真实编译代码处理完整的内部调用链。

Transport Entry(Fallback)

IPC 回调或 handler(如 .func = 注册的函数、OnRemoteRequest)。当无法追溯到 Public API 时,使用 transport entry 配合 IPC 序列化格式构造输入。

优先级模式说明
1(最高)target_compile编译真实源码为 .a 静态库,链接测试驱动。OHOS 模块强制使用
2chain_aware包含完整调用链源码的 PoC,通过入口点触发(非 OHOS)
3(最低)standalone自包含 PoC,仅用于非 OHOS 项目的 fallback

4. OpenHarmony 编译基础设施

OpenHarmony(OHOS)是一个复杂的嵌入式操作系统,其模块依赖大量系统框架 API(IPC 通信、系统服务管理、日志等)。 为了在标准 Linux x86_64 环境下编译和运行 OHOS 模块,我们构建了完整的编译工具链和桩代码(stub)体系。

工具链概览

gnGN 构建系统

模拟 OHOS 构建环境,定义编译目标、依赖关系和编译标志

ninjaNinja 编译器

执行增量编译,调度 gcc/g++ 编译任务

sanASan + UBSan

AddressSanitizer 检测内存越界,UndefinedBehaviorSanitizer 检测未定义行为

pysetup_build.py

一键生成完整构建根目录的脚本,自动链接框架头文件和第三方库

C 语言桩代码

ohos_stubs.c 提供了 OHOS 系统框架的 C API 实现。桩代码的作用是在编译时替代缺失的系统服务,使目标模块能够编译和链接。

IPC 通信(进程间通信)

OHOS 使用 IpcIo 进行进程间通信的数据序列化。我们实现了完整的 IpcIo 读写 API,测试驱动可以配置 IPC 调用的返回值。

// IpcIo 序列化/反序列化
IpcIoInit(io, buffer, bufferSize, maxobjects)
WriteInt32 / WriteInt64 / WriteString
ReadInt32 / ReadString
// IPC 客户端代理 — 测试驱动可配置返回值
IClientProxy.Invoke → 写入 g_mock_result_code + g_mock_response_json
// IPC 异步推送模拟 — 模拟服务端向客户端推送数据
SAMGR_SimulatePush(IpcIo *data) → 调用 WriteRemoteObject 注册的回调

系统服务管理(SamgrLite)

SamgrLite 是 OHOS 的服务管理框架,包含 13 个函数指针。我们实现了完整的 vtable,包括服务注册、发现和功能 API 管理。

// SamgrLite — 13 个函数指针全部实现
RegisterService / UnregisterService
RegisterFeature / UnregisterFeature
RegisterDefaultFeatureApi / GetDefaultFeatureApi
RegisterFeatureApi / GetFeatureApi → 返回模拟 IPC 代理
AddSystemCapability / HasSystemCapability

日志系统

HiLogPrint(type, level, domain, tag, fmt, ...)
→ vfprintf(stderr, ...) // 转发到标准错误输出

C++ 框架桩代码

ohos_cpp_stubs.h 提供了 OHOS C++ 框架核心类的实现,位于 OHOS 命名空间下。

功能
RefBase引用计数基类,提供 IncRef/DecRef 自动生命周期管理
sptr<T>智能指针模板,类似 Android sp<>,自动管理引用计数
ParcelIPC 数据序列化缓冲区,支持基本类型和字符串的读写
MessageParcel扩展 Parcel,支持 InterfaceToken 写入
IRemoteObjectIPC 远程对象接口,定义 SendRequest 虚方法
IPCSkeletonIPC 框架入口,提供 GetCallingPid/Uid 等静态方法
HiLog日志工具,Info/Error 方法输出到 stderr

HAL 桩代码

hal_stubs.c 提供 Hardware Abstraction Layer 和平台 API 的模拟实现,用于服务端/守护进程模块的编译。

函数模拟行为
HalMalloc(size)calloc(1, size+1) — 多分配 1 字节给 null 终止符
HalFree(ptr)标准 free(ptr)
HalGetPermissionPath()返回 /tmp/ohos_perm_test/ — 将文件操作重定向到测试目录
HalAccess(pathname)标准 access(pathname, F_OK)
HalIsValidPath(path)始终返回 true
HalGetMaxPermissionSize()返回 1,000,000
HalGetPermissionList(length)返回一个示例权限条目(ohos.permission.CAMERA)
HalMutexLock/Unlock()pthread_mutex_lock/unlock

5. ASan/UBSan/MSan 检测能力

ASan 工作原理

AddressSanitizer(ASan)是编译器内置的内存错误检测器。编译时,ASan 在每块内存分配周围插入"哨兵"区域(red zone)。 当程序访问这些哨兵区域时,ASan 立即报告错误并终止程序。这意味着越界读写不会"静默通过"——每次违规都会被捕获。

// ASan 内存布局示意
[red zone][ 分配的缓冲区 ][red zone]
↑ 禁止 ↑ 合法访问范围 ↑ 禁止
正常读写 → 在合法范围内 → 程序正常运行
越界读写 → 进入 red zone → ASan 报错并终止
检测类型含义对应 CWE
heap-buffer-overflow堆缓冲区越界读写CWE-125, CWE-787
SEGV (out-of-bounds)越界访问到未映射内存页CWE-129
allocation-size-too-big整数溢出导致异常大分配CWE-190
out-of-memory未校验的外部输入控制分配大小CWE-190
heap-use-after-free释放后使用CWE-416
stack-buffer-overflow栈缓冲区溢出CWE-121
UBSan: signed-integer-overflow有符号整数溢出CWE-190
MSan: use-of-uninitialized-value未初始化内存读取CWE-908

Critical: ASan 配置传播问题

在构建 OHOS GN 项目时,set_defaults() 不会将 cflags 传播到各个编译目标。ASan 标志 (-fsanitize=address,undefined必须在 每个 target 的 configs 列表中显式声明。

如果不做这一步,代码编译时不会插入 ASan 探针,但链接阶段会链接 ASan 运行时——结果是程序正常运行, 越界读写静默通过,不报任何错误。这给人一种"一切正常"的假象。

// Correct: every target needs explicit ASan config
config("asan_config") {
cflags = [ "-fsanitize=address,undefined", ... ]
cflags_cc = [ "-std=c++17", ... ]
}
executable("poc") {
configs = [ ":asan_config" ] // MUST be explicit
}

6. PoC 构建与验证管线

漏洞报告(L3 输出)
↓ PoC Path Resolver — 入口追溯 + IPC 格式提取 + 触发值选择
入口函数 + 序列化格式 + CWE 触发值
↓ Scenario Planner — 结构化攻击场景
SCENARIO / SETUP / TRIGGER / EXPECTED_SIGNAL
↓ setup_build.py — 一键生成构建根目录
GN 构建描述 (BUILD.gn) + 桩代码
↓ gn gen + ninja — gcc/g++ 编译 + ASan/MSan 插桩
静态库 (.a) — 真实编译产物
↓ Build Agent — 链接 PoC 测试驱动(自动修复编译错误)
可执行文件(含 Sanitizer runtime)
↓ Oracle 验证 — 运行 + 捕获信号
漏洞确认 → 补丁生成 → 补丁有效性验证 → 反馈知识库
阶段工具输入输出
入口解析PoC Path Resolver漏洞函数 + 源码Public API 入口 + IPC 格式
场景规划Scenario Planner入口 + CWE + 触发条件结构化攻击计划
源码准备setup_build.pyOHOS 模块源码GN 构建根目录 + 桩代码
构建生成gn genBUILD.gn 文件ninja 构建文件
编译ninja (gcc/g++).c/.cpp 源文件.o 目标文件
打包ar rcs.o 目标文件.a 静态库
链接Build AgentPoC.o + .a + Sanitizer可执行文件
验证Oracle (ASan/MSan/Assertion)构造的恶意输入漏洞确认报告
补丁Patch Generator + Validator漏洞点 + PoC修复补丁 + 有效性验证

7. 已验证的 PoC 案例

6
已验证 PoC
3
覆盖仓库
4
CWE 类型
Target-Compile
验证方法
Issue漏洞类型
OH-2026-IPC-007CWE-908Use of Uninitialized Resource
OH-2026-FS-001CWE-22Path Traversal
OH-2026-PERMLITE-005CWE-190Integer Overflow or Wraparound
OH-2026-IPC-005CWE-190Integer Overflow or Wraparound
OH-2026-IPC-006CWE-129Improper Validation of Array Index
OH-2026-PERMLITE-001CWE-129Improper Validation of Array Index

8. OpenHarmony Target-Compile 技术挑战

在 x86_64 Linux 上编译和运行 OHOS 模块面临一系列独特的技术挑战。以下是我们遇到并解决的关键问题:

1

SamgrLite 结构体 ABI 精确匹配

SamgrLite 结构体有 13 个函数指针,必须与 samgr_lite.h 精确匹配。一个指针偏移错误就会导致调用跳转到错误地址。这意味着桩代码中的结构体布局必须和 OHOS 头文件逐字节对齐。

2

IpcIo bufferCur 重置时序

IpcIo mock 在写入数据后、notify 回调读取前,必须将 bufferCur 重置到 bufferBase,否则读取位置错误。这是一个微妙的时序问题——写入指针不重置,后续读取会从错误位置开始。

3

HalMalloc null 终止符空间

ReadString 不分配 null 终止符空间。HalMalloc 必须使用 calloc(1, size+1)——比请求的大小多分配 1 字节。如果不做这一步,字符串操作(strlen、printf)会越界读取。

4

ParsePermissions JSON flags 字段

服务端 ParsePermissions 要求 JSON 中必须有 'flags' 字段,ParseNewPermissionsItem 在没有 flags 时返回错误。这意味着测试数据必须包含完整的权限结构。

5

HiLogPrint 枚举类型签名

HiLogPrint 使用 LogType/LogLevel 枚举而非 int,桩代码签名必须使用正确的枚举类型。使用 int 会导致编译器在 strict 模式下报错。

6

GetCallingUid 返回类型

GetCallingUid 返回 pid_t 而非 uid_t。在 OHOS 中两者可能不同,混淆会导致类型不匹配的编译错误。

7

GN ASan cflags 不自动传播

GN 的 set_defaults() 不会将 cflags(包括 -fsanitize=address)传播到编译目标。必须在每个 target 的 configs 中显式声明。这是 RtpPacket PoC 最初无法触发 ASan 的根本原因——代码编译时没有 ASan 插桩,但链接了 ASan 运行时,造成越界访问静默通过。

8

模块特定编译依赖解析

不同 OHOS 模块使用不同的构建配置和第三方依赖。某些模块的 BUILD.gn 有 3-4 层嵌套引用,Build Agent 需要自动解析依赖树、查找缺失头文件并生成对应桩代码。setup_build.py 自动化了大部分流程,但极端情况仍需手动调整。

9

C++ NAPI 桩代码边界

涉及 NAPI(JS/Native 桥接层)的模块需要 napi.h 和 node_api.h 的桩实现,包括 napi_value、napi_env 等不透明类型。当前 Target-Compile 仅覆盖纯 C/C++ 模块,NAPI 类型需要独立的桩系统才能支持 JS 桥接模块的编译。

10

-Dstatic= 多重定义冲突

-Dstatic= 将 static 函数暴露为全局符号,当多个 .o 文件定义了同名 static 函数时会导致链接器报 multiple definition 错误。解决方案是对 OHOS target-compile 自动传递 -Wl,--allow-multiple-definition 链接器标志。

11

securec static inline 失效

-Dstatic= 同时影响 securec.h 中的 static inline 函数(memset_s、strcpy_s 等),使其变为 inline 但丢失外部定义(C99 语义)。需要独立的 securec_stubs.c 在不使用 -Dstatic= 的情况下编译,提供外部链接定义。

9. 如何阅读 PoC 报告

Fermat Review Board 上的每个漏洞报告包含以下结构化信息:

#章节内容说明
1漏洞概述漏洞的简要描述、触发条件和影响范围
2问题代码精确的代码位置和有缺陷的代码片段(含行号)
3触发条件触发漏洞的步骤序列和攻击前提
4影响漏洞的影响范围和潜在危害
5PoC 验证验证方法、编译环境、编译产物列表
6ASan 输出ASan/UBSan 的完整报告,包含调用栈和分配追踪
7修复建议漏洞的修复代码(unified diff 格式)
8PoC 类型声明PoC 生成模式(target_compile / chain_aware / standalone)、验证 Oracle 类型、是否可在真实设备触发、攻击前提

ASan 输出示例与解读

ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50200000003c
READ of size 1 at 0x50200000003c thread T0
#0 in OHOS::Sharing::RtcpHeader::GetPaddingSize() const rtcp.cpp:45
#1 in main rtp_poc.cpp:83
0x50200000003c is located 399 bytes after 4-byte region [0x502000000010,0x502000000014)
allocated by thread T0 here:
#0 in operator new[](unsigned long)
#1 in main rtp_poc.cpp:69

ERROR 行:检测到的错误类型(heap-buffer-overflow)和出错地址

READ/WRITE 行:操作类型(读/写)、操作大小、所在线程

定位行:出错地址与分配区域的关系。"399 bytes after 4-byte region" 表示程序在一个 4 字节缓冲区之后 399 字节的位置进行了读取

分配追踪:显示缓冲区的分配来源和调用栈,证明 OOB 读取的缓冲区来自目标代码

10. 当前验证能力与局限

已验证能力

  • 纯 C 模块(permission_lite, config_policy, sensors_sensor_lite)
  • C++ 模块(castengine_wifi_display: DataBuffer, RtpPacket)
  • 路径遍历验证(CWE-22,文件系统沙箱 Oracle)
  • 未初始化内存检测(CWE-908,MSan Oracle)
  • 整数溢出→堆破坏链(CWE-190 → allocation-size-too-big)
  • ASan/UBSan/MSan 多 Sanitizer 检测
  • 自动入口追溯(PoC Path Resolver)+ 场景级 PoC 生成
  • 补丁生成与有效性验证反馈闭环
  • 客户端 + 服务端 + IPC 模块三向支持

当前局限

  • 仅 Linux x86_64(非 ARM 交叉编译)
  • NAPI/JS 桥接层模块暂不支持
  • 需要 OHOS 图形/音频/Ability 框架的模块暂不支持
  • TSan 竞态条件验证(CWE-362/367)仍为规划中
  • 权限/认证 Assertion Oracle(CWE-862/306)仍为规划中

11. 编译环境

操作系统Ubuntu 24.04 LTS, Linux 6.17, x86_64
构建系统GN + Ninja (OHOS 构建工具链)
C 编译器gcc 13.x + ASan + UBSan
C++ 编译器g++ 13.x + ASan + UBSan
C++ 标准C++17 (-std=c++17)
RTTI禁用 (-fno-rtti)
优化级别-O0 (无优化,确保 ASan 插桩完整)
安全库bounds_checking_function (memcpy_s, strcpy_s 等)