Syzkaller实验报告(Crash Demo)
Syzkaller实验报告(Crash Demo)
实验配置
测试工具:Syzkaller
测试对象:linux6.7.0-rc4,编译进一个有堆溢出漏洞的驱动程序。
测试环境:
虚拟机:Kali 6.5.0
gcc 7.4.0
g++ 7.4.0
(注:gcc版本不要太高,当我使用gcc11的时候,编译时直接就检测出了堆溢出的问题。)
测试过程:
参考Syzkaller Crash Demo,复现了这个Demo 。
实验环境搭建过程在我的另一篇博客中:Set up Syzkaller (记录安装过程) | Xjelly‘s Blog
一、关键步骤:
1.添加导致堆溢出的规则到syzkaller并重新构建
1.Syzkaller语法
syzkaller自己定义了一套描述系统调用模版的声明式语言(syzlang),称之为描述文件/声明文件。为了提高fuzz效率,我们必须为目标系统量身定制这种声明文件。通常一个设备节点对应一个声明文件。所谓的声明文件就是一个txt,根据syzkaller定义的语法,在这个txt文档中描述设备节点的接口信息以及参数格式。
The grammar of *.txt
1
2
3
4 open$proc(file ptr[in, string["/proc/test"]], flags flags[proc_open_flags], mode flags[proc_open_mode]) fd
...
proc_open_flags = O_RDONLY, O_WRONLY, O_RDWR, O_APPEND, FASYNC, O_CLOEXEC, O_CREAT, O_DIRECT, O_DIRECTORY, O_EXCL, O_LARGEFILE, O_NOATIME, O_NOCTTY, O_NOFOLLOW, O_NONBLOCK, O_PATH, O_SYNC, O_TRUNC, \__O_TMPFILE
proc_open_mode = S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH系统调用的声明由系统调用名称、参数和返回值组成,系统调用名称的格式如下所示:
1 SyscallName$Type后面的"Type"是系统调用的具体类型。在我的例子中:
1 open$proc表示具有限定类型"proc"的系统调用"open()",名称由编写者确定,限制由后续参数确定,参数的格式如下:
1 ArgumentName ArgumentType[Limit]ArgumentName是参数的名称,ArgumentType是参数的类型。在我的例子中,有几种参数类型,比如string、flags等。"[Limit]"将限制参数的值,如果没有指定,syzkaller将生成一个随机值。
1
2 mode flags[proc_open_mode]
proc_open_mode = ...在我们的例子中,参数"mode"的类型是"flags",它将从“proc_open_mode = …”中选择一些值。
声明的最后是返回值。在我的例子中,"fd"是文件描述符。
一些常见的系统调用声明写在源代码树$(SYZKALLER_SOURCE)/sys/sys.txt中。
- More infomation about programmer can be found on this
在我的例子中,堆溢出可以通过写入/proc/test来触发。因此,我们应该将"open"的参数"file"限制为"/proc/test",其他可以参考sys.txt文件。
2.添加堆溢出规则
syzkaller源码中,找到sys/linux/目录,新建一个文件,命名为proc_operation.txt
,内容如下:
1 | include <linux/fs.h> |
3.在syzkaller目录下编译 syz-extract 和 syz-sysgen
如果syzkaller/bin目录下,没有syz-extract和syz-sysgen这两个文件的话,需要执行如下命令编译:
1 | make bin/syz-extract |
接下来我们使用 syz-extract 生成 .const 文件:指定txt文件名,可单独生成该文件对应的const文件。(注意自行更改源码的路径。)
1 | bin/syz-extract -os linux -sourcedir "root/source/linux" -arch amd64 proc_operation.txt |
运行syz-sysgen
1 | bin/syz-sysgen |
重新编译syzkaller
1 | make clean |
修改配置文件:
启动syzkaller的配置文件如下。为了更快看到crash结果,增加了“enable_syscalls”项,只允许某些系统调用,能更快地触发漏洞。
增加:
1 | "enable_syscalls": [ |
完整的配置文件如下:(注意自行修改文件路径。)
1 | { |
2.将堆溢出代码编译到内核
使用demo中的test.c
在kernel_src/drivers/char
目录下,新建一个test.c。这是一个有漏洞的内核模块,漏洞代码片段如下:
1 | static ssize_t proc_write (struct file *proc_file, const char __user *proc_user, size_t n, loff_t *loff) |
打开char/目录下的Kconfig文件,添加:
1 | config PROC_OP |
打开char/目录下的Makefile文件,添加:
1 | obj-$(CONFIG_PROC_OP) += test.o |
若/linux/drivers/char/是新目录,还需修改/linux/drivers/Kconfig(加上source “drivers/char/Kconfig”);修改/linux/drivers/Makefile(加上obj-$(CONFIG_PROC_OP) += char/)。
回到linux目录,修改.config,增加:CONFIG_PROC_OP=y
重新编译内核:
1 | make clean |
用新的内核启动虚拟机,查看模块是否加载成功
1 | # 查看模块对应设备节点是否存在 |
3.运行syzkaller来寻找该漏洞
1 | ./bin/syz-manager -config=my.cfg |
二、测试情况
1.整体情况
2.漏洞种类及其数量
Bad rss-rounter state:7
NULL pointer:80
generaral protection fault:18
waring:若干
3.漏洞种类及其成因
Bad rss-router state
属于网络漏洞的一种。它是由于操作系统或网络设备中的错误实现导致的。
RSS(Receive Side Scaling)是一种网络流量分发技术,可以将传入的网络流量分散到多个CPU核心来进行处理,提高网络性能。而Bad rss-router state漏洞是指在实现RSS的过程中,出现了错误的路由状态。这可能会导致网络流量被错误地分发到不正确的CPU核心,造成网络性能下降、数据包丢失或网络拥塞等问题。
这种漏洞的成因可能包括操作系统或网络设备中的软件错误、缓冲区溢出、内存错误或配置错误等。在本次测试中,极有可能是因为堆溢出而引发的。
NULL pointer deference
内核代码中出现了对空指针的解引用操作,导致系统无法正确处理而引发的错误。
在计算机编程中,空指针是指没有指向有效内存地址的指针。当内核代码中的某个地方尝试解引用空指针时,就会触发"unable to handle kernel NULL pointer dereference"错误。
general protection fault
一般保护错误时,表示内核尝试访问未分配给它的内存区域,或者访问了无效的内存地址。
以上三种错误都很有可能是堆溢出引发的。
4.关于reproducing
这次实验由于时间原因,没有分析漏洞复现。如果以后有机会就补上吧。
三、实验中的难点
1.配置时踩到的坑
踩坑详情可见我的博客:Set up Syzkaller (记录安装过程) | Xjelly‘s Blog
1.编译内核时缺程序:bc (Basic Calculate)
解决办法:安装bc。
2.创建镜像时wget时拒绝连接
解决办法:尝试修改各种网络设置,无效。由于文件只有一个且不是很大,于是手动复制。
3.运行create-image.sh时无法下载所需的包
解决办法:更换为国内镜像源并更新。
4.启动QEMU时在虚拟机中嵌套使用虚拟机
解决办法:在虚拟机设置中开启虚拟化,并在主机中关闭Hyper-v。Hyper-v和虚拟化不能同时使用。
5.启动QEMU时报错:Failed to mount /sys/kernel/config
解决办法:重新编译内核。有可能是配置的时候什么地方手误写错了。
6.测试syzkaller时无法启动网络接口
Failed to start Raise network interfaces
解决办法:修改网卡设置。修改/etc/network/interfaces中的eth0改为enp0s4。因为syzkalelr的启动命令和QEMU不一样,所以在测试QEMU时没有发现这个问题。
7.虚拟机磁盘空间不足
解决办法:扩容。
2.测试时遇到的困难
1.将带有漏洞的驱动程序编译入内核时报错:
从编译结果的末尾查找出错信息,一无所获,原来是编译的报错在编译信息的中间,在十几分钟的编译过程中被我忽视了。
查找了各个Makefile,却一无所获:
真正的报错:
第一个报错是关于test.c中的函数proc_create的参数类型不匹配的报错。
在linux6.7.0版本下:proc_create的最后一个参数要求是 const struct proc_ops * 而不是旧的 const struct file_operations *。
所以修改了demo test.c中 结构体a的数据类型:
第二个报错是关于堆溢出的报错,因为检测到了用户复制的数据会超出缓存区大小。
这个就有点尴尬,因为我的本意就是要编写一个堆溢出漏洞作为测试demo。
首先我怀疑了内核版本的问题,于是我切换到linux5.14,与参考文章[2]一样的测试版本,结果发现居然连这个版本的UAF漏洞都检查出来了。于是我才意识到应该是编译器的问题。
在上述的试验中,我使用的是gcc 11和g++ 11,高版本的编译器可能对程序的安全性做了更多的检查。
所以我换用了低版本的编译器。
1 | sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 60 --slave /usr/bin/g++ g++ /usr/bin/g++-7 |
这下终于不再报错了:
终于成功地编译了内核。
四、参考文献
【2】syzkaller fuzz 工具的使用方法及实践实例 | blingbling’s blog (blingblingxuanxuan.github.io)
【3】[原创]从0到1开始使用syzkaller进行Linux内核漏洞挖掘-二进制漏洞-看雪-安全社区|安全招聘|kanxue.com
【4】[Syzkaller安装 Fuzz Qemu amd64 Kernel | BruceFan’s Blog (pwn4.fun)](http://pwn4.fun/2019/05/31/Syzkaller安装 Fuzz Qemu amd64 Kernel/)
【5】Using syzkaller, part 2: Detecting programming bugs in the Linux kernel (collabora.com)
【6】零基础syzkaller挖掘Linux内核漏洞_debug earlyprintk=serial slub_debug=quz-CSDN博客
五、其他
把syzkaller开着跑了一下午,看看结果~