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

前言

上一篇基于论文讲解了信息泄露漏洞和bochspwn-reloaded的设计,这一篇讲解bochspwn-reloaded的代码和信息泄露漏洞的其它挖掘方法。

bochspwn-reloaded代码分析

下面我们来介绍bochspwn-reloaded的代码实现。首先来看一下代码的整体目录。

bochspwn-reloaded/third_party/instrumentation目录和bochspwn-reloaded/instrumentation目录下都有四个文件夹:linux-x86,windows-x64,windows-x86和windows-x86-markers。bochspwn-reloaded/configs目录下也有对应的四个配置文件:config-linux-x86.txt,config-windows-x64.txt,config-windows-x86.txt和config-windows-x86-markers.txt。

  • linux-x86:32位linux系统内核信息泄露漏洞检测
  • windows-x86:32位windows系统内核信息泄露漏洞检测
  • windows-x64:64位windows系统内核信息泄露漏洞检测
  • windows-x86-markers:检测内核内存中文件系统或网络等的泄漏

这里分析linux-x86/windows-x86/windows-x64这三个文件夹下的代码。如果我们要检测32位linux系统内核信息泄露漏洞,就需要拷贝bochspwn-reloaded/third_party/instrumentation目录和bochspwn-reloaded/instrumentation目录下的linux-x86文件夹中的内容到bochs-2.6.9/instrument中并编译,同时使用config-linux-x86.txt配置文件。
在bochspwn-reloaded/third_party/instrumentation目录下的文件夹都含有mem_interface.h和mem_interface.cc两个文件,它们提供read_lin_mem和write_lin_mem两个函数。从指定的虚拟地址读取或者写入数据。
在bochspwn-reloaded/instrumentation目录下的文件夹基本都含有下面这些文件:

  • breakpoints.cc\h:断点功能,由一个unordered_map管理(不是真正意义上的断点,是上一篇说的hook)。这部分代码基本都一样
  • common.cc\h:提供一些通用函数
  • instrument.cc\h:插桩功能
    基本上实现了下面这些插桩函数。

bx_instr_initialize\bx_instr_exit

分别实现初始化和清理。

bx_instr_interrupt

在发现漏洞之后如果设置了此时中断会调用invoke_guest_int3函数。

在invoke_guest_int3函数中保存当前地址中的值,然后向当前地址写入0xcc。

bx_instr_interrupt发生中断时将原来的值写入中断地址,设置全局标志。

bx_instr_before_execution

linux-x86

如果是push指令,检查是否是内存分配或释放相关函数的开头。

对于kmalloc和vmalloc函数,保存size和flags参数。

对于kfree,vfree和kmem_cache_free函数,清除污点。


对于kmem_cache_create函数,保存cache的size和构造函数指针。

对于kmem_cache_alloc函数,保存cache,size和flags参数。

对于kmem_cache_destroy函数,从内部结构中移除cache和cache的构造函数上的断点。

对于动态cache构造函数,标记污点(动态cache构造函数的地址是kmem_cache_create函数结尾时获得的,见下文)。

如果是ret指令,检查是否是内存分配相关函数的结尾。对于kmem_cache_create函数,如果函数执行成功,保存新创建的缓存的地址并在构造函数上设置一个断点。

对于其它的内存分配函数标记污点。

如果是内存拷贝指令,标记rep_movs标志为true;如果是改变内核栈的指令,标记esp_change标志为true并记录下ESP;如果是我们添加的prefetcht1和prefetcht1指令,对于prefetcht1指令将当前的ESP值记录到一个unordered_set中,对于prefetcht2指令将当前的ESP值从unordered_set中删除。

windows-x86

类似而且更加简单,因为只需要处理ExAllocatePoolWithTag函数的情况。

windows-x64

匹配上了配置文件中的memcpy_signature就调用handle_memcpy函数。

memcpy被编译为内联的mov指令序列的情况也调用handle_memcpy函数。

在handle_memcpy函数中,如果源地址和目的地址都是内核地址则进行污点传播;如果只有目的地址是内核地址说明是用户态向内核态写,清除污点;如果只有源地址是内核地址说明是内核态向用户态写,检查污点标记,如果所有的字节都和标记字节不同则清除污点,如果发现有未初始化的字节则进入漏洞报告逻辑。

标记内核栈是否发生改变。

池污点标记。遇到push指令如果是内存分配函数开头读取origin和size,存储在RSP作为key的hashmap中。

遇到ret指令如果是内存分配函数结尾从RAX寄存器中读取base address,由于RSP寄存器的值在进入和退出一个函数时不变,所以找到之前存储的origin和size,根据这些信息做污点标记。

bx_instr_wrmsr

只有windows-x64中用到了这个插桩函数。在config-windows-x64.txt中设置了pool_alloc_prologues和pool_alloc_epilogues的地址。

当写入的地址是MSR_LSTAR寄存器时代表系统调用,计算出内核基址,据此修改并设置pool_alloc_prologues和pool_alloc_epilogues。

bx_instr_after_execution

linux-x86

栈污点标记。

windows-x64

和linux-x86类似。

windows-x86

也和linux-x86类似,不过要处理xchg eax, esp和__SEH_prolog4/__SEH_prolog4_GS的特殊情况。

bx_instr_lin_access

linux-x86

如果在执行内存拷贝指令,拷贝的源地址和目的地址至少要有一个是内核态地址。接下来的逻辑和windows-x64中handle_memcpy函数的逻辑差不多。

如果是用户态向内核态写则标记为已初始化。

如果是在两个内核态地址之间则进行污点传播。

如果是内核态向用户态写并且存在未初始化的字节就报告可能存在的漏洞,如果所有的字节都和标记字节不同重新标记已初始化。

如果不是在执行内存拷贝指令,处理流程也和前面类似。

最后进入报告漏洞的代码。

windows-x86

和linux-x86类似。

windows-x64

内存在每次被任何不属于memcpy操作的指令覆盖时清除污点。

  • os_x.cc\h:一些特定于操作系统的功能
  • symbols.cc\h:windows操作系统上对日志信息符号化
  • taint.cc\h:污点功能
    在windows-x64中的taint.cc中的initialize函数中包括上一篇的文章中提到的使用异常分配机制实现的Memory overcommitment功能。

其它未初始化漏洞挖掘方法

静态分析

对于linux可以进行源代码分析,对于windows可以进行二进制分析。开发挖掘未初始化漏洞的源代码分析的工具可能会简单一点,但是开发挖掘未初始化漏洞的二进制分析的工具可能难度就比较大了。

代码审计

对于linux可以查找所有调用copy_to_user的函数进行审计,对于windows可以查找ProbeForWrite函数的上下文。研究人员通过这种方式找到了windows内核中的两个未初始化漏洞。

不同内核版本比较

因为微软对于win 7这样比较老的操作系统打补丁不是很积极,所以如果win10上有memset而win7相同的地方没有,可能就是在win10默默修了个未初始化漏洞。研究人员通过这种方式也找到了影响老版本windows内核的两个未初始化漏洞。

进行两次系统调用

这个方法就是说如果每块新分配内存中的字节都随着时间变化,那么用户态程序可以分析两次系统调用的输出,寻找不相等但公共偏移量相同的字节。存在这样的字节说明可能存在信息泄露漏洞。研究人员对windows操作系统含有NtQuery前缀的函数进行了这种fuzz,发现了多个漏洞。

没有污点追踪的类似bochspwn的插桩

和bochspwn-reloaded类似的全系统插桩应该能够在没有污点追踪的情况下检测未初始化内存造成的信息泄露,只要它能够检查所有源自内核的用户态内存写。一种方法就是分析系统调用的输出,搜索像内核地址这样不应该在其中出现的信息。或者可以给每块新分配内存中标记特殊字节,在内核态写入到用户态的数据中查找这些特殊字节。冰刃实验室的digtool大致就是这样的原理,也和Project Zero撞了很多洞,有兴趣可以看一下他们的论文。

参考资料

1.Bochspwn漏洞挖掘技术深究(2):内核未初始化漏洞检测
2.Detecting Kernel Memory Disclosure with x86 Emulation and Taint Tracking
3.Bochspwn Revolutions: Further Advancements in Detecting Kernel Infoleaks with x86 Emulation
4.Bochspwn Reloaded: Detecting Kernel Memory Disclosure with x86 Emulation and Taint Tracking

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖