内核漏洞挖掘技术系列(6)——使用AFL进行内核漏洞挖掘(2)
字数 1317 2025-08-03 16:44:51

使用AFL进行Linux内核漏洞挖掘技术详解

1. 概述

本教程详细介绍了如何使用AFL (American Fuzzy Lop) fuzzer对Linux内核进行漏洞挖掘,特别是针对netlink子系统。这是一种相对轻量级的内核fuzz方法,相比复杂的工具如syzkaller更易于理解和上手。

2. 技术背景

2.1 基于代码覆盖率的fuzz原理

基于代码覆盖率的fuzz工作流程如下:

  1. 测试用例选择:fuzzer选择最有希望的测试用例
  2. 变异生成:fuzzer将测试用例变异为大量新的测试用例
  3. 执行监控:目标代码运行变异的测试用例,并报告代码覆盖率
  4. 反馈优化:fuzzer根据覆盖范围计算得分,确定测试用例优先级并删除冗余

2.2 相关工具比较

  • syzkaller:功能强大的持续集成内核fuzzer,已发现数百个问题
  • Trinity:专门的内核fuzzer
  • AFL:以易用性、速度和优秀的变异逻辑著称,适合初学者

3. 准备工作

3.1 内核配置

首先需要配置内核以支持KCOV (代码覆盖率)和KASAN (内存错误检测):

cd linux
./scripts/config \
  -e KCOV \
  -d KCOV_INSTRUMENT_ALL \
  -e KASAN

3.2 选择性启用KCOV

在net目录下启用KCOV:

find net -name Makefile | xargs -L1 -I {} bash -c 'echo "KCOV_INSTRUMENT := y" >> {}'

3.3 虚拟化支持配置

为KVM运行配置必要的选项:

./scripts/config \
  -e VIRTIO -e VIRTIO_PCI -e NET_9P -e NET_9P_VIRTIO -e 9P_FS \
  -e VIRTIO_NET -e VIRTIO_CONSOLE -e DEVTMPFS

4. KCOV使用详解

4.1 基本操作

KCOV使用简单,通过ioctl控制:

ioctl(kcov_fd, KCOV_ENABLE, KCOV_TRACE_PC);  // 启用
/* 被分析的代码 */
ioctl(kcov_fd, KCOV_DISABLE, 0);             // 禁用

4.2 读取覆盖率数据

n = __atomic_load_n(&kcov_ring[0], __ATOMIC_RELAXED);
for (i = 0; i < n; i++) {
    printf("0x%lx\n", kcov_ring[i + 1]);
}

5. AFL与KCOV集成

5.1 AFL工作原理

AFL使用64KB的共享内存数组(trace_bits)记录分支对(branch_src, branch_dst)的命中计数:

cur_location = <COMPILE_TIME_RANDOM>;
shared_mem[cur_location ^ prev_location]++;
prev_location = cur_location >> 1;

5.2 KCOV数据转换

将KCOV的%rip值转换为AFL格式:

n = __atomic_load_n(&kcov_ring[0], __ATOMIC_RELAXED);
uint16_t prev_location = 0;
for (i = 0; i < n; i++) {
    uint16_t cur_location = hash_function(kcov_ring[i + 1]);
    shared_mem[cur_location ^ prev_location]++;
    prev_location = cur_location >> 1;
}

6. Netlink Fuzzing实现

6.1 读取AFL输入

char buf[512 * 1024];
int buf_len = read(0, buf, sizeof(buf));

6.2 Netlink操作

使用输入数据的前5字节作为netlink协议和组ID:

netlink_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, buf[0]);
struct sockaddr_nl sa = {
    .nl_family = AF_NETLINK,
    .nl_groups = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4],
};
bind(netlink_fd, (struct sockaddr*)&sa, sizeof(sa));

struct iovec iov = { &buf[5], buf_len - 5 };
struct sockaddr_nl sax = { .nl_family = AF_NETLINK };
struct msghdr msg = { &sax, sizeof(sax), &iov, 1, NULL, 0, 0 };
r = sendmsg(netlink_fd, &msg, 0);

6.3 主循环结构

forksrv_welcome();
while (1) {
    forksrv_cycle();
    test_data = afl_read_input();
    kcov_enable();
    /* netlink操作 */
    kcov_disable();
    /* 填充shared_map */
    if (new_crash_in_dmesg) {
        forksrv_status(1);
    } else {
        forksrv_status(0);
    }
}

7. 运行环境设置

7.1 运行选项比较

选项 优点 缺点
Native 速度最快 崩溃会丢失数据
UML 无需特权 不支持KASAN
KVM 稳定隔离 需要虚拟化支持

7.2 使用virtme运行

virtme-run \
  --kimg bzImage \
  --rw --pwd --memory 512M \
  --script-sh "./afl-fuzz -i inp -o out -- fuzznetlink"

8. 输入语料库

即使没有专门准备的语料库,也可以从简单输入开始:

mkdir inp
echo "hello world" > inp/01.txt

9. 优化方向

  1. AFL共享内存设置:优化shared_memory配置
  2. 持久模式:实现AFL持久模式提高效率
  3. 命名空间隔离:使用网络命名空间隔离netlink命令
  4. 崩溃检测:改进/dev/kmsg读取以检测内核崩溃
  5. 稳定性提升:在KVM外运行AFL提高稳定性

10. 总结

本方法不仅适用于netlink子系统,还可扩展到其他Linux内核子系统,如文件系统或BPF验证器。关键点包括:

  1. 正确配置内核支持KCOV和KASAN
  2. 实现KCOV到AFL的代码覆盖率数据转换
  3. 设计简单的netlink接口fuzzer
  4. 使用KVM提供隔离的运行环境

虽然这是一个基础实现,但已经能够发现一些内核问题。进一步的优化可以显著提高fuzzing效率和发现漏洞的能力。

使用AFL进行Linux内核漏洞挖掘技术详解 1. 概述 本教程详细介绍了如何使用AFL (American Fuzzy Lop) fuzzer对Linux内核进行漏洞挖掘,特别是针对netlink子系统。这是一种相对轻量级的内核fuzz方法,相比复杂的工具如syzkaller更易于理解和上手。 2. 技术背景 2.1 基于代码覆盖率的fuzz原理 基于代码覆盖率的fuzz工作流程如下: 测试用例选择 :fuzzer选择最有希望的测试用例 变异生成 :fuzzer将测试用例变异为大量新的测试用例 执行监控 :目标代码运行变异的测试用例,并报告代码覆盖率 反馈优化 :fuzzer根据覆盖范围计算得分,确定测试用例优先级并删除冗余 2.2 相关工具比较 syzkaller :功能强大的持续集成内核fuzzer,已发现数百个问题 Trinity :专门的内核fuzzer AFL :以易用性、速度和优秀的变异逻辑著称,适合初学者 3. 准备工作 3.1 内核配置 首先需要配置内核以支持KCOV (代码覆盖率)和KASAN (内存错误检测): 3.2 选择性启用KCOV 在net目录下启用KCOV: 3.3 虚拟化支持配置 为KVM运行配置必要的选项: 4. KCOV使用详解 4.1 基本操作 KCOV使用简单,通过ioctl控制: 4.2 读取覆盖率数据 5. AFL与KCOV集成 5.1 AFL工作原理 AFL使用64KB的共享内存数组( trace_bits )记录分支对(branch_ src, branch_ dst)的命中计数: 5.2 KCOV数据转换 将KCOV的%rip值转换为AFL格式: 6. Netlink Fuzzing实现 6.1 读取AFL输入 6.2 Netlink操作 使用输入数据的前5字节作为netlink协议和组ID: 6.3 主循环结构 7. 运行环境设置 7.1 运行选项比较 | 选项 | 优点 | 缺点 | |------|------|------| | Native | 速度最快 | 崩溃会丢失数据 | | UML | 无需特权 | 不支持KASAN | | KVM | 稳定隔离 | 需要虚拟化支持 | 7.2 使用virtme运行 8. 输入语料库 即使没有专门准备的语料库,也可以从简单输入开始: 9. 优化方向 AFL共享内存设置 :优化shared_ memory配置 持久模式 :实现AFL持久模式提高效率 命名空间隔离 :使用网络命名空间隔离netlink命令 崩溃检测 :改进/dev/kmsg读取以检测内核崩溃 稳定性提升 :在KVM外运行AFL提高稳定性 10. 总结 本方法不仅适用于netlink子系统,还可扩展到其他Linux内核子系统,如文件系统或BPF验证器。关键点包括: 正确配置内核支持KCOV和KASAN 实现KCOV到AFL的代码覆盖率数据转换 设计简单的netlink接口fuzzer 使用KVM提供隔离的运行环境 虽然这是一个基础实现,但已经能够发现一些内核问题。进一步的优化可以显著提高fuzzing效率和发现漏洞的能力。