内核漏洞挖掘技术系列(4)——syzkaller(4)
字数 2376 2025-08-05 08:18:36

Syzkaller 内核漏洞挖掘技术详解

1. Syzkaller 概述

Syzkaller 是一个开源的、无监督的、覆盖引导的内核模糊测试工具,主要用于发现操作系统内核中的漏洞。它通过系统调用序列来测试内核,能够自动生成、变异和执行测试用例,并监控内核的运行状态以发现异常。

2. Syzkaller 工作流程

2.1 初始设置

  1. 启动命令

    /syz-fuzzer -executor=/syz-executor -name=vm-0 -arch=amd64 -manager=10.0.2.10:33185 -procs=1 -leak=false -cover=true -sandbox=none -debug=true -v=100
    
  2. 监控机制

    • MonitorExecution 函数监控输出中的内核 oops 信息、连接丢失、挂起等情况

2.2 初始化阶段

  1. 加载语料库

    • 通过 RPC 调用 Check 函数
    • loadCorpus 函数将数据库中的语料加载到 mgr.candidates
  2. 轮询机制

    • pollLooppoll → RPC 调用 Poll 函数
    • candidateBatch 函数获取 mgr.candidates 并加入 fuzzerworkQueue
  3. 优先级计算

    • CalculatePriorities 函数计算系统调用间的优先级
    • BuildChoiceTable 函数基于优先级构建选择表

3. 优先级计算机制

3.1 静态优先级 (calcStaticPriorities)

  1. 资源使用分析

    • 创建哈希表记录每种资源的使用情况
    • 资源类型包括:Vma、Ptr、Buffer 等
    • 权重分配:
      • Vma: 0.5
      • Ptr: 1.0
  2. 计算规则

    • 对于同一种资源,将系统调用 A 的权重 × 系统调用 B 的权重
    • 加到系统调用 A 和 B 的优先级中
    • 当 A = B 时,prios 设置为 A 与其他所有系统调用 prios 的最大值
    • 最后调用 normalizePrio 规范化

3.2 动态优先级 (calcDynamicPrio)

  1. 计算规则
    • 如果语料库中一对系统调用经常一起出现,则 prios 加 1
    • 同样调用 normalizePrio 规范化

3.3 选择表构建 (BuildChoiceTable)

  1. 构建规则

    • 对于系统调用 i 和 j:
      • run[i][j] = 之前 run[i][x] (x < j) 的和 + prios[i][j] × 1000
    • 结果是一个按权重排序的表
  2. 选择函数 (Choose)

    • 根据 run 表随机选择系统调用
    • 表仅提供有限权重指导

4. Fuzz 循环 (loop 函数)

4.1 工作队列 (WorkQueue)

包含三类对象:

  1. WorkTriage - 可能有新覆盖率的待确认程序
  2. WorkSmash - 新添加到语料库的程序
  3. WorkCandidate - 来自 hub 的待评估程序

4.2 处理函数

  1. triageInput 函数

    • 确认程序是否提供新覆盖率
    • 如果是,则最小化并添加到语料库
  2. smashInput 函数

    • 如果启用 comparisonTracingEnabled,调用 executeHintSeed
    • 使用 hint 机制优化变异
  3. execute 函数

    • 处理 WorkCandidate 类型的程序
    • 与本地生成的程序处理方式相同

5. Hint 机制详解

5.1 基本原理

  1. Hint

    • 由指向程序系统调用参数的指针和应赋给该参数的值组成
    • 称为 "replacer"
  2. 工作流程

    1. 启动程序 (hint seed)
    2. 为每个系统调用收集所有比较数据
    3. 尝试匹配比较操作数值和输入参数值
    4. 用匹配值替换指定参数
    5. 检查新程序是否获得新覆盖率
  3. 数据收集

    • 使用 kcov 的 KCOV_MODE_TRACE_CMP 模式

5.2 关键函数

  1. executeHintSeed

    • 先执行原始程序
    • MutateWithHints 中用比较操作数匹配变异初始程序
    • 检查每个变异是否提供新覆盖率
  2. generateHints

    • 调用 checkConstArgcheckDataArg
    • 使用 shrinkExpand 得到 replacer 替换原值

5.3 示例分析

  1. 示例 1

    void f(u64 qw) {
        u8 b = (u8) qw;
        u16 w = (u16) qw;
        u32 dw = (u32) qw;
        if (b == 0xab) {...}
        if (w == 0xcdcd) {...}
        if (dw == 0xefefefef) {...}
        if (qw == 0x0101010101010101) {...}
    };
    f(0x1234567890abcdef);
    

    CompMap:

    CompMap {
        0xef: uint64Set{0xab: true},
        0xcdef: uint64Set{0xcdcd: true},
        0x90abcdef: uint64Set{0xefefefef: true},
        0x1234567890abcdef: uint64Set{0x0101010101010101: true},
    }
    

    结果:

    uint64Set{
        0x1234567890abcdab: true,
        0x1234567890abcdcd: true,
        0x12345678efefefef: true,
        0x0101010101010101: true,
    }
    
  2. 示例 2

    void f(i32 dw) {
        i64 qw = (i32) dw;
        if (qw == -2)
    };
    f(-1);
    

    CompMap:

    CompMap{0xffffffffffffffff: uint64Set{0xfffffffffffffffe: true}}
    

    结果:

    uint64Set{0xfffffffe: true}
    

6. Executor 执行流程

  1. 调用链

    proc.execute → proc.executeRaw → proc.env.Exec → env.cmd.exec
    
  2. 执行前准备

    • makeCommand 设置 pipe 通信
    • 通过 osutil.Command 运行 executor
  3. Executor 主流程 (executor.cc):

    • 重新映射输入/输出
    • 根据 sandbox 设置调用相应函数
    • 执行系统调用并收集覆盖率信息
  4. 执行链

    loop → execute_one → schedule_call → thread_create → 
    thread_start → worker_thread → execute_call → execute_syscall
    

7. 改进方向 - MoonShine

7.1 基本原理

  1. Distillation (蒸馏) 算法

    • 分析真实世界程序的 system-call 依赖关系
    • "蒸馏"出种子程序给 syzkaller
  2. 优势

    • 发现更多内核漏洞
    • 实际应用中发现了 17 个新的 Linux 内核漏洞

7.2 资源

  1. 源代码
    https://github.com/shankarapailoor/moonshine

  2. 论文
    https://www.usenix.org/conference/usenixsecurity18/presentation/pailoor

  3. 解读
    https://zhuanlan.zhihu.com/p/60798641

8. 关键总结

  1. 优先级计算

    • 结合静态资源分析和动态共现频率
    • 指导系统调用的选择组合
  2. Hint 机制

    • 利用比较操作数指导变异
    • 有效提高路径覆盖率
  3. 执行流程

    • 多级调用确保安全执行
    • 完善的监控机制捕获异常
  4. 改进空间

    • 种子选择算法优化
    • 真实程序行为模拟
Syzkaller 内核漏洞挖掘技术详解 1. Syzkaller 概述 Syzkaller 是一个开源的、无监督的、覆盖引导的内核模糊测试工具,主要用于发现操作系统内核中的漏洞。它通过系统调用序列来测试内核,能够自动生成、变异和执行测试用例,并监控内核的运行状态以发现异常。 2. Syzkaller 工作流程 2.1 初始设置 启动命令 : 监控机制 : MonitorExecution 函数监控输出中的内核 oops 信息、连接丢失、挂起等情况 2.2 初始化阶段 加载语料库 : 通过 RPC 调用 Check 函数 loadCorpus 函数将数据库中的语料加载到 mgr.candidates 中 轮询机制 : pollLoop → poll → RPC 调用 Poll 函数 candidateBatch 函数获取 mgr.candidates 并加入 fuzzer 的 workQueue 优先级计算 : CalculatePriorities 函数计算系统调用间的优先级 BuildChoiceTable 函数基于优先级构建选择表 3. 优先级计算机制 3.1 静态优先级 ( calcStaticPriorities ) 资源使用分析 : 创建哈希表记录每种资源的使用情况 资源类型包括:Vma、Ptr、Buffer 等 权重分配: Vma: 0.5 Ptr: 1.0 计算规则 : 对于同一种资源,将系统调用 A 的权重 × 系统调用 B 的权重 加到系统调用 A 和 B 的优先级中 当 A = B 时,prios 设置为 A 与其他所有系统调用 prios 的最大值 最后调用 normalizePrio 规范化 3.2 动态优先级 ( calcDynamicPrio ) 计算规则 : 如果语料库中一对系统调用经常一起出现,则 prios 加 1 同样调用 normalizePrio 规范化 3.3 选择表构建 ( BuildChoiceTable ) 构建规则 : 对于系统调用 i 和 j: run[i][j] = 之前 run[i][x] (x < j) 的和 + prios[i][j] × 1000 结果是一个按权重排序的表 选择函数 ( Choose ) : 根据 run 表随机选择系统调用 表仅提供有限权重指导 4. Fuzz 循环 ( loop 函数) 4.1 工作队列 ( WorkQueue ) 包含三类对象: WorkTriage - 可能有新覆盖率的待确认程序 WorkSmash - 新添加到语料库的程序 WorkCandidate - 来自 hub 的待评估程序 4.2 处理函数 triageInput 函数 : 确认程序是否提供新覆盖率 如果是,则最小化并添加到语料库 smashInput 函数 : 如果启用 comparisonTracingEnabled ,调用 executeHintSeed 使用 hint 机制优化变异 execute 函数 : 处理 WorkCandidate 类型的程序 与本地生成的程序处理方式相同 5. Hint 机制详解 5.1 基本原理 Hint : 由指向程序系统调用参数的指针和应赋给该参数的值组成 称为 "replacer" 工作流程 : 启动程序 (hint seed) 为每个系统调用收集所有比较数据 尝试匹配比较操作数值和输入参数值 用匹配值替换指定参数 检查新程序是否获得新覆盖率 数据收集 : 使用 kcov 的 KCOV_MODE_TRACE_CMP 模式 5.2 关键函数 executeHintSeed : 先执行原始程序 在 MutateWithHints 中用比较操作数匹配变异初始程序 检查每个变异是否提供新覆盖率 generateHints : 调用 checkConstArg 和 checkDataArg 使用 shrinkExpand 得到 replacer 替换原值 5.3 示例分析 示例 1 : CompMap : 结果 : 示例 2 : CompMap : 结果 : 6. Executor 执行流程 调用链 : 执行前准备 : makeCommand 设置 pipe 通信 通过 osutil.Command 运行 executor Executor 主流程 ( executor.cc ): 重新映射输入/输出 根据 sandbox 设置调用相应函数 执行系统调用并收集覆盖率信息 执行链 : 7. 改进方向 - MoonShine 7.1 基本原理 Distillation (蒸馏) 算法 : 分析真实世界程序的 system-call 依赖关系 "蒸馏"出种子程序给 syzkaller 优势 : 发现更多内核漏洞 实际应用中发现了 17 个新的 Linux 内核漏洞 7.2 资源 源代码 : https://github.com/shankarapailoor/moonshine 论文 : https://www.usenix.org/conference/usenixsecurity18/presentation/pailoor 解读 : https://zhuanlan.zhihu.com/p/60798641 8. 关键总结 优先级计算 : 结合静态资源分析和动态共现频率 指导系统调用的选择组合 Hint 机制 : 利用比较操作数指导变异 有效提高路径覆盖率 执行流程 : 多级调用确保安全执行 完善的监控机制捕获异常 改进空间 : 种子选择算法优化 真实程序行为模拟