介绍
ProcessHacker的2.8.0.0版本的驱动程序kprocesshacker.sys
存在漏洞,该驱动是数字签名的,利用驱动漏洞可以在R0下结束进程、暂停进程,且可结束自我防护不严格的杀毒软件。
目的
以漏洞利用工具入手,分析漏洞点以增强知识理解和调试熟练度。
寻找漏洞点
先检验一下ProcessHacker能否杀火绒。
火绒直接被杀了且没有重新启动。
既然是结束进程,可能用到了ZwTerminateProcess
。
也有其他使用非公开内核函数的方法来结束进程,但一般正常的工具应该不会那样做,这种技术是非常稀有的。
为了证实使用的ZwTerminateProcess
这一点,可以调试一下结束进程操作是否真的调用了ZwTerminateProcess
.
我没有找到2.8.0.0版本驱动的ProcessHacker
的文件,用的是其他版本进行调试。
先找到函数偏移量0x68f1
当我尝试用其他版本的exe加载2.8.0.0的sys文件时,虽然exe可以正常使用,但是经过调试后发现sys文件似乎没有被调用,因为ProcessHacker.exe
用的自己而不是驱动来调用ZwTerminateProcess
,且驱动模块入口没有成功断下来,看来exe和sys版本是配对的。
如图,当我使用了其他版本的exe来使用2.8.0.0的驱动时,无法结束火绒。因为结束进程的操作没有从驱动层发起,而是从exe本身发起。
言归正传。如图在驱动模块的ZwTerminateProcess
下断点之后在ProcessHacker
执行结束进程时断住了。说明确实是调用的ZwTerminateProcess
来结束的进程
来看看漏洞成因。
搜索ZwTerminateProcess
可以看到被函数FUN_00017e78
调用了,那么FUN_00017e78
是不是造成漏洞的主要原因呢
在看FUN_00017e78
的反汇编之前先看看ZwTerminateProcess
函数原型ZwTerminateProcess
接受2个参数,第一个是进程的句柄,第二个是进程的退出状态。简单粗暴
NTSTATUS ZwTerminateProcess(
_In_ HANDLE ProcessHandle,
_In_ NTSTATUS ExitStatus
);
如果是结束进程可以被控制,那想必第一个参数ProcessHandle
肯定可以被控制。
FUN_00017e78
函数反汇编如下
ulonglong FUN_00017e78(undefined8 param_1,undefined4 param_2,undefined param_3)
{
longlong lVar1;
uint uVar2;
ulonglong uVar3;
longlong lVar4;
code *pcVar5;
longlong local_res20;
ulonglong uVar6;
undefined8 local_18 [2];
uVar6 = 0;
uVar3 = ObReferenceObjectByHandle
(param_1,0,*(undefined8 *)PsProcessType_exref,param_3,&local_res20,0);
if ((int)uVar3 < 0) {
return uVar3;
}
lVar4 = IoGetCurrentProcess();
lVar1 = local_res20;
if (local_res20 == lVar4) {
uVar3 = 0xc00000db;
}
else {
if ((DAT_00014334 == 0) && (pcVar5 = (code *)FUN_00011bac(&DAT_000141c0), pcVar5 != (code *)0x0)
) {
uVar2 = (*pcVar5)(lVar1,param_2);
uVar3 = (ulonglong)uVar2;
if (uVar2 != 0xc00000bb) goto LAB_00017f58;
}
uVar2 = ObOpenObjectByPointer
(local_res20,0x200,0,1,*(undefined8 *)PsProcessType_exref,
uVar6 & 0xffffffffffffff00,local_18);
uVar3 = (ulonglong)uVar2;
if (-1 < (int)uVar2) {
uVar2 = ZwTerminateProcess(local_18[0],param_2);
uVar3 = (ulonglong)uVar2;
ZwClose(local_18[0]);
}
}
LAB_00017f58:
ObfDereferenceObject(local_res20);
return uVar3;
}
代码读得头昏脑胀?没关系,这种事情可以交给GPT。
简而言之,FUN_00017e78
将第一个参数传入了ObReferenceObjectByHandle
,该函数是打开进程对象,并返回一个句柄。ObReferenceObjectByHandle
将返回的句柄存到了local_18
数组的第一个元素。而ZwTerminateProcess
接受local_18
的第一个元素作为结束的进程句柄传入。
所以只需要控制FUN_00017e78
函数的第一个参数或直接控制ZwTerminateProcess
传入参数就可以利用驱动直接结束进程。
再来看看利用工具是怎么实现的。
利用工具分析
https://github.com/MrEmpy/Reaper
工具几个函数如图
主要功能函数包括加载驱动、卸载驱动、暂停进程、结束进程。
来试一下工具的效果吧!
如下图杀了火绒2个进程,火绒已经被杀没了。
这个工具每次结束一个进程都会停止驱动,火绒存在多个进程,若要实战需要改一下程序。
总而言之,这是可以正确地杀死火绒的。
来用windbg跟一下。
首先在2.8.0.0版本的sys找到函数偏移量
根据偏移量在内存下断点
执行reaper工具,成功在预期函数处断下,rcx是进程句柄,rdx是退出状态。
这个工具首先使用DeployDriver
加载sys驱动并设置对象路径\.KProcessHacker2
, 然后通过指定的PID使用OpenProcess
获取要结束的进程句柄。
而最核心的在于这两行相应功能的IOCTL
编号,这也是漏洞存在的点
#define IOCTL_CODE_KILLPROCESS 0x999920df
#define IOCTL_CODE_SUSPENDPROC 0x999920d7
通过DeviceIoControl
函数操作驱动对象,往相应的IOCTL
编号发送了ioInput
结构体,这个结构体包含进程句柄和退出状态,直接操控了ZwTerminateProcess
函数的传参。
尾声
关于漏洞点,也就是IOCTL
编号为什么是这个,还需要进一步研究。
原文始发于微信公众号(XINYU2428):ProcessHacker驱动漏洞分析