Syzkaller部署、使用与原理分析
在介绍Skykaller之前,需要对Fuzz模糊测试进行一个简单的介绍。
1 Fuzz
模糊测试(fuzz testing, fuzzing)是一种软件测试技术。其核心思想是自动或半自动的生成随机数据输入到一个程序中,并监视程序异常,如崩溃,断言(assertion)失败,以发现可能的程序错误,比如内存泄漏。模糊测试常常用于检测软件或计算机系统的安全漏洞。
进行软件漏洞挖掘时,通常有静态分析(static analysis)、动态分析(dynamic analysis)、符号执行(symbolic execution)、模糊测试(fuzzing)这几种技术手段。
- 静态分析:不真正的运行目标程序,但是通过对它进行各种语法、语义、数据流等的分析,来进行漏洞发掘。静态分析是由静态分析软件完成的;它的速度快,但是误报率高。
- 动态分析:我们通常见到的大佬们用od一步步跟踪程序运行进行的分析。它的准确率很高,但是需要调试人员丰富的知识储备,而且这种调试方法很难进行大规模的程序漏洞挖掘。
- 符号执行:简单来说,就是试图找到什么输入对应什么样的运行状态,它要去覆盖所有的执行路径。因此,当被分析的程序比较复杂,有很多执行路径时,就会遇到路径爆炸的问题。
- 模糊测试:不需要人过多的参与,也不像动态分析那样要求分析人员有丰富的知识。简单解释,它就是用大量的输入数据自动去执行程序,从而发现哪些输入能够使程序发生异常,进而分析可能存在的漏洞。
2 Skykaller
2.1 概述
Syzkaller是一个用于系统内核的Fuzz测试工具,主要用于发现操作系统内核中的漏洞和错误。它是一个开源项目,由Google公司开发和维护。
Syzkaller的工作原理是通过生成大量随机的系统调用序列,以及输入数据,来Fuzz测试操作系统内核。通过在不同的场景下模拟系统调用和输入数据,Syzkaller可以发现一些潜在的漏洞,例如内存泄漏、空指针引用、死锁等。
Syzkaller支持多种操作系统内核,包括Linux、FreeBSD、NetBSD等,并且可以方便地扩展支持新的系统调用和数据类型。通过对Syzkaller的使用,开发人员可以提高操作系统内核的安全性和稳定性。
Syzkaller具有以下主要特点:
- 自动化测试:Syzkaller是一个自动化的Fuzz测试工具,可以自动生成大量的测试用例,并在目标系统上运行这些测试用例,无需人工干预;
- 系统覆盖广泛:Syzkaller支持多种操作系统内核,包括Linux、FreeBSD、NetBSD等,可以用于不同的操作系统环境中进行测试;
- 高效发现漏洞:通过Fuzz测试的方式,Syzkaller可以在较短的时间内发现系统内核中的各种漏洞和错误,如内存泄漏、空指针引用、死锁等;
- 容易扩展性:Syzkaller的设计考虑了易于扩展性,可以方便地添加新的系统调用和数据类型,以适应不断变化的内核代码结构;
- 持续集成:Syzkaller可以与持续集成系统集成,实现自动化的测试流程,及时发现和修复新的漏洞;
- 开源社区支持:作为一个开源项目,Syzkaller得到了广泛的开源社区支持和贡献,有着活跃的开发者和用户社区,持续改进和更新。
此外Syzkaller工具基于Go语言、C/C++语言、Roff语言和Python语言开发。总的来说,Syzkaller是一个强大的工具,对于提高操作系统内核的安全性和稳定性起着至关重要的作用。
2.2 架构
关于Syzkaller的整体架构如下图所示,该图摘自syzkaller/docs/internals.md at master · google/syzkaller。
sys-manager负责:
- 启动、重新启动和监控多个虚拟机实例,并在虚拟机内部启动一个进程;
- 实际的模糊测试过程(输入代、突变、最小化等);
- 负责持久性语料库和崩溃存储;
它在具有稳定内核的主机上运行,该内核不会受到白噪声Fuzz测试负载的影响。
若要执行程序,请启动瞬态syz-executor子进程。
每个syz-executor进程执行单个输入(一系列系统调用)。它被设计得尽可能简单(以不干扰Fuzz测试过程),用C++编写,编译为静态二进制,并使用共享内存进行通信。
3 使用syzkaller进行Linux内核漏洞挖掘
3.1 环境准备
准备一台装有Linux系统的机器。如果没有可以去云平台租用一台Linux云服务器。这里我使用的是阿里云无影云电脑,系统版本是Ubuntu 22.04。
首先更新一遍软件源,防止安装某些软件时显示无可用安装包。
1 | sudo apt-get update |
Syzkaller的安装部署需要很多软件的支持,除了系统自带的软件环境外,通过指令安装额外需要的基本软件。
1 | sudo apt-get install debootstrap |
系统自带的go语言版本可能较低,可以用snap安装较新版本的go。
1 | snap install go |
如果你的系统没有snap,那就可以通过golang的官网下载压缩包进行安装,这里就不赘述。
3.2 syzkaller构建
- 通过git获取syzkaller源码。
1 | git clone git@github.com:google/syzkaller.git |
- make编译编译syzkaller
1 | cd syzkaller |
完成make
操作后,构建的二进制文件在bin/
目录下:
3.3 用于测试的Linux内核源码
我们的Fuzz测试是要对Linux内核进行漏洞挖掘,因此需要准备好内核。
这里我们选择下载Linux 5.14版本的内核。下载内核压缩包然后解压即可。
1 | wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v5.x/linux-5.14.tar.gz |
下载并解压完后,进入到Linux
5.14内核目录下,生成.config
配置文件:
1 | cd linux-5.14 |
然后用vim打开.config
配置文件来编辑。为了提高我们的Fuzz效率,我们要在.config
配置文件中加入如下内容(注意要将原有已经存在对应配置项即对应注释删除,可以使用记事本打开后搜索然后删除,否则后续的操作可能会报错):
1 | CONFIG_KCOV=y |
修改配置文件后,保存修改并退出,然后重新生成配置文件并编译:
1 | make olddefconfig |
3.4 配置测试所用的虚拟机
在上一小节中我们已经准备好了待Fuzz的内核。我们需要将这个内核部署到一个虚拟机中,这样才能使用Syzkaller工具对这个内核进行Fuzz。
可以创建一个image
文件夹,在该文件夹下使用debootstrap构建Linux内核启动镜像。
1 | mkdir image |
以上命令的执行时间较长,执行完成后,可以看到在目录下出现了如下内容:
可以看到生成了bullseye.id_rsa
、bullseye.id_rsa.pub
、bullseye.img
几个文件。其中生成的bullseye.img
文件即为镜像磁盘。
之后就是激动人心的启动虚拟机环节。回到~/Desktop/
目录下,编辑一个setup.sh
脚本文件,在文件中写入以下内容:
1 |
|
对各个参数的解释如下:
-m 2G
:分配2G内存给虚拟系统;-smp 2
:分配2个CPU给虚拟系统;-kernel ./linux-5.14/arch/x86/boot/bzImage
:使用linux-5.14/arch/x86/boot/bzImage
作为内核镜像文件,QEMU可以使用这个功能用来测试不同的内核;-append "console=ttyS0 root=/dev/sda earlyprintk=serial net.ifnames=0"
:添加内核启动参数,包括控制台设置、根文件系统设备、早期打印和网络接口名称(禁用);-drive file=./image/bullseye.img,format=raw
:指定image-test/bullseye.img
作为磁盘映像文件,并指定其格式为raw;-net user,hostfwd=tcp:127.0.0.1:10021-:22 -net nic,model=e1000
:添加网络配置,将虚拟机的网卡连接到一个用户网络,可以使宿主机与客户机进行通信,同时使用e1000模型;-enable-kvm
:开启KVM虚拟化,启用KVM硬件加速。注意绝大多数云服务器的CPU不支持虚拟化。-nographic
:禁用图形显示,所有输出都将通过控制台输出-pidfile vm.pid 2>&1 | tee vm.log
:将QEMU进程pid储存在vm.pid这个文件中,并将执行过程中的标准错误和标准输出同时定向输出到控制台和vm.log文件中。
赋予setup.sh
可执行权限,随后执行它:
1 | chmod +x setup.sh |
执行完后,出现以下界面说明成功了,输入root
后按Enter
键就进入到了虚拟机中。另外需要注意的是,一直到Fuzz结束之前,此虚拟机都不要关闭,如果Fuzz结束或者有其它原因需要关闭这个虚拟机,只需要在此虚拟机内执行init 0
命令即可。
进入虚拟机后我们暂时先不要做其它操作,我们首先在主机新开一个终端,执行如下命令使用ssh来查看主机和这个虚拟机之间是否可以进行通信:
1 | sudo ssh -i image/bullseye.id_rsa -p 10021 -o "StrictHostKeyChecking no" root@localhost |
若出现以下内容,即代表主机和刚刚安装的虚拟机之间的通信没有问题:
既然主机和客户机之间的通信没有问题,那么我们在这个新打开的终端中执行exit
命令,断开ssh连接。
随后我们就可以进行最后的配置,准备开始Fuzz了。我们只需在刚刚创建的虚拟机中顺序执行如下命令,就可以创建一个新的目录fuzzdir
,此目录作为Fuzz过程中的中间文件的存储目录:
1 | mkdir fuzzdir |
3.5 对Linux内核进行Fuzz
如果已经成功部署了Syzkaller,那么对其进行测试就比较简单了,首先打开一个新的终端,在其中顺序执行如下命令,目的是进入到Syzkaller源代码目录中,并新建一个工作目录和一个配置文件:
1 | cd syzkaller/ |
在setting.cfg
中写入以下内容:
1 | { |
以上参数也可以进行自定义,为了方便后续的测试与使用,现将上面配置文件中的各个参数进行详细解释:
target
:目标操作系统/架构;http
:显示有关正在运行的syz-manager进程的信息的url;rpc
:为Fuzz进程的RPC提供服务的TCP地址(可选);workdir
:syz-manager进程的工作目录的路径;kernel_obj
:内核构建目录的路径;sshkey
:用于与虚拟机通信的根SSH标识的位置(在主机上)(对于某些虚拟机类型可能为空);syzkaller
:Syzkaller源码的位置;sandbox
:Fuzz处理期间要使用的沙盒类型none
:root下试验;namespace
:使用CLONE_NEWUSER创建一个新的用户命名空间进行测试(仅在Linux上支持);setuid
:模拟成用户nobody(65534)(在Linux、FreeBSD、NetBSD、OpenBSD上受支持);android
:模拟不受信任的Android应用程序的权限(仅在Linux上支持);
type
:要使用的虚拟机类型,例如“qemu”、“gce”、“android”、“isolated”等;vm.targets
:用于Fuzz测试的主机列表;vm.pstore
:如果被测设备(DUT)支持Pstore,则可以配置Syzkaller从sys/fs/pstore
获取崩溃日志;vm.target_dir
:目标主机上的工作目录;vm.target_reboot
:如果远程进程挂起,请重新启动机器(对广泛的Fuzz测试很有用,默认为false
)。
然后执行如下命令启动Syzkaller进行Fuzz。此命令中的syz-manager是一个进程,将会启动虚拟机并在其中开始Fuzz测试,并利用-config命令行选项确定的配置文件的位置,根据配置文件中的具体配置内容进行Fuzz:
1 | sudo ./bin/syz-manager -config=setting.cfg |
成功启动后,终端中会出现如下图所示的内容,我们需要保存下图红框处的http地址,因为发现的崩溃、统计数据和其他信息都会在管理器配置的这个指定的Http地址上公开:
这个http地址是可以直接到浏览器输入框输入然后访问的: