内核漏洞挖掘技术系列(4)——syzkaller(1)

2019-05-10 约 159 字 预计阅读 1 分钟

声明:本文 【内核漏洞挖掘技术系列(4)——syzkaller(1)】 由作者 houjingyi 于 2019-05-10 09:09:00 首发 先知社区 曾经 浏览数 97 次

感谢 houjingyi 的辛苦付出!

这是内核漏洞挖掘技术系列的第五篇。
第一篇:内核漏洞挖掘技术系列(1)——trinity
第二篇:内核漏洞挖掘技术系列(2)——bochspwn
第三篇:内核漏洞挖掘技术系列(3)——bochspwn-reloaded(1)
第四篇:内核漏洞挖掘技术系列(3)——bochspwn-reloaded(2)

前言

syzkaller是google的安全研究人员开发并维护的内核fuzz工具。它主要是用go语言编写的,也有少部分C代码。syzkaller支持akaros/fuchsia/linux/android/freebsd/netbsd/openbsd/windows等系统,发现的漏洞多达上千。不过它支持最全面的还是linux系统,对其它系统的支持都不同程度的差一点,也不支持Darwin/XNU。有研究人员做过移植syzkaller fuzz windows WSL[1]和Darwin/XNU[4]的尝试,也都取得了较好的成果。可以说,syzkaller是当今宇宙最强大的内核fuzz工具了。我们将从整体架构开始,介绍syzkaller一些实现的细节。本文分析的syzkaller版本是开始计划写作这个系列文章时从github下载的当时最新的版本,当读者读到这篇文章时代码肯定已经有了变化,所以文章仅供参考,若有变动之处还需自行分析。

整体架构

下面是官方原理介绍中给的一张图。

syz-manager通过ssh调用syz-fuzzer,syz-fuzzer和syz-manager之间通过RPC进行通信。syz-fuzzer将输入传给syz-executor,从kernel中读取代码覆盖率信息。syz-executor执行syscall系统调用。我们再看一下github上的整体目录。

  • Godeps目录:go的依赖包管理。
  • dashboard目录:主要与syzbot有关,syzbot会自动fuzz linux内核主线分支并向内核邮件列表报告发现的错误。我们可以在https://syzkaller.appspot.com上看到相关的情况。

  • docs目录:相关文档。

  • executor目录:文章最开始已介绍syz-executor。
  • pkg目录:配置文件。该目录下的结构如下图。

    • ast目录:解析并格式化sys文件。
    • bisect目录:通过二分查找,编译代码测试确定引入含有漏洞代码的commit和引入修复的commit。
    • build目录:包含用于构建内核的辅助函数。
    • compiler目录:从文本描述生成系统调用,类型和资源的sys描述。
    • config目录:加载配置文件。
    • cover目录:提供处理代码覆盖信息的类型。
    • csource目录:根据syzkaller程序生成等价的c程序。
    • db目录:存储syz-manager和syz-hub中的语料库。
    • email目录:解析处理邮件相关功能。
    • gce目录:对Google Compute Engine(GCE) API的包装。
    • gcs目录:对Google Compute Storage(GCS) API的包装。
    • hash目录:提供一些hash函数。
    • host目录:检测host是否支持一些特性和特定的系统调用。
    • html目录:提供一些web端显示fuzz结果的html页面用的辅助函数。
    • ifuzz目录:生成和变异x86机器码。
    • instance目录:提供用于测试补丁,镜像和二分查找的临时实例的辅助函数。
    • ipc目录:进程间通信。
    • kd目录:windows KD调试相关。
    • log目录:日志功能。
    • mgrconfig目录:管理解析配置文件。
    • osutil目录:os和文件操作工具。
    • report目录:处理内核输出和检测/提取crash信息并符号化等。
    • repro目录:对crash进行复现并进行相关的处理。
    • rpctype目录:包含通过系统各部分之间的net/rpc连接传递的消息类型。
    • runtest目录:syzkaller程序端到端测试的驱动程序。
    • serializer目录:序列化处理。
    • signal目录:提供用于处理反馈信号的类型。
    • symbolizer目录:处理符号相关信息。
    • vcs目录:处理各种库的辅助函数。
  • prog目录:目标系统相关信息以及需要执行的系统调用。

  • sys目录:系统调用描述。该目录下的结构如下图。

    syzkaller使用它自己的声明式语言来描述系统调用模板,docs目录下的syscall_descriptions.md中可以找到相关的说明。这些系统调用模板被翻译成syzkaller使用的代码需要经过两个步骤。第一步是使用syz-extract从linux源代码中提取符号常量的值。syz-extract生成一个小的C程序,其中包含由include指令引用的内核头文件,定义由define指令指定的宏并打印出符号常量。结果被存储在.const文件中,例如/sys/linux/tty.txt被转换为sys/linux/tty_amd64.const。第二步是根据系统调用模板和第一步中生成的const文件使用syz-sysgen生成syzkaller用的go代码。可以在/sys/linux/gen/amd64.go和/executor/syscalls.h中看到结果。如果需要对新的系统调用进行测试,需要将新的系统调用的描述添加到适当的文件中:各种sys/linux/.txt文件保存特定内核子系统的系统调用,例如bpf或socket。/sys/linux/sys.txt包含更多常规系统调用的描述。也可以为一个全新的子系统添加sys/linux/.txt文件。docs目录下的syscall_descriptions_syntax.md中可以找到语法描述,Project Zero也有相关博客[6]。回想一下我们第一篇文章介绍的trinity,也是需要自己编写系统调用模板。但是syzkaller的模板比trinity强大多了,系统调用和参数都可以自己定义。在sys/*.txt文件中描述了上千个系统调用,但是linux内核一直在持续的更新,每个新版本的发布系统调用和数据结构都在发生变化,需要人工更新系统调用模板,这个过程目前还没有实现自动化[7]。下面给大家简单介绍一下人工更新系统调用模板的步骤。
    当我们下载linux内核源码之后,比较一下最新的版本和上一个版本uapi/*.h文件发生了哪些变化。
    git diff -U0 v4.20 v4.19 include/uapi/*.h | grep "+++"

    Linus在linux 3.7版本中接受了David Howell的用来解决递归引用的补丁。递归引用这个问题通常发生在inline函数中:比如头文件A中的inline函数需要头文件中B的struct,但是同时B中也有一个inline函数需要A中的一个struct。所以David Howell把include和arch/xxxxxx/include目录中的内核头文件中有关用户空间API的内容分割出来,放到新的uapi/子目录中相应的地方。除了解决递归引用的问题之外,这样做简化了仅供内核使用的头文件的大小,并且使得跟踪内核向用户空间呈现的API的更改变得更容易。对C库维护者、测试项目(如LTP)、文档项目(如man-pages)还有我们利用syzkaller做内核fuzz的人来说有很大的帮助。大家还可以看看[2]上的一个例子,找找感觉。这篇文章中编写了一个含有漏洞的内核模块加载到内核中,然后在cfg文件的enable_syscalls中指定了相应的系统调用,运行syzkaller之后能够看到相关的crash信息。下面假设我们想对bpf模块做fuzz。根据linux\include\uapi\linux\bpf.h中的改动修改bpf.txt。由于syzkaller默认make时没有编译syz-extract和syz-sysgen,首先编译出这两个文件。

    用syz-extract生成.const文件。

    因为这里我编译的linux内核是amd64的,所以只生成了bpf_amd64.const。对于其它的架构只需要修改__NR_bpf的系统调用号即可。然后运行syz-sysgen。

    自己编译内核分析过CVE-2017-16995的同学应该还记得编译内核时需要开启CONFIG_BPF_SYSCALL相关选项。在cfg文件中指定enable_syscalls为bpf之后我们就可以运行syz-manager做fuzz了。

  • sys-ci目录:持续运行syzkaller的系统。

  • sys-fuzzer目录:文章最开始已介绍syz-fuzzer。
  • sys-hub目录:将多个syz-manager连接在一起并允许它们交换程序。
  • sys-manager目录:文章最开始已介绍syz-manager。
  • tools目录:一些工具。该目录下的结构如下图。

    这些工具大都是对pkg目录下代码的一些封装,check_links.py用来检查文档中的链接是否正确。还有几个create*.sh用来是用来生成镜像用的,demo_setup.sh包含设置syzkaller+qemu环境所有的操作。对于我们来说最重要的几个工具是syz-execprog、syz-repro和syz-prog2c。为syzkaller发现的bug生成能够复现的程序的过程是自动化的,但是如果不能自动化生成这个程序则需要一些工具手动复现。workdir/crashes目录下包含crash之前执行的程序。如果在config文件中设置的procs大于1那么是并行执行的,在这种情况下引发crash的程序可能在更前面的地方。syz-execprog和syz-prog2c可以帮助我们找到引发crash的程序。syz-execprog以各种模式执行单个或一组程序,首先在循环中运行log中所有的程序来确认确实它们之一引发了crash。
    ./syz-execprog -executor=./syz-executor -repeat=0 -procs=16 -cover=0 crash-log
    然后尝试识别是哪个程序导致的crash。
    ./syz-execprog -executor=./syz-executor -repeat=0 -procs=16 -cover=0 single-program
    syz-execprog在本地执行程序,所以需要将syz-execprog和syz-executor复制到带有测试内核的VM中并在那里运行它。一旦确认了引发崩溃的单个程序,尝试通过注释掉单个系统调用并删除无关的数据来缩写范围。还可以尝试将所有mmap调用合并为一个。给syz-execprog加上-threaded=0 -collide=0标志确认这个程序仍然能够导致crash。
    如果不能复现的话,尝试把每个系统调用移到单独的线程中[3]。然后通过syz-prog2c得到C程序,C程序应该也可以导致crash。这个过程在某种程度上也可以通过syz-repro自动化,需要提供config文件和crash报告。
    ./syz-repro -config my.cfg crash-qemu-1-1455745459265726910

  • vendor目录:依赖包。

  • vm目录:提供VM接口。该目录下的结构如下图。

    我们可以看到该目录下提供了qemu/kvm/vmm等多种虚拟化方案,还包括google自家的轻量沙箱gvisor和云服务,甚至可以使用Odroid C2和远程的物理机做fuzz。vm.go把它们封装成统一的接口方便调用。这里再提一个在编译比较老的内核时启用KASAN之后经常会遇到的一个错误,可以参考[5]patch一下代码。

总结

这篇文章我们简单聊了聊syzkaller的整体架构和如何人工更新系统调用模板,在接下来的文章中会逐步介绍syzkaller一些实现的细节。

参考资料

1.WSL Reloaded
2.Syzkaller crash DEMO
3.use-after-free in ip6_setup_cork
4.Drill the Apple Core:Up & Down
5.Kernel panic when kasan is applied
6.Exploiting the Linux kernel via packet sockets
7.sys/linux: automatic syscall interface extraction

关键词:[‘安全技术’, ‘二进制安全’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now