内核漏洞挖掘技术系列(5)——KernelFuzzer

2019-06-21 约 540 字 预计阅读 3 分钟

声明:本文 【内核漏洞挖掘技术系列(5)——KernelFuzzer】 由作者 houjingyi 于 2019-06-21 07:42:00 首发 先知社区 曾经 浏览数 19 次

感谢 houjingyi 的辛苦付出!

这是内核漏洞挖掘技术系列的第十篇。
第一篇:内核漏洞挖掘技术系列(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)

前言

KernelFuzzer(https://github.com/mwrlabs/KernelFuzzer)是一个mwrlab前几年在defcon24开源的内核fuzz工具,宣称支持Windows 7/10,OS X和QNX系统,PPT的题目就叫Platform Agnostic Kernel Fuzzing(平台无关的内核Fuzzing)。但是我仔细看过代码之后觉得吹nb有点过分了,其实基本上也就只支持windows系统。不过它们github页面上还有一个OSX内核fuzz工具(https://github.com/mwrlabs/OSXFuzz),两者的代码框架倒是有一些相似。

整体架构

我们首先还是来看一下代码的整体目录。

  • crash_processing:处理crash
  • crashes:产生的crash会存放在这个目录
  • library_calls:要fuzz的库调用
  • reproducer:复现crash(这个目录下就几行代码,基本没什么用)
  • worker_setup:设置环境,启动fuzzer
  • bughunt.c:启动fuzz的线程
  • bughunt.h:提供一些返回随机字符或数组的函数
  • bughunt_build_*_*.bat:编译用的bat文件
  • bughunt_loop.py:处理发现的creash
  • bughunt_syscall_x64/bughunt_syscall.asm:进行系统调用的汇编文件
  • bughunt_syscalls.h:要fuzz的系统调用
  • bughunt_thread.h:bughunt.c启动的进行fuzz的线程
  • handles_database.h:生成各种各样的handle
  • helpers.h:几个辅助函数
  • hooking.h:设置和取消hook
  • library_calls.h:library_calls目录下要fuzz的库调用
  • logger.h:日志功能

使用需要先在主机上安装好VS环境,使用提供的bat文件编译出可执行文件,然后将所有文件拷贝到待fuzz的虚拟机系统,在虚拟机系统上安装好python环境,运行worker_setup.py。

代码分析

接下来从worker_setup.py开始分析。在worker_setup目录下除了worker_setup.py还有windbg的安装文件和禁用UAC,禁用自动更新,禁用锁屏,禁用Windows错误报告的注册表脚本文件。

在worker_setup.py中安装了windbg并执行了这些脚本文件,还安装了python的couchdb模块,配置dump文件路径,设置在登录时运行bughunt_loop.py的计划任务,为win32k.sys启用special pool,开启内核调试,最后重启系统。在bughunt_loop.py中如果在C:/Dumps/目录下发现了.dmp文件就说明产生了crash,将其拷贝到新的文件夹,使用kd.exe打开该文件,输出记录到windbg.log,将.log日志文件拷贝到和.dmp文件相同的文件夹,调用couchdb_submit.py将crash信息提交到远程服务器上的数据库。然后运行bughunt.exe,线程设置为1,系统调用设置为350000,seed设置为1,timeout设置为10分钟。超时后执行一些清理工作并重启。
在bughunt.c中,如前所述,主要就是启动fuzz的线程。

在bughunt_thread.h的bughunt_thread函数中首先随机调用要fuzz的库调用。这里也可以看到本意可能是在进行库调用时随机用hooking.h提供的设置和取消hook的功能,但是实际上没有用到。

library_calls.h中有library_calls目录下要fuzz的所有库调用名,这里library_calls目录下只提供了一个GetSysColorBrush。


接下来就是随机进行系统调用了,根据bughunt_syscalls.h提供的参数类型信息随机生成对应的参数,这里也只提供了两个系统调用。

系统调用参数可以是bool,char,int,handle等几种情况,然后调用bughunt_syscall函数。

具体实现分别在bughunt_syscall.asm和bughunt_syscall_x64.asm中,分别处理32位和64位的情况。
32位下参数通过栈传递,提取系统调用号和系统调用参数压栈然后mov edx, 7FFE0300h和call dword ptr [edx]即可。

64位下前四个参数通过RCX,RDX,R8和R9传递,RCX,RDX,R8和R9原来是系统调用号,第一个参数,第二个参数,第三个参数,现在把系统调用号RCX放入RAX,参数依次向前挪,RCX赋值为RDX,RDX赋值为R8,R8赋值为R9,R9赋值为[rbp + 30h],RCX,RDX,R8和R9现在是第一个参数,第二个参数,第三个参数,第四个参数,最后调用syscall指令。

功能拓展

从前面的分析我们可以看到代码的功能还是比较简陋的,也没有提供多少库调用和系统调用供fuzz。如果我们要增加更加的系统调用呢?系统调用表有现成的,但是参数好像没有现成的。我目前也只想到参考ReactOS的代码生成系统调用参数这个方法。
首先下载j00ru提供的系统调用表(https://github.com/j00ru/windows-syscalls)和ReactOS的代码(https://github.com/reactos/reactos/archive/0.4.11-release.zip),找到ReactOS中含有对应系统调用的头文件(脚本运行需要的时间有点长)。

import os
import csv
import shutil

global allfiles
allfiles = []

def findstring(pathfile, syscallname):
    file = open(pathfile, "r")
    string = file.read()
    if string.find(syscallname) != -1:
        global allfiles
        if pathfile not in allfiles:
            allfiles.append(pathfile)

def readFilename(file_dir, syscallname):
    for root, dirs, files in os.walk(file_dir):
        for file in files:
            if file.endswith(".h"):
                full_path=os.path.join(root, file)
                findstring(full_path, syscallname)

file_path = "F:\\windows-fuzzing\\ReactOS-0.4.11"
fscv = csv.reader(open("F:\\windows-fuzzing\\KernelFuzzer\\nt.csv", 'r'))
for row in fscv:
    syscallname = row[0]
    readFilename(file_path, syscallname)
fscv = csv.reader(open("F:\\windows-fuzzing\\KernelFuzzer\\win32k.csv", 'r'))
for row in fscv:
    syscallname = row[0]
    readFilename(file_path, syscallname)
print allfiles

然后用正则表达式转换成KernelFuzzer用的格式。

import re
import csv

def search_csv(str,fscv,f):
    for row in fscv:
        if str in row:
            if row[26]:
                syscallname = row[0]
                syscallnumber = row[15]
                #win10 1903:27
                #win10 1809:26
                #win10 1803:25
                #win10 1709:24
                #win7 SP1:15
                #XP SP2:2
                arguments = "{"
                while True:
                    str = f.readline()
                    flag = 0

                    matchobj = re.search(r'(\s)+DWORD|int\s', str)
                    if matchobj:
                        flag = 1
                        arguments += " _INT32,"
                    matchobj = re.search(r'(\s)+DWORD_PTR\s', str)
                    if matchobj:
                        flag = 1
                        arguments += " _INT32_PTR,"
                    matchobj = re.search(r'(\s)+UINT\s', str)
                    if matchobj:
                        flag = 1
                        arguments += " _UINT32,"
                    matchobj = re.search(r'(\s)+UINT_PTR\s', str)
                    if matchobj:
                        flag = 1
                        arguments += " _UINT32_PTR,"
                    matchobj = re.search(r'(\s)+LONG\s', str)
                    if matchobj:
                        flag = 1
                        arguments += " _INT64,"
                    matchobj = re.search(r'(\s)+LONG_PTR\s', str)
                    if matchobj:
                        flag = 1
                        arguments += " _INT64_PTR,"
                    matchobj = re.search(r'(\s)+ULONG\s', str)
                    if matchobj:
                        flag = 1
                        arguments += " _UINT64,"
                    matchobj = re.search(r'(\s)+ULONG_PTR\s', str)
                    if matchobj:
                        flag = 1
                        arguments += " _UINT64_PTR,"
                    matchobj = re.search(r'(\s)+H(.*)\s', str)
                    if matchobj:
                        flag = 1
                        arguments += " _HANDLE,"
                    matchobj = re.search(r'(\s)+BOOL\s', str)
                    if matchobj:
                        flag = 1
                        arguments += " _BOOL,"
                    matchobj = re.search(r'(\s)+LPVOID\s|(\s)+PVOID\s', str)
                    if matchobj or flag == 0:
                        flag = 1
                        arguments += " _VOID_PTR,"
                    matchobj = re.search(r'\);', str)
                    if matchobj:
                        arguments += " NIL }, NIL },"
                        break

                finalstr = "    { ((DWORD)" + syscallnumber + "), " + arguments + "     //" + syscallname
                print finalstr
                return True
    return False

def locate_function_to_search(f):
    while True:
        str = f.readline()
        if not str:
            break
        matchobj = re.search(r'Nt(.*)\(',str)
        if matchobj:
            index=str.find("(")
            str = str[:index]
            ntfile = open("F:\\windows-fuzzing\\KernelFuzzer\\nt.csv", 'r')
            fntscv = csv.reader(ntfile)
            if search_csv(str, fntscv, f) == True:
                continue
            win32kfile = open("F:\\windows-fuzzing\\KernelFuzzer\\win32k.csv", 'r')
            fwin32kscv = csv.reader(win32kfile)
            if search_csv(str, fwin32kscv, f) == True:
                continue

if __name__ == '__main__':
    files = (
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\lpcfuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\sefuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\xdk\\ntifs.template.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\exfuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\iofuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\psfuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\mmfuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\xdk\\mmfuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\kefuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ddk\\ntpoapi.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\pofuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\base\\setup\\lib\\utils\\filesup.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\drivers\\filesystems\\udfs\\Include\\nt_native.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\drivers\\filesystems\\udfs\\Include\\zw_2_nt.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\obfuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\psdk\\winternl.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\xdk\\nttmapi.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\cmfuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\xdk\\winnt_old.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\dbgkfuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\dbgktypes.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\iotypes.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\base\\setup\\lib\\utils\\regutil.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\rtlfuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\pstypes.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\mmtypes.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\xdk\\iotypes.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\drivers\\filesystems\\udfs\\Include\\env_spec_w32.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\xdk\\cmtypes.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\drivers\\filesystems\\udfs\\CDRW\\scsi_port.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\drivers\\storage\\ide\\uniata\\ntddscsi.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\drivers\\storage\\ide\\uniata\\atapi.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\dll\\shellext\\shellbtrfs\\shellext.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\cmtypes.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\reactos\\drivers\\ntddrdsk.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\xdk\\psfuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\potypes.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\psdk\\powrprof.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\kdfuncs.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\extypes.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\lpctypes.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\modules\\rostests\\win32\\DriverLoading\\Application\\DriverTester.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\drivers\\filesystems\\udfs\\Include\\CrossNt\\CrossNt.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\drivers\\storage\\ide\\uniata\\ntddk_ex.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\drivers\\storage\\ide\\uniata\\inc\\CrossNt.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\modules\\rostests\\kmtests\\include\\kmt_test.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\reactos\\wine\\winioctl.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\reactos\\subsys\\win\\conmsg.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\psdk\\ntdll.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\drivers\\filesystems\\udfs\\Include\\env_spec_nt.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\reactos\\debug.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\ndk\\ketypes.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\psdk\\ntgdi.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\win32ss\\gdi\\gdi32\\include\\gdi32p.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\win32ss\\user\\ntuser\\painting.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\modules\\rostests\\dxtest\\win32kdxtest\\test.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\win32ss\\include\\ntgdibad.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\win32ss\\gdi\\ntgdi\\intgdi.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\win32ss\\include\\ntgdityp.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\win32ss\\gdi\\gdi32\\wine\\gdi_private.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\win32ss\\user\\ntuser\\winpos.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\win32ss\\include\\ntuser.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\win32ss\\user\\user32\\include\\ntwrapper.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\win32ss\\user\\user32\\include\\user_x.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\sdk\\include\\reactos\\undocuser.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\subsystems\\win32\\csrsrv\\status.h',
'F:\\windows-fuzzing\\ReactOS-0.4.11\\win32ss\\user\\ntuser\\cursoricon.h')

    for file in files:
        f = open(file)
        locate_function_to_search(f)

将转换后的结果粘贴到bughunt_syscalls.h中就可以对更多的系统调用进行fuzz了。当然毕竟没有反馈驱动,很难就这样跑出漏洞。
另外我在github上还找到一个对windows系统调用进行的fuzz的工具NtCall64(https://github.com/hfiref0x/NtCall64)。在NtCall64Main中首先解析命令行参数,之后进入FuzzInitPhase1函数进行一些准备工作,然后调用FuzzInitPhase2函数。根据是否指定-win32k参数fuzz W32pServiceTable(win32k.sys)或者fuzz KiServiceTable(ntoskrnl.exe)。

解析系统调用表要用到三个重要的符号:表的基地址、表的大小和参数在栈上占用的字节数。对于win32k.sys这些符号的名称分别是W32pServiceTable、W32pServiceLimit和W32pArgumentTable;对于ntoskrnl.exe这些符号的名称分别是KiServiceTable、KiServiceLimit和KiArgumentTable。
fuzz W32pServiceTable因为已经导出,所以可以直接调用GetProcAddress取得它们的地址。


fuzz KiServiceTable因为没有导出,所以是通过特征码定位实现的。


FuzzInitPhase2函数继续依次调用FuzzRunThreadWithWait->FuzzThreadProc->DoSystemCall-> ntSyscallGate函数,最后在syscall.asm中完成系统调用。

在DoSystemCall函数中,可以看到系统调用的参数是完全从fuzzdata数组中随机选择的。

总结

这篇文章分析了KernelFuzzer的原理,同时对其进行了简单的扩展,使其对更多的系统调用进行fuzz。同时还以NtCall64为例展示了不同的对windows系统调用进行fuzz的方法。有兴趣可以自行继续对它们进行完善。

参考资料

1.Platform Agnostic Kernel Fuzzing
2.https://www.thezdi.com/blog/2018/5/21/mindshare-walking-the-windows-kernel-with-ida-python

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


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