热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用AFL对Linux内核Fuzzing的总结

模糊测试是现在最常用的漏洞挖掘技术,Fuzzer将半随机输入喂到到测试程序,目的是找到触发错误的输入。模糊测试在查找C或C程序中的内存破坏漏洞时特别有用

模糊测试是现在最常用的漏洞挖掘技术,Fuzzer将半随机输入喂到到测试程序,目的是找到触发错误的输入。模糊测试在查找C或C ++程序中的内存破坏漏洞时特别有用。

通常情况下,建议选择一个众所周知但很少探索的库,这个库在解析时很重要。历史上,像libjpeg,libpng和libyaml这样的东西都是完美的目标。如今找到一个好目标更难 – 一切似乎都已经被模糊化了。这是好事!我猜软件越来越好了!我没有选择用户空间目标,而是选择了Linux内核netlink机器。

Netlink是一个Linux内核工具,它用于配置网络接口,IP地址,路由表等。这是一个很好的fuzzing 目标:它是内核的一个小模块,并且生成畸形有效消息相对比较容易。最重要的是,我们可以在此过程中学到很多关于Linux内核的知识。

在这篇文章中,我将使用AFL模糊器,将netlink shim程序与自定义Linux内核相对应,所有这些都在KVM虚拟机中运行。

历史上的内核Fuzzing技术

我们将要使用的技术被称为“覆盖引导模糊测试”。有很多以前的文献:

· Dan Guido 的智能模糊革命,以及LWN关于它的文章

· Mateusz“j00ru”Jurczyk的有效文件格式模糊测试

· honggfuzz是一个现代化的,功能丰富的覆盖面引导的fuzzer

· ClusterFuzz

· Fuzzer测试套件

很多人过去都在Fuzzing Linux内核:

· 由Dmitry Vyukov 创建的syzkaller(又名syzbot)是一个非常强大的CI风格的持续运行的内核模糊器,它已经发现了数百个漏洞。

· 三位一体的模糊器

我们将使用AFL,可能是大家最喜欢的模糊器。AFL由MichałZalewski撰写。它以其易用性,速度和非常好的变异逻辑而闻名,这是开始模糊测试之旅的完美选择!奇热

如果您想了解有关AFL的更多信息,请参阅几个文件:

· 历史笔记

· 技术白皮书

· 自述

覆盖引导的模糊测试

覆盖引导的模糊测试基于反馈回路的原理:

· 模糊测试选择最有希望的测试用例

· 模糊测试将测试变为大量新的测试用例

· 目标代码运行变异的测试用例,并报告代码覆盖率

· 模糊器根据报告的覆盖范围计算得分,并使用它来确定有效的变异测试的优先级并删除冗余的测试

例如,假设输入测试是“hello”。Fuzzer可能会将其变为多种测试,例如:“hEllo”(位翻转),“hXello”(字节插入),“hllo”(字节删除)。如果这些测试中的任何一个将产生有趣的代码覆盖,那么它将被优先化并用作下一次测试的基础。

有关如何完成突变以及如何有效地比较数千个程序运行的代码覆盖率报告的细节是模糊测试的秘诀,阅读AFL的技术白皮书,可以了解更多细节。

通常,在使用AFL时,我们需要检测目标代码,以便以AFL兼容的方式报告覆盖范围。但我们想要Fuzzing 内核!我们不能只用“afl-gcc”重新编译它!。我们将准备一个二进制文件,让AFL认为它是用它的工具编译的。这个二进制文件将报告从内核中提取的代码覆盖率。

内核代码覆盖率

内核至少有两个内置的覆盖机制–GCOV和KCOV:

· 将gcov与Linux内核一起使用

· KCOV:模糊测试的代码覆盖率

KCOV的设计考虑了模糊测试,因此我们将使用它。

使用KCOV非常简单。我们必须使用正确的设置编译Linux内核。首先,启用KCOV内核配置选项:

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

KCOV能够记录整个内核的代码覆盖率。可以使用KCOV_INSTRUMENT_ALL选项进行设置。有个缺点是,它会减慢我们不想分析的内核部分,并且会在Fuzzing 中引入噪声(降低“稳定性”)。对于初学者,让我们禁用KCOV_INSTRUMENT_ALL并有选择地在实际想要分析的代码上启用KCOV。

我们专注于Fuzzing netlink,所以在整个“net”目录树上启用KCOV:

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

在一个理想环境中,我们只能为真正感兴趣的几个文件启用KCOV。重庆但是netlink处理遍及整个网络堆栈代码,现在没有时间进行微调。

有了KCOV,将增加报告内存损坏错误的可能性。最重要的是KASAN,使用该集合,可以编译我们的KCOV和KASAN启用的内核。

我们将以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 ...

如何使用KCOV

KCOV非常容易上手。代码覆盖率会记录在每个进程的数据结构中,就是说必须在用户空间进程中启用和禁用KCOV,并且无法记录非任务事项的覆盖范围,比如最常见的中断处理。

KCOV将数据发送到缓冲区,然后就可以使用一个简单的ioctl启用&禁用它:

 ioctl(kcov_fd, KCOV_ENABLE, KCOV_TRACE_PC);/* profiled code */ioctl(kcov_fd, KCOV_DISABLE, 0);

缓冲区会包含启用KCOV内核代码的所有基本块的%rip值列表。

要读取缓冲区,运行如下代码:

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

使用addr2line工具可以将&#xff05;rip解析为特定的代码行。

将KCOV喂到AFL中

AFL需要一个特制的可执行文件&#xff0c;但我们想要知道内核代码覆盖率。我们先了解一下AFL的工作原理。

AFL设置一个64K 8位数字的数组。该存储器区域称为“shared_mem”或“trace_bits”&#xff0c;并与trace的程序共享这块存储区域。数组中的每个字节都可以被认为是检测代码中特定对&#xff08;branch_src&#xff0c;branch_dst&#xff09;的命中计数器。

AFL更更多的是使用随机分支&#xff0c;而不是重用&#xff05;rip值来识别基本块&#xff0c;主要是为了增加熵&#xff0c;我们希望数组中的命中计数器均匀分布。

AFL使用的算法如下&#xff1a;

 cur_location &#61; ;shared_mem[cur_location ^ prev_location]&#43;&#43;; prev_location &#61; cur_location >> 1;

在使用KCOV的情况下&#xff0c;没有每个分支的编译时随机值。但是&#xff0c;我们可以使用哈希函数从KCOV记录的&#xff05;rip生成统一的16位数。

下面代码显示了如何将KCOV报告提供给AFL“shared_mem”数组&#xff1a;

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

从AFL读取测试数据

现在需要实际编写核心netlink接口的测试代码&#xff01;首先&#xff0c;我们需要从AFL读取输入数据。默认情况下&#xff0c;AFL将测试用例发送到stdin&#xff1a;

 /* read AFL test data */char buf[512*1024];int buf_len &#61; read(0, buf, sizeof(buf));

Fuzzing netlink

然后我们需要将此缓冲区发送到netlink套接字&#xff0c;使用前5个字节的输入作为netlink协议和组ID字段。这将允许AFL找出并猜测这些字段的正确值。

netlink测试代码&#xff08;简化&#xff09;&#xff1a;

netlink_fd &#61; socket(AF_NETLINK, SOCK_RAW | SOCK_NONBLOCK, buf[0]); struct sockaddr_nl sa &#61; {         .nl_family &#61; AF_NETLINK,         .nl_groups &#61; (buf[1] <<24) | (buf[2]<<16) | (buf[3]<<8) | buf[4], }; bind(netlink_fd, (struct sockaddr *) &sa, sizeof(sa)); struct iovec iov &#61; { &buf[5], buf_len - 5 }; struct sockaddr_nl sax &#61; {       .nl_family &#61; AF_NETLINK, }; struct msghdr msg &#61; { &sax, sizeof(sax), &iov, 1, NULL, 0, 0 }; r &#61; sendmsg(netlink_fd, &msg, 0); if (r !&#61; -1) {       /* sendmsg succeeded! great I guess... */ }

为了提升Fuzzing速度&#xff0c;我们将它包装在一个模仿AFL“fork服务器”逻辑的短循环中。

AFL-to-KCOV的结果代码如下所示&#xff1a;

forksrv_welcome(); while(1) {     forksrv_cycle();     test_data &#61; afl_read_input();     kcov_enable();     /* netlink magic */     kcov_disable();     /* fill in shared_map with tuples recorded by kcov */     if (new_crash_in_dmesg) {          forksrv_status(1);     } else {          forksrv_status(0);     } }

查看完整的源代码

运行自定义内核

如何实际运行构建的自定义内核。有三种选择&#xff1a;

“native”&#xff1a;您以在服务器上完全启动构建的内核并在本机Fuzzing它。这种方法速度很快&#xff0c;但也很有问题。如果Fuzzing成功找到了crash&#xff0c;电脑可能会蓝屏崩溃&#xff0c;可能会丢失测试数据。

“uml”&#xff1a;可以将内核配置为以用户模式运行。运行UML内核不需要任何权限&#xff0c;内核只运行用户空间进程。UML有一个问题是&#xff0c;它不支持KASAN&#xff0c;因此对于内存破坏漏洞的挖掘就那么有用了。

“kvm”&#xff1a;可以使用kvm在虚拟机环境中运行自定义内核&#xff0c;这就是我们要做的。

在KVM环境中运行自定义内核的最简单方法之一是使用“virtme”脚本。有了它们&#xff0c;我们可以避免创建专用的磁盘映像或分区&#xff0c;只需共享主机文件系统。

这就是我们运行代码的方式&#xff1a;

virtme-run \     --kimg bzImage \     --rw --pwd --memory 512M \     --script-sh ""

构建输入语料库

每个Fuzzer都需要精心设计的测试用例作为输入&#xff0c;以引导使程序产生突变输出。测试用例应该简短&#xff0c;并尽可能覆盖大部分代码。

这是我们的输入语料库&#xff1a;

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

如何编译和运行的说明都在我们的github上的README.md中。

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

运行后&#xff0c;AFL就开始Fuzzing了&#xff1a;

 

总结

在这篇文章中我们没有提到&#xff1a;

· AFL shared_memory设置的详细信息

· 运行AFL持久模式

· 关于如何读取dmesg&#xff08;/ dev / kmsg&#xff09;以查找内核crash的技巧

· 在KVM之外运行AFL&#xff0c;以获得速度和稳定性

但是实现了我们的目标&#xff0c;我们针对内核建立了一个基本但仍然有用的Fuzzer。最重要的是&#xff1a;可以重复使用相同的机制来Fuzzing Linux子系统的其他部分&#xff0c;比如从文件系统到bpf验证程序。

我有一些感悟&#xff1a;正确的模糊测试绝对不是启动Fuzzer后无所事事地等待崩溃。总有一些东西需要改进&#xff0c;调整和重新实现。Mateusz Jurczyk在之前演讲开头的一句话引起了我的共鸣&#xff1a;“Fuzzing很容易学&#xff0c;但很难掌握。”


推荐阅读
  • pypy 真的能让 Python 比 C 还快么?
    作者:肖恩顿来源:游戏不存在最近“pypy为什么能让python比c还快”刷屏了,原文讲的内容偏理论,干货比较少。我们可以再深入一点点,了解pypy的真相。正式开始之前,多唠叨两句 ... [详细]
  • 兆芯X86 CPU架构的演进与现状(国产CPU系列)
    本文详细介绍了兆芯X86 CPU架构的发展历程,从公司成立背景到关键技术授权,再到具体芯片架构的演进,全面解析了兆芯在国产CPU领域的贡献与挑战。 ... [详细]
  • c语言二元插值,二维线性插值c语言
    c语言二元插值,二维线性插值c语言 ... [详细]
  • oracle 对硬件环境要求,Oracle 10G数据库软硬件环境的要求 ... [详细]
  • C语言是计算机科学和编程领域的基石,许多初学者在学习过程中会感到困惑。本文将详细介绍C语言的基本概念、关键语法和实用示例,帮助你快速上手C语言。 ... [详细]
  • 在 CentOS 6.4 上安装 QT5 并启动 Qt Creator 时,可能会遇到缺少 GLIBCXX_3.4.15 的问题。这是由于系统中的 libstdc++.so.6 版本过低。本文将详细介绍如何通过更新 GCC 版本来解决这一问题。 ... [详细]
  • 本文详细介绍了在Linux系统上编译安装MySQL 5.5源码的步骤。首先,通过Yum安装必要的依赖软件包,如GCC、GCC-C++等,确保编译环境的完备。接着,下载并解压MySQL 5.5的源码包,配置编译选项,进行编译和安装。最后,完成安装后,进行基本的配置和启动测试,确保MySQL服务正常运行。 ... [详细]
  • protobuf 使用心得:解析与编码陷阱
    本文记录了一次在广告系统中使用protobuf进行数据交换时遇到的问题及其解决过程。通过这次经历,我们将探讨protobuf的特性和编码机制,帮助开发者避免类似的陷阱。 ... [详细]
  • 本文详细介绍了如何在ARM架构的目标设备上部署SSH服务端,包括必要的软件包下载、交叉编译过程以及最终的服务配置与测试。适合嵌入式开发人员和系统集成工程师参考。 ... [详细]
  • java类名的作用_java下Class.forName的作用是什么,为什么要使用它?
    湖上湖返回与带有给定字符串名的类或接口相关联的Class对象。调用此方法等效于:Class.forName(className,true,currentLoader) ... [详细]
  • MyBatisCodeHelperPro 2.9.3 最新在线免费激活方法
    MyBatisCodeHelperPro 2.9.3 是一款强大的代码生成工具,适用于多种开发环境。本文将介绍如何在线免费激活该工具,帮助开发者提高工作效率。 ... [详细]
  • centos 7.0 lnmp成功安装过程(很乱)
    下载nginx[rootlocalhostsrc]#wgethttp:nginx.orgdownloadnginx-1.7.9.tar.gz--2015-01-2412:55:2 ... [详细]
  • malloc 是 C 语言中的一个标准库函数,全称为 memory allocation,即动态内存分配。它用于在程序运行时申请一块指定大小的连续内存区域,并返回该区域的起始地址。当无法预先确定内存的具体位置时,可以通过 malloc 动态分配内存。 ... [详细]
  • 为了在Hadoop 2.7.2中实现对Snappy压缩和解压功能的原生支持,本文详细介绍了如何重新编译Hadoop源代码,并优化其Native编译过程。通过这一优化,可以显著提升数据处理的效率和性能。此外,还探讨了编译过程中可能遇到的问题及其解决方案,为用户提供了一套完整的操作指南。 ... [详细]
  • 在C语言程序开发中,调试和错误分析是确保代码正确性和效率的关键步骤。本文通过一个简单的递归函数示例,详细介绍了如何编写和调试C语言程序。具体而言,我们将创建一个名为 `factorial.c` 的文件,实现计算阶乘的功能,并通过逐步调试来分析和解决可能出现的错误。此外,文章还探讨了常见的调试工具和技术,如GDB和断点设置,以帮助开发者高效地定位和修复问题。 ... [详细]
author-avatar
rgx-秀_550
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有