写点什么

web 安全之挖掘 Linux 内核漏洞

  • 2022 年 3 月 05 日
  • 本文字数:4597 字

    阅读完需:约 15 分钟

一、简述

Syzkaller 是 Google 开发的一款内核模糊测试工具,简单点说就是自动化向内核输入各种有效的、无效的、完全随机化的参数数据,并观察内核的运行状况,是否发生了 panic、内存泄漏等问题,以此发现隐藏在内核中的漏洞。近些年很多内核的 CVE 发现均来自于此,该工具的开发维护也相对活跃。它不仅支持 x86,还支持 ARM、Power、MIPS 等处理器,而且不仅支持 Linux,还支持 windows、FreeBSD、Fuchsia 等系统,同时还能支持对远程物理机、本地虚拟机的测试,此外还能支持分布式多机器测试。


本篇文章侧重于使用,并无太多原理与代码分析,仅需一点 linux 使用基础即可,适合用于 syzkaller 入门,整个环境搭建和使用过程踩了很多坑,有不少是网上没提到的。

二、基础环境


【一>所有资源获取<一】1、网络安全学习路线 2、电子书籍(白帽子)3、安全大厂内部视频 4、100 份 src 文档 5、常见安全面试题 6、ctf 大赛经典题目解析 7、全套工具包 8、应急响应笔记

三、环境搭建

3.1 Ubuntu 虚拟机配置

Ubuntu 虚拟机配置如下图所示,因为需要编译 Linux 内核与 syzkaller 所以内存尽量的设置大一些。



Vmware 自带的 vmtools 安装在 Ubunut1804 上不能与物理机之间互相拷贝文件可以尝试如下命令解决:


sudo apt updatesudo apt install open-vm-tools-desktop fuse
复制代码

3.2 安装基本软件

sudo apt-get install debootstrapsudo apt install qemu-kvmsudo apt-get install subversionsudo apt-get install gitsudo apt-get install makesudo apt-get install qemusudo apt install libssl-dev libelf-devsudo apt-get install flex bison libc6-dev libc6-dev-i386 linux-libc-dev linux-libc-dev:i386 libgmp3-dev libmpfr-dev libmpc-devsudo apt-get install g++sudo apt-get install build-essentialsudo apt install gccsudo apt install openssh-server
复制代码


安装 go 编程语言并没有使用 apt install golang-go,使用 apt 安装的 go 编程语言版本为 1.10,使用这个版本的 go 会在编译 syzkaller 时报错,所以在这选择下载安装 1.17 版本的 go。


wget https://dl.google.com/go/go1.17.6.linux-amd64.tar.gztar -zxvf go1.17.6.linux-amd64.tar.gzexport GOPATH=/home/test/git/go/go //路径替换为自己虚拟机中的路径export GOROOT=/home/test/git/go/goexport PATH=$GOPATH/bin:$PATHexport PATH=$GOROOT/bin:$PATH
复制代码


运行 go 命令可以执行,即为安装成功。

3.3 编译 syzkaller

使用下面的命令拉取编译 syzkaller 代码。


git clone https://github.com/google/syzkaller.gitcd syzkallermake //这一步有可能会报错
复制代码


如果出现卡死或 killed process,使用 dmesg | egrep -i -B100 ‘killed process’查看,如果为 Out of memory 即为内存不足。这时可以先使用如下命令单独编译第一个文件:


GOOS=linux GOARCH=amd64 go build "-ldflags=-s -w -X github.com/google/syzkaller/prog.GitRevision= -X 'github.com/google/syzkaller/prog.gitRevisionDate='" -o ./bin/syz-manager github.com/google/syzkaller/syz-manager
复制代码


查看 bin 目录下是否有编译好的 syz-manager 文件:



继续使用 make 命令完成编译,如下图所示:



如果单独编译第一个文件之后还是存在内存不足的问题,可以通过添加 swap 分区解决。


dd if=/dev/zero of=/root/swapfile bs=1M count=1024 //创建要作为swap分区的文件:增加1GB大小的交换分区,则命令写法如下,其中的count等于想要的块的数量(bs*count=文件大小)。mkswap /root/swapfile #建立swap的文件系统swapon /root/swapfile #启用swap文件/root/swapfile swap swap defaults 0 0 //使系统开机时自启用,在文件/etc/fstab中添加
复制代码

3.4 编译 Linux 内核

git 拉取 linux 代码:


git clone https://mirrors.tuna.tsinghua.edu.cn/git/linux.gitcd linux
复制代码


如果拉取代码的时候报证书校验错误如下图所示:



通过如下命令解决:


sudo apt updatesudo apt install -y libgnutls30
复制代码


进入 linux 目录后使用如下命令进行配置:


make CC="/usr/bin/gcc" defconfigmake CC="/usr/bin/gcc" kvm_guest.config
复制代码



配置完成后打开当前目录下的.config 文件进行手动添加配置,添加内容如下:


CONFIG_KCOV=yCONFIG_DEBUG_INFO=yCONFIG_KASAN=yCONFIG_KASAN_INLINE=yCONFIG_CONFIGFS_FS=yCONFIG_SECURITYFS=y
复制代码



再执行如下命令:make CC="/usr/bin/gcc" olddefconfig


如果出现如下图所示:



再次打开.config 文件发现刚才添加的配置被删除了,那是因为配置文件中存在如下图所示:



重新执行 olddefconfig 之前的所有配置命令,然后在.config 文件中,删除我们想添加配置的注释命令所在的行如:# CONFIG_KCOV is not set,最后在上面重新添加配置,然后执行 make CC=”/usr/bin/gcc” olddefconfig 命令可以发现不会出现 warning。


如果不删除在之后进行 qemu 虚拟化时会出现 Failed to start Remount Root and Kernel File Systems 的错误。



最后执行如下命令即可完成编译。make CC="/usr/bin/gcc" -j64


3.5 制作文件系统

使用如下命令:


wget https://raw.githubusercontent.com/google/syzkaller/master/tools/create-image.sh -O create-image.shchmod +x create-image.sh./create-image.sh
复制代码


wget 命令下载文件失败,可以直接浏览器访问拷贝一份也不影响使用。



可以看到目录下出现 stretch.id_rsa、stretch.id_rsa.pub、stretch.img 文件即为成功。


3.6 运行 syzkall

这里需要打开 Vmware 虚拟机的虚拟化。



安装 qemu 虚拟工具。


sudo apt-get install qemu-system-x86


在当前目录创建 boot.sh 文件,文件内容如下:


qemu-system-x86_64 \-kernel linux/arch/x86/boot/bzImage \-append "console=ttyS0 root=/dev/sda debug earlyprintk=serial slub_debug=QUZ"\-hda ./stretch.img \-net user,hostfwd=tcp::10021-:22 -net nic \-enable-kvm \-nographic \-m 2560M \-smp 2 \-pidfile vm.pid \2>&1 | tee vm.log 
复制代码


运行 boot.sh,出现 Failed to start Remount Root and Kernel File Systems 是上面配置文件没配置好,出现不能访问 KVM 为虚拟机设置问题。


运行 qemu 虚拟机有登录提示输入 root 如下图所示,无密码登录。



在 Vmware 虚拟机使用如下命令,以是否能登录 qemu 虚拟机判断 qemu 虚拟机的 ssh 服务是否成功启动(syzkaller 需要使用 ssh)。


ssh -i stretch.id_rsa -p 10021 -o "StrictHostKeyChecking no" root[@localhost](https://github.com/localhost "@localhost")
复制代码


进入之前下载的 syzkaller 目录,创建 my.cfg 配置文件,文件内容如下:


{"target": "linux/amd64","http": "127.0.0.1:56741","workdir": "/home/test/git/syzkaller/workdir","kernel_obj": "/home/test/git/linux","image": "/home/test/git/stretch.img","sshkey": "/home/test/git/stretch.id_rsa","syzkaller": "/home/test/git/syzkaller","procs": 8,"type": "qemu","vm": {"count": 4,"kernel": "/home/test/git/linux/arch/x86/boot/bzImage","cpu": 2,"mem": 2048}}
复制代码


使用./bin/syz-manager -config my.cfg 命令运行。运行时稍微有些慢需要等待一下。


四、解决 Failed to start Raise network interfaces 错误

执行 syz-manager 或 qemu 模拟运行的时候经常会出现 Failed to start Raise network interfaces 错误。


执行 boot.sh 脚本,运行起虚拟机,执行 ifconfig 命令,发现不存在此命令。



目前 qemu 虚拟机 ping 不通外网不能使用 apt 命令进行安装,所以这里选择下载 net-tools 离线包编译好,拷贝进 qemu 虚拟机。


qemu 虚拟机初始有默认的 ip 为 10.0.2.15,同时也会初始化物理机 ip 为 10.0.2.2。



可以使用如下命令进行文件拷贝操作:


ip link set enp0s3 upscp -r [test@10.0.2](mailto:test@10.0.2).2:/home/test/Desktop/net-tools-2.10 ./
复制代码


拷贝完成后就可以执行 ifconfig 命令了,如下图所示:



当使用 boot.sh 脚本运行 qemu 虚拟机,出现报错 Failed to start Raise network interfaces 的时候,再次执行 ifconfig 命令发现只存在 lo 网卡、enp0s3 网卡未启动或未分配 ip 地址。进行删除 qemu 虚拟机中的/etc/network/interfaces 文件,新建 interfaces 文件,文件内容如下,拷贝到到 qemu 虚拟机/etc/network/interfaces 路径。


auto eth0iface eth0 inet dhcp
auto enp0s3iface enp0s3 inet dhcp
复制代码


多次使用 boot.sh 启动 qemu 虚拟机,有时报错 Failed to start Raise network interfaces,然后使用 ifconfig 命令查看结果依旧存在 ip 地址。


本机网卡名不为 eth0 可以使用如下命令进行更改:


ip link set ens33 down ip link set ens33 name eth0 ip link set eth0 up


再次使用 syzkaller 进行 fuzz,效果会好很多,至于根本原因笔者目前也并未分析源码,以后可能会更新。


五、fuzz Linux 驱动程序

5.1 编译驱动

在 test.c 中存在一个堆溢出的 demo:



编译内核模块的时候,涉及到一个 linux header 的问题。(比如说我在 5.4.0 的系统下编译 5.17 的驱动)所以这里的 Makefile 如下:


CONFIG_MODULE_SIG=n
obj-m += test.o
EXTRA_CFLAGS += -fno-stack-protector -no-pieall:make -C /lib/modules/5.17.0-rc3-00316-gb81b1829e7e3/build M=$(PWD) modules
复制代码


创建目录 test,将 test.c 和 Makefile 拷贝到目录下,运行 make 命令。



如果找不到/lib/modules/5.17.0-rc3-00316-gb81b1829e7e3路径,在 linux 源代码目录下执行make modules_install /lib/module命令即可。



将 test.c 拷贝到 linux/drivers/char 目录下:


在 char 目录下的 Kconfig 文件中添加如下配置:


config TEST_MODULEtristate "Heap Overflow Test"default yhelpThis file is to test a buffer overflow.
复制代码


在 char 目录下的 Makefile 中添加 obj-$(CONFIG_TEST_MODULE) += test.o


进入 linux 源码目录重新 make,编译后使用 boot.sh 启动虚拟机,进入 proc 目录,可以看到 test,表明成功编译代码并加载。


5.2 添加 syzkaller 规则

进入 syzkaller/sys/linux/目录,新建 proc_operation.txt,文件内容如下所示:


include <linux/fs.h>
open$proc(file ptr[in, string[“/proc/test”]], flags flags[proc_open_flags], mode flags[proc_open_mode]) fdread$proc(fd fd, buf buffer[out], count len[buf])write$proc(fd fd, buf buffer[in], count len[buf])close$proc(fd 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_TMPFILEproc_open_mode = S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH
复制代码


回到 syzkaller 目录,编译 syz-extract 和 syz-sysgen:


make bin/syz-extractmake bin/syz-sysgen
复制代码


使用 syz-extract 生成.const 文件:


bin/syz-extract -os linux -sourcedir "/home/test/git/linux" -arch amd64 proc_operation.txt
复制代码


生成了 proc_operation.txt.const 内容如下:



接下来执行如下命令:


bin/syz-sysgenmake cleanmake //这里参考上面`
修改my.cfg文件,在其中加上如下字段:
`"enable_syscalls": ["open$proc","read$proc","write$proc","close$proc"],
复制代码

5.3 fuzz linux 驱动程序

使用 bin/syz-manager -config my.cfg 命令:






syzkaller 将其识别为空指针解引用错误。

六、总结

从使用体验来讲这个框架进行漏洞发掘还是存在一定难度,尤其对一些具有复杂接口的内核模块来说更是如此,并且是使用 go 编写的增加了学习成本,但它确实挖出了不少漏洞值得学习。

用户头像

我是一名网络安全渗透师 2021.06.18 加入

关注我,后续将会带来更多精选作品,需要资料+wx:mengmengji08

评论

发布
暂无评论
web安全之挖掘Linux内核漏洞_Linux_网络安全学海_InfoQ写作平台