在介绍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

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
2
3
4
5
6
7
8
9
10
11
sudo apt-get install debootstrap
sudo apt install qemu-kvm
sudo apt-get install subversion
sudo apt-get install git
sudo apt-get install make
sudo apt-get install qemu
sudo apt install libssl-dev libelf-dev
sudo apt-get install flex bison libc6-dev libc6-dev-i386 linux-libc-dev libgmp3-dev libmpfr-dev libmpc-dev
apt-get install g++
apt-get install build-essential
apt install gcc

系统自带的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
2
cd syzkaller
make

完成make操作后,构建的二进制文件在bin/目录下:

3.3 用于测试的Linux内核源码

我们的Fuzz测试是要对Linux内核进行漏洞挖掘,因此需要准备好内核。

这里我们选择下载Linux 5.14版本的内核。下载内核压缩包然后解压即可。

1
2
wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v5.x/linux-5.14.tar.gz
tar -zxvf linux-5.14.tar.gz

下载并解压完后,进入到Linux 5.14内核目录下,生成.config配置文件:

1
2
3
cd linux-5.14
make defconfig
make kvm_guest.config

然后用vim打开.config配置文件来编辑。为了提高我们的Fuzz效率,我们要在.config配置文件中加入如下内容(注意要将原有已经存在对应配置项即对应注释删除,可以使用记事本打开后搜索然后删除,否则后续的操作可能会报错):

1
2
3
4
5
6
CONFIG_KCOV=y
CONFIG_DEBUG_INFO_DWARF4=y
CONFIG_KASAN=y
CONFIG_KASAN_INLINE=y
CONFIG_CONFIGFS_FS=y
CONFIG_SECURITYFS=y

修改配置文件后,保存修改并退出,然后重新生成配置文件并编译:

1
2
make olddefconfig
make -j4

3.4 配置测试所用的虚拟机

在上一小节中我们已经准备好了待Fuzz的内核。我们需要将这个内核部署到一个虚拟机中,这样才能使用Syzkaller工具对这个内核进行Fuzz。

可以创建一个image文件夹,在该文件夹下使用debootstrap构建Linux内核启动镜像。

1
2
3
4
5
mkdir image
cd image
wget https://raw.githubusercontent.com/google/syzkaller/master/tools/create-image.sh -O create-image.sh
chmod +x create-image.sh
./create-image.sh

以上命令的执行时间较长,执行完成后,可以看到在目录下出现了如下内容:

可以看到生成了bullseye.id_rsabullseye.id_rsa.pubbullseye.img几个文件。其中生成的bullseye.img文件即为镜像磁盘。

之后就是激动人心的启动虚拟机环节。回到~/Desktop/目录下,编辑一个setup.sh脚本文件,在文件中写入以下内容:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
qemu-system-x86_64 \
-m 2G \
-smp 2 \
-kernel ./linux-5.14/arch/x86/boot/bzImage \
-append "console=ttyS0 root=/dev/sda earlyprintk=serial net.ifnames=0" \
-drive file=./image/bullseye.img,format=raw \
-net user,hostfwd=tcp:127.0.0.1:10021-:22 -net nic,model=e1000 \
-enable-kvm -nographic -pidfile vm.pid 2>&1 | tee vm.log

对各个参数的解释如下:

  • -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
2
chmod +x setup.sh
sudo bash 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
2
3
cd syzkaller/
sudo mkdir workdir
sudo vim setting.cfg

setting.cfg中写入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"target": "linux/amd64",
"http": "127.0.0.1:56741",
"rpc": "127.0.0.1:0",
"workdir": "/home/admin/Desktop/syzkaller/workdir",
"kernel_obj": "/home/admin/Desktop/linux-5.14",
"sshkey": "/home/admin/Desktop/image/bullseye.id_rsa",
"syzkaller": "/home/admin/Desktop/syzkaller",
"sandbox": "setuid",
"type": "isolated",
"vm": {
"targets" : [ "127.0.0.1:10021" ],
"pstore": false,
"target_dir" : "/root/fuzzdir",
"target_reboot" : false
}
}

以上参数也可以进行自定义,为了方便后续的测试与使用,现将上面配置文件中的各个参数进行详细解释:

  • 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地址是可以直接到浏览器输入框输入然后访问的: