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/416 | ASan (AddressSanitizer) | SIGABRT + ASan report 含完整调用栈和分配追踪 | ✓ 已实现 |
| 未初始化内存读取 CWE-457/908 | MSan (MemorySanitizer) | use-of-uninitialized-value 精确到首次读取位置 | ✓ 已实现 |
| 未定义行为 / 整数溢出 CWE-190/191 | UBSan (UndefinedBehaviorSanitizer) | runtime error: signed integer overflow 或 shift exponent too large | ✓ 已实现 |
| 竞态条件 / TOCTOU CWE-362/367 | TSan + 并发竞争 Harness | TSan: data race detected 或竞争窗口内 symlink 替换成功 | ◐ 规划中 |
| 权限 / 认证缺失 CWE-862/306 | 断言式 PoC (Assertion Oracle) | 受保护操作在无权限上下文中 成功执行 → [VULN] marker | ◐ 规划中 |
| 路径遍历 CWE-22 | 文件系统沙箱 Oracle | 沙箱外文件被成功读写 (../../ 逃逸检测) | ✓ 已实现 |
仅静态分析
Oracle 驱动的 PoC 验证
1.5 验证策略详解
A. Sanitizer 家族 — 内存与未定义行为(已实现)
编译时插桩,运行时检测。Sanitizer 在内存分配周围插入 red zone,在释放后标记为 poisoned,在未初始化区域标记为 uninitialized。任何违规访问立即触发报告并终止。
B. 竞态条件 / TOCTOU 验证(规划中)
TOCTOU 漏洞的 check 和 use 之间存在时间窗口。验证策略是在该窗口内并发修改目标资源,证明竞争可被利用。
备选方案: TSan 编译 + 多线程 harness,检测 data race 报告。
C. 权限 / 认证缺失验证(规划中)
逻辑漏洞不会触发 crash。验证策略是构造不满足前置条件的调用上下文,证明受保护操作仍然成功执行。参考 POC-GYM (arXiv:2602.04165) 的 assertion-based oracle 方法。
D. 路径遍历验证(已验证)
构造包含 ../ 序列的路径输入,在受限沙箱环境中调用目标函数,检测是否成功逃逸到沙箱外。
已通过 OH-2026-FS-001(customization_config_policy GetOneCfgFile)验证。PoC 使用 ../../etc/passwd 序列成功探测沙箱外文件存在性。
2. 漏洞类型速查
| 漏洞类型 | CWE | 技术描述 |
|---|---|---|
| 堆越界读取 | CWE-125Out-of-bounds Read | 程序读取超出堆缓冲区分配范围的内存,可泄露相邻堆块中的敏感数据 |
| 堆越界写入 | CWE-787Out-of-bounds Write | 程序写入超出堆缓冲区分配范围的内存,可覆盖堆元数据或相邻对象 |
| 整数溢出 | CWE-190Integer Overflow or Wraparound | 算术运算结果超出数据类型表示范围,导致分配大小计算错误 |
| Null 终止符缺失 | CWE-170Improper Null Termination | 字符串缺少 \0 结束标记,后续字符串操作越界读取直到遇到随机 \0 |
| 空指针解引用 | CWE-476NULL Pointer Dereference | 通过空指针访问内存,内核映射 NULL 页时可能被利用 |
| 释放后使用 | CWE-416Use After Free | 访问已释放的堆内存,若该区域被重新分配给其他对象则可控制其内容 |
| 未初始化内存使用 | CWE-908Use of Uninitialized Resource | 通过 malloc 分配但未初始化的内存被后续代码读取,堆上残留数据被泄露给调用方 |
| 路径遍历 | CWE-22Path Traversal | 路径参数未过滤 ../ 序列,攻击者可拼接路径访问受限目录外的文件 |
| 数组索引越界 | CWE-129Improper Validation of Array Index | 数组索引未校验范围,外部输入可控的索引值导致越界数组访问 |
3. 什么是 Target-Compile(目标编译)?
Target-Compile 是 Fermat 的核心验证方法。我们不提取代码片段或模拟头文件——而是将目标项目的真实生产源码通过其自身的构建系统(GN + Ninja)编译为静态库(.a),然后链接测试驱动程序构造恶意输入,触发漏洞。
#include 模拟(我们不这样做)
- - 提取漏洞函数到独立文件
- - 模拟或简化依赖关系
- - 编译环境与真实环境不同
- - 可能引入或消除真实 bug
Target-Compile(我们的方法)
- - 编译目标项目的真实源码
- - 仅提供最小桩代码解决链接问题
- - 使用目标项目自身的构建系统
- - PoC 触发的是真正的代码路径
自动化 PoC 生成管线
Fermat 的 L4 验证层将 PoC 生成从手动编写升级为全自动管线:从漏洞函数自动追溯入口点、生成攻击场景计划、编写 PoC 代码、编译验证、生成补丁并验证补丁有效性。
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 模块强制使用 |
| 2 | chain_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 + UBSanAddressSanitizer 检测内存越界,UndefinedBehaviorSanitizer 检测未定义行为
pysetup_build.py一键生成完整构建根目录的脚本,自动链接框架头文件和第三方库
C 语言桩代码
ohos_stubs.c 提供了 OHOS 系统框架的 C API 实现。桩代码的作用是在编译时替代缺失的系统服务,使目标模块能够编译和链接。
IPC 通信(进程间通信)
OHOS 使用 IpcIo 进行进程间通信的数据序列化。我们实现了完整的 IpcIo 读写 API,测试驱动可以配置 IPC 调用的返回值。
系统服务管理(SamgrLite)
SamgrLite 是 OHOS 的服务管理框架,包含 13 个函数指针。我们实现了完整的 vtable,包括服务注册、发现和功能 API 管理。
日志系统
C++ 框架桩代码
ohos_cpp_stubs.h 提供了 OHOS C++ 框架核心类的实现,位于 OHOS 命名空间下。
| 类 | 功能 |
|---|---|
RefBase | 引用计数基类,提供 IncRef/DecRef 自动生命周期管理 |
sptr<T> | 智能指针模板,类似 Android sp<>,自动管理引用计数 |
Parcel | IPC 数据序列化缓冲区,支持基本类型和字符串的读写 |
MessageParcel | 扩展 Parcel,支持 InterfaceToken 写入 |
IRemoteObject | IPC 远程对象接口,定义 SendRequest 虚方法 |
IPCSkeleton | IPC 框架入口,提供 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 立即报告错误并终止程序。这意味着越界读写不会"静默通过"——每次违规都会被捕获。
| 检测类型 | 含义 | 对应 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 运行时——结果是程序正常运行, 越界读写静默通过,不报任何错误。这给人一种"一切正常"的假象。
6. PoC 构建与验证管线
| 阶段 | 工具 | 输入 | 输出 |
|---|---|---|---|
| 入口解析 | PoC Path Resolver | 漏洞函数 + 源码 | Public API 入口 + IPC 格式 |
| 场景规划 | Scenario Planner | 入口 + CWE + 触发条件 | 结构化攻击计划 |
| 源码准备 | setup_build.py | OHOS 模块源码 | GN 构建根目录 + 桩代码 |
| 构建生成 | gn gen | BUILD.gn 文件 | ninja 构建文件 |
| 编译 | ninja (gcc/g++) | .c/.cpp 源文件 | .o 目标文件 |
| 打包 | ar rcs | .o 目标文件 | .a 静态库 |
| 链接 | Build Agent | PoC.o + .a + Sanitizer | 可执行文件 |
| 验证 | Oracle (ASan/MSan/Assertion) | 构造的恶意输入 | 漏洞确认报告 |
| 补丁 | Patch Generator + Validator | 漏洞点 + PoC | 修复补丁 + 有效性验证 |
7. 已验证的 PoC 案例
| Issue | 漏洞类型 |
|---|---|
| OH-2026-IPC-007 | CWE-908Use of Uninitialized Resource |
| OH-2026-FS-001 | CWE-22Path Traversal |
| OH-2026-PERMLITE-005 | CWE-190Integer Overflow or Wraparound |
| OH-2026-IPC-005 | CWE-190Integer Overflow or Wraparound |
| OH-2026-IPC-006 | CWE-129Improper Validation of Array Index |
| OH-2026-PERMLITE-001 | CWE-129Improper Validation of Array Index |
8. OpenHarmony Target-Compile 技术挑战
在 x86_64 Linux 上编译和运行 OHOS 模块面临一系列独特的技术挑战。以下是我们遇到并解决的关键问题:
SamgrLite 结构体 ABI 精确匹配
SamgrLite 结构体有 13 个函数指针,必须与 samgr_lite.h 精确匹配。一个指针偏移错误就会导致调用跳转到错误地址。这意味着桩代码中的结构体布局必须和 OHOS 头文件逐字节对齐。
IpcIo bufferCur 重置时序
IpcIo mock 在写入数据后、notify 回调读取前,必须将 bufferCur 重置到 bufferBase,否则读取位置错误。这是一个微妙的时序问题——写入指针不重置,后续读取会从错误位置开始。
HalMalloc null 终止符空间
ReadString 不分配 null 终止符空间。HalMalloc 必须使用 calloc(1, size+1)——比请求的大小多分配 1 字节。如果不做这一步,字符串操作(strlen、printf)会越界读取。
ParsePermissions JSON flags 字段
服务端 ParsePermissions 要求 JSON 中必须有 'flags' 字段,ParseNewPermissionsItem 在没有 flags 时返回错误。这意味着测试数据必须包含完整的权限结构。
HiLogPrint 枚举类型签名
HiLogPrint 使用 LogType/LogLevel 枚举而非 int,桩代码签名必须使用正确的枚举类型。使用 int 会导致编译器在 strict 模式下报错。
GetCallingUid 返回类型
GetCallingUid 返回 pid_t 而非 uid_t。在 OHOS 中两者可能不同,混淆会导致类型不匹配的编译错误。
GN ASan cflags 不自动传播
GN 的 set_defaults() 不会将 cflags(包括 -fsanitize=address)传播到编译目标。必须在每个 target 的 configs 中显式声明。这是 RtpPacket PoC 最初无法触发 ASan 的根本原因——代码编译时没有 ASan 插桩,但链接了 ASan 运行时,造成越界访问静默通过。
模块特定编译依赖解析
不同 OHOS 模块使用不同的构建配置和第三方依赖。某些模块的 BUILD.gn 有 3-4 层嵌套引用,Build Agent 需要自动解析依赖树、查找缺失头文件并生成对应桩代码。setup_build.py 自动化了大部分流程,但极端情况仍需手动调整。
C++ NAPI 桩代码边界
涉及 NAPI(JS/Native 桥接层)的模块需要 napi.h 和 node_api.h 的桩实现,包括 napi_value、napi_env 等不透明类型。当前 Target-Compile 仅覆盖纯 C/C++ 模块,NAPI 类型需要独立的桩系统才能支持 JS 桥接模块的编译。
-Dstatic= 多重定义冲突
-Dstatic= 将 static 函数暴露为全局符号,当多个 .o 文件定义了同名 static 函数时会导致链接器报 multiple definition 错误。解决方案是对 OHOS target-compile 自动传递 -Wl,--allow-multiple-definition 链接器标志。
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 | 影响 | 漏洞的影响范围和潜在危害 |
| 5 | PoC 验证 | 验证方法、编译环境、编译产物列表 |
| 6 | ASan 输出 | ASan/UBSan 的完整报告,包含调用栈和分配追踪 |
| 7 | 修复建议 | 漏洞的修复代码(unified diff 格式) |
| 8 | PoC 类型声明 | PoC 生成模式(target_compile / chain_aware / standalone)、验证 Oracle 类型、是否可在真实设备触发、攻击前提 |
ASan 输出示例与解读
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 等) |