这是内核漏洞挖掘技术系列的第十四篇。
第一篇:内核漏洞挖掘技术系列(1)——trinity
第二篇:内核漏洞挖掘技术系列(2)——bochspwn
第三篇:内核漏洞挖掘技术系列(3)——bochspwn-reloaded(1)
第四篇:内核漏洞挖掘技术系列(3)——bochspwn-reloaded(2)
第五篇:内核漏洞挖掘技术系列(4)——syzkaller(1)
第六篇:内核漏洞挖掘技术系列(4)——syzkaller(2)
第七篇:内核漏洞挖掘技术系列(4)——syzkaller(3)
第八篇:内核漏洞挖掘技术系列(4)——syzkaller(4)
第九篇:内核漏洞挖掘技术系列(4)——syzkaller(5)
第十篇:内核漏洞挖掘技术系列(5)——KernelFuzzer
第十一篇:内核漏洞挖掘技术系列(6)——使用AFL进行内核漏洞挖掘(1)
第十二篇:内核漏洞挖掘技术系列(7)——静态模式匹配
第十三篇:内核漏洞挖掘技术系列(6)——使用AFL进行内核漏洞挖掘(2)
前面介绍了很多内核漏洞挖掘工具和方法,要挖到操作系统中的内核漏洞还是有一定难度的,不过操作系统中通常还会安装第三方驱动,这些第三方驱动的代码质量参差不齐,挖掘其中的漏洞相对容易一些,而且同样也能达到获取system权限的效果。今天介绍windows操作系统中挖掘第三方驱动的经典工具ioctlbf(https://github.com/koutto/ioctlbf)。
ioctlbf可以通过执行以下两个任务来发现windows内核驱动程序中的漏洞:
1.扫描驱动程序支持的IOCTL
2.进行基于生成的IOCTL fuzz
该工具的优点是它不依赖于捕获的IOCTL,因此能够检测驱动程序支持但是很少甚至从未被用户使用的IOCTL。例如可能在非常特定的条件下调用的IOCTL(不容易发现/复现)或者用于调试的IOCTL等等。扫描完成并找到给定驱动程序的有效IOCTL后,用户可以在列表中选择一个IOCTL以开始进行基于生成的IOCTL fuzz。
代码目录结构如下。
inc:一些头文件
getopt.c:GNU中的getopt
ihm.c:打印输出
ioctl_bf.c:fuzzer的主要功能
ioctl_manipulation.c:ioctl_manipulation.h中定义了一个IOCTL列表。
ioctl_manipulation.c中提供了对该列表进行维护的函数。
rng.c:生成随机数
utilities.c:只有一个substr函数
ioctlbf代码量不多,最重要的也就是ioctl_bf.c文件。
下面以对avira(小红伞)的驱动进行fuzz为例说明。
1.使用DriverView之类的工具找到其驱动程序。
2.使用DeviceTree之类的工具检查与目标驱动程序关联的设备。这里确定了与avdevprot.sys对应的设备avdevprot。
3.反编译avdevprot.sys,在IDA中依次选择File->Produce File->Create C file…导出反编译的C文件。
4.搜索switch语句找到IOCTL。找到有效的IOCTL后即可使用ioctlbf。可以:1.只对一个IOCTL进行fuzz(-u参数);2.扫描一个范围内有效的IOCTL(-r参数);根据一个给定的IOCTL暴力破解枚举(-i参数)。
当然也可以去找对应的用户态程序调用DeviceIoControl的地方来找IOCTL。
5.选择一个IOCTL进行fuzz。
使用命令如下:
ioctlbf.EXE -d <deviceName> (-i <code>|-r <code1>-<code2>) [-u] [-q] [-f] [-e]
-d <devicename>: 设备名(不含\\.\)
-i : fuzz IOCTL的范围为code—code&0x00003fff
</devicename>
-r <code1>-<code2>: fuzz IOCTL的范围为code1-code2
-u: 只fuzz –i参数指定的那一个IOCTL
-f: 过滤掉对缓冲区长度没有限制的IOCTL
-q: fuzz时不显示hexdumps
-e: 扫描IOCTL 时显示error code
-h: 显示帮助信息</code2></code1>
经过解析命令行参数等准备工作之后首先调用CreateFile函数打开-d参数指定的驱动,如果失败就退出。
代码分析
接下来根据确定的IOCTL范围对每个IOCTL依次调用DeviceIoControl函数,如果遇到ERROR_ACCESS_DENIED错误或者ERROR_NOT_SUPPORTED错误就跳过。
如果设置了-f参数则再一次调用DeviceIoControl函数并将nInBufferSize参数和nOutBufferSize参数设置为MAX_BUFSIZE,即4096个字节。如果返回值不等于0则继续调用DeviceIoControl函数并将nInBufferSize参数和nOutBufferSize参数依次设置为0,1,2,3。如果返回值仍然不等于0表示该IOCTL对缓冲区长度没有限制,应该跳过。
接下来继续通过调用DeviceIoControl函数确定nInBufferSize和nOutBufferSize的最大值和最小值并将IOCTL,minBufferLength和maxBufferLength记录到IOCTLlist。
此时就可以开始fuzz了,如果没有指定-u参数则需要指定一个IOCTLlist中的IOCTL进行fuzz。fuzz分为4步。
第一步:给lpInBuffer和lpOutBuffer提供无效的地址。
第二步:提供比lpInBuffer和lpOutBuffer小的nInBufferSize和nOutBufferSize。
第三步:使用预设的DWORD(无效地址,指向长ascii/unicode字符串的地址,指向无效地址的表的地址)进行fuzz。
第四步:用完全随机的数据进行fuzz。
ioctlbf的一个改进版本是k0shl在2018年初发布的kDriver-Fuzzer(https://github.com/k0keoyo/kDriver-Fuzzer),增加了驱动枚举和日志记录等功能,有兴趣可以进一步研究改进。