利用差分重放技术检测未初始化的变量

利用差分重放技术检测未初始化的变量

Different is Good: Detecting the Use of Uninitialized Variables through Differential Replay


Remark

Conference: CCS 2019

Full Paper: malgenomeproject.org/pa


Summary

In this paper, the authors propose differential replay, a new technique for detecting the use of uninitialized variables without source code. The main idea of this paper is to replay two different instances with vanilla and poisoned values of stack and heap memory, respectively. The difference in program states could indicate the presence of the use of uninitialized variables. To determine the location where the variable was allocated, symbolic taint analysis is conducted. Experiments are carried out on 119 kernel information leaks in Windows operating systems, compared to the state-of-art kernel memory disclosure detector Bochspwn Reloaded and a reference system that purely leverages the taint analysis to track uninitialized variables.


(+) Significance: The use of uninitialized variables could cause kernel information leaks. However, Modern compilers are limited to a single function and fail to deal with arrays, pointers and loops. Existing commercial tools also suffer from high false positives. The technique presented by the authors can analyze binaries without source code, with none false positives.

(+) Novelty: The authors suggest recording and replay a program's execution in multiple instances. If the states in two running instances are different, there may exist uninitialized variables. The symbolic taint analysis is only used to further identify the location where the variable was allocated after uninitialized variables have been detected.

(+) Soundness: Experiments are conducted with Windows kernels compared to the state-of-art tool Bochspwn Reloaded, successfully identifying 34 new issues and another 85 ones that had been patched.


Introduction

Q: 为什么使用未初始化变量会带来很大的危害?

A: 在Windows和Linux等现代操作系统中,变量的值通常在被显式初始化之前是不确定的。这些未初始化的变量可能会危及系统的安全性,特别是当它们跨越不同的权限域时。内核使用内核地址空间布局随机化(KASLR)机制,如果一块内存中存放着kernel object的地址,使用那块内存的变量未进行初始化而泄漏到用户空间,将损害KASLR的安全性。攻击者可以根据泄漏的信息进行权限提升。


Q: 为什么检测未初始化变量很困难?

A:

静态分析:

  • 现代编译器(gcc, clang, etc.)具备简单的检测功能。但大多局限于一个函数,且对于数组、指针、循环、结构体、union等无能为力。
  • 需要源码

动态分析:

  • 首先使用未初始化变量这类内存泄漏问题通常不会导致crash或很明显的影响。
  • 一种naive的思路是:进行全系统的动态污点分析,在分配栈或堆时设置source,被赋值时去除污点标记,如果程序使用了一块tainted的内存,则可以确定使用了未初始化变量。
  • 但进行全系统的污点分析会带来极大的overhead
  • 目前最好的工具Bochspwn Reloaded仅对特定类型指令进行污点传播,从而降低overhead,但导致了大量的漏报。
  • 其他内存检测工具如MemorySanitizer等,需要基于源码插桩,且仅适用于用户态


Q: 作者的方案?

A: 作者的方案是通过差分重放来检测未初始化变量的使用,overhead比上述动态分析方案大幅度降低。当检测到未初始化变量的使用时,再利用符号化污点分析技术,定位未初始化变量的source(变量在哪分配的)。


Motivating Example

如下图所示,作者用一个内核信息泄漏漏洞(CVE-2018- 8408)作为例子,展示目前工具的局限性,以及作者工具发现该漏洞的过程。


图中截取了三条关键的汇编指令。

  • 第一条汇编指令(01),分配栈上变量,未进行初始化
  • 第一条汇编指令(02),将其中4个字节存入ecx寄存器
  • 第一条汇编指令(03),将ecx的内容存入eax+4的位置,而此时eax指向的是用户空间



Q: 为什么Bochspwn无法发现这个漏洞?

A:Bochspwn为了避免过高的overhead,仅对特定种类的指令进行污点传播,(其对memory to memory的指令进行污点传播,而不对memory to reg的指令进行污点传播),当受污染的数据流入寄存器时,标记将丢失(这确实是此漏洞的情况),所以污点无法进入ecx,从而无法被Bochspwn发现。


Q: 为什么本文的方法可以发现这个漏洞?

A: 首先通过确定性差分重放,观察到的两个重放实例的不同内存状态,一个是正常重播实例,另一个是堆栈内存值为0xaa的重播实例。如下图所示,执行到0x83e24501 mov dword ptr [eax + 4], ecx时,对比地址0x021afad8发现差异:



接下来利用符号化污点分析技术,定位未初始化变量的source(变量是在哪里被分配):

  • 利用stack trace记录的信息,往前N个栈帧开始dump trace
  • 再对trace进行符号化污点分析
  • 遇到sub esp, eax、函数ExAllocatePoolWithTag等栈、堆分配指令时,新建一个符号并打上taint标记
  • 遍历指令,传递污点标记。遇到被赋值操作,则去除对应符号的污点标记。
  • 污点分析到0x83e24501 mov dword ptr [eax + 4], ecx时,查看ecx对应的符号即可知道其是在哪里被分配的。



Approach



Key technique I: 差分重放:

(1)使用全系统模拟器记录内核和用户态的运行

(2)确定性重放

(3)对比检测到差异

Key technique II: 符号化污点分析:

(4)在检测到差异的指令回退N个栈帧,利用得到的trace,开始符号化污点分析

(5)进行污点传播

(6)到达检测到差异的指令,确定泄漏内存被分配的位置


running example




Evaluation

实验主要解答以下三个研究问题:

  • RQ1: Effectiveness: Can TimePlayer detect new vulnerabilities, and perform better than the state-of-the-art tool?
  • RQ2: Efficiency: Can TimePlayer quickly detect the vulnerabilities?
  • RQ3: Performance overhead: What is the performance overhead?


Personal Thoughts

通过差分重放找未初始化变量的思路很好,这套系统还是需要靠输入触发漏洞,或许可以跟fuzzer相结合。

发布于 04-16

文章被以下专栏收录