Automatically Discovering Windows Kernel Information Leak Vulnerabilities

author : fanxiaocao(@TinySecEx) and @pjf_ of IceSword Lab , Qihoo 360


This Patch-Tuesday MS fixed 6 kernel information leak vulnerabilities reported by us, the details are at the end of this article.
I had already show how to fuzz the windows kernel via JS , today we will introduce a new method to discover windows kernel vulnerabilities automatically without fuzzing.
I selected a small part from the work in the past few months to spread out this topic.


In Windows Vista and above, Microsoft enable Kernel Address Space Layout Randomization (KASLR) by default to prevent exploitation by placing various objects at random addresses, rather than fixed ones. It is an effective method against exploitation using Return-oriented Programming (ROP) attack.

Beginning with Windows 8, KASLR is enhanced with a newly introduced function ExIsRestrictedCaller.
Programs under medium integrity are not able to invoke functions such as NtQuerySystemInformation to obtain addresses of kernel modules, kernel objects or pools.

The functions include but not limited to:


* SystemModuleInformation 
* SystemModuleInformationEx 
* SystemLocksInformation 
* SystemStackTraceInformation 
* SystemHandleInformation 
* SystemExtendedHandleInformation 
* SystemObjectInformation 
* SystemBigPoolInformation 
* SystemSessionBigPoolInformation 
* SystemProcessInformation
* SystemFullProcessInformation



The above is the traditional way to get the kernel module address and kernel object address, as the kernel normal feature.
But after win8, low integrity application will fail in calling these functions.

In order to bypass KASLR, a direct countermeasure is to discover vulnerabilities that leak valuable information from the kernel mode to calculate the address of kernel module or kernel object.

Kernel Information Leak

As a kind of kernel vulnerability, it has its own uniqueness. For example, for the traditional memory damage vulnerabilities, the vulnerability itself will affect the running of the kernel. With the help of verifier and other tools, you can easily capture this exception among the normal traffic.
But the kernel information leak vulnerability does not trigger any exception, nor does it affect the running of the kernel, which makes it more difficult to be discovered.
Vulnerabilities objectively exist, what we need to do is to find them at lowest cost.

Discover ideas

When kernel information leak vulnerability occurs, the kernel will certainly write some valuable data to the user buffer.
So if we monitor all the writing behaviors to user buffer in the kernel, we will be able to find them.

Of course, the system does not provide this feature.
I capture the process with the help of a hardware virtualization based framework of pjf,
who is the author of the famous windows kernel anti-rootkit tool named iceSword.

In order not to affect the dest system itself, I monitored in the VMWARE guest and write some log files, and then further analyze them in the host system.

In the host machine, after decoding and analyzing the logs:

Then we have the human-readable logs:

Further Analysis

Now we have operation records in user memory buffer written by kernel.
Most of them are just normal functions.

We need remove nosiy data to find out the key information.
Two skills are needed.

Poison the kernel stack

Poisoning or polluting the target is a common idea.
At network penetration testing, there are also ARP and DNS cache poisoning.

Here is the kernel stack poisoning, refers to the pollution to the entire unused kernel stack space.

If a variable on a kernel stack is not initialized, then when this variable is written to the user buffer, there will be a magic value in the record written by me. Wherever these is a magic value, there is a leak.

I noticed that j00ru also used similar techniques in his BochsPwn project.

KiFastCallEntry Hook

In order to poison the kernel stack, I hooked nt!KiFastCallEntry.
So that when a syscall invoked, I can poisoning the entire unused kernel stack space.

Firstly, I used IoGetStackLimits to get the current thread stack range, and then from the bottom of the stack to the current stack location of the entire space are filled with 0xAA.

So when I entered the syscall, all the contents of the local variables on the kernel stack will be filled into 0xAA.

Poison the kernel pool

Similarly, for dynamically allocated memory, I used hook nt!ExAllocatePoolWithTag and so on, and polluted its POOL content.

If the kernel stack/heap variable is not properly initialized, it is possible to write this magic value to the user buffer.

With the help of the logs we captured, we can immediately find this vulnerability.
In order to remove the coincidence, I also used a number of magic value such as 0xAAAAAAAA , 0xBBBBBBB to exclude false positives.

A typical result after excluding the interference is as follows.

You can see that in a short monitoring process, it caught the 161 leaks in the system!
Of course, this is not exhaustive. There are not so many independent vulnerabilities, but some vulnerabilities made repeated leaks.

At this point we caught a real information leak vulnerability, there is stack information, supplemented by a simple manual analysis, we can got the details.
This is also the story behind the CVE-2017-8482.

Difference comparison

For the kernel information leak caused by the uninitialized stack, we can poison them at first and then find them.
But for the direct disclosure of key information, such as the module and the object address written directly, it cannot be found in this way.

In the process of the system running, the kernel itself will frequently write data to the user buffer, a lot of data is in the kernel address range, but in fact it is not a valid address, but a noise data.
There are many such noise data, such as strings, pixels, rect, region, etc. which are likely happen to be a kernel address. We need to rule out the noise and found a real leak.

Here we filter out some meaningful addresses, such as:

  1. Module address, must be inside in the system module list
  2. object address
  3. POOL address

After the environment changes, such as restarting the system, it must be able to leak the same type of data at the same location.

After the exclusion of the normal function of the system, such as NtQuerySystemInformation and similar functions, the left data’s credibility is very high.

The leak of module address

For example CVE-2017-8485

You can see that the results at this time is very obvious - the same stack, the same location, are leaked nt! ObpReferenceObjectByHandleWithTag + 0x19f

The leak of object address

Due to leakage of object address and POOL address not fixed by Microsoft this month, I cannot describe the details.


You can see that we do not need a fuzzer, only through the code coverage generated by normal running of the system itself, we found these vulnerabilities.
Any normal program running can improve this coverage.
In fact, in the actual work, I only use the game and the browser to improve coverage and got good results.
A game finished, ten kernel vulnerabilities on the hand.

The case of this month






自动化挖掘 windows 内核信息泄漏漏洞

author : fanxiaocao(@TinySecEx) and @pjf_ of IceSword Lab , Qihoo 360


2017年6月补丁日,修复了我们之前报告的6个内核信息泄漏漏洞 , 文章末尾有细节。
前年我演示过如何用JS来fuzz 内核,今天我们要给大家带来的是不依赖fuzz,来自动化挖掘内核漏洞。


windows vista 之后,微软对内核默认启用了了ASLR ,简称KASLR.
KASLR 随机化了模块的加载基址 , 内核对象的地址等,缓解了漏洞的利用。

在win8 之后,这项安全特性的得到了进一步的增强。
引入 nt!ExIsRestrictedCaller 来阻止Low integrity 的程序调用某些可以泄漏出模块基址,内核对象地址等关键信息的函数。


* SystemModuleInformation 
* SystemModuleInformationEx 
* SystemLocksInformation 
* SystemStackTraceInformation 
* SystemHandleInformation 
* SystemExtendedHandleInformation 
* SystemObjectInformation 
* SystemBigPoolInformation 
* SystemSessionBigPoolInformation 
* SystemProcessInformation
* SystemFullProcessInformation



以上是传统的可以获取 内核模块地址和内核对象地址的方法 , 作为内核正常的功能。
但对于integrity 在medium 以下的程序,在win8 以后调用会失败。

KASLR 作为一项漏洞利用缓解措施,其中的一个目的就是为了使得构建通用的ROP-CHAIN 更为困难.











毒化或者说污染目标数据,是一种常见的思路。在网络攻防里,也有ARP 和DNS缓存的投毒。
那么在这个变量被写到到用户态时,写入的数据里就有我所标记的magic value ,找出这个magic value所在的记录,就是泄漏的发生点。
同时我注意到,j00ru 在他的BochsPwn项目里也曾使用了类似的技巧。

KiFastCallEntry Hook

为了有时机污染内核栈,我Hook 了KiFastCallEntry , 在每个系统调用发生时,污染当前栈以下剩余栈空间。

首先使用 IoGetStackLimits 获取当前线程的范围,然后从栈底部到当前栈位置的整个空间都被填充为0xAA 。


类似的,对于动态分配的内存,我采用hook ExAllocatePoolWithTag等,并污染其POOL内容的方式。

如果这个内核堆栈变量没有正确的初始化,就有可能将这个magic value写入到用户态的内存。结合我们捕获的日志,就能马上发现这个信息泄漏。

为了排除掉巧合,使用了多次变换magic value 如 0xAAAAAAAA , 0xBBBBBBBB 的办法来进行排除误报。


可以看到,在某次短暂的监控过程中,就抓到了系统里 161 次泄漏。
这也是 CVE-2017-8482 背后的故事。





  1. 模块地址,必须在内核模块地址范围内。
  2. object地址
  3. POOL地址

在环境改变,比如重启系统之后 ,必须还能在相同的位置泄漏相同类型的数据。

在排除掉系统正常的功能如 NtQuerySystemInformation 之类的之后,得到的数据,可信度就非常高了。


CVE-2017-8485 为例,比对之后得到的结果






事实上,在实际的挖掘过程中,我仅仅使用了运行游戏和浏览器的办法就取得了良好的效果 , 一局游戏打完,十个内核洞也就挖到了。