Home > front end >  How is an integrity level SID constructed?
How is an integrity level SID constructed?

Time:01-21

I feel like I'm missing something obvious but I've been stuck on this for hours. I am attempting to call SetTokenInformation on a duplicate primary token of the current process, but it fails with 0x5 (Access denied).

I'm trying to create a child process with no privileges. Then I want to adjust the integrity level of the token to low.

Flow of execution in the minimal example:

  1. We enable every single privilege. All of them.

  2. We open the current process with PROCESS_ALL_ACCESS, and open the process token with TOKEN_ALL_ACCESS

  3. We get a duplicate token for our current process.

  4. We attempt to change the mandatory integrity level of the token, and we get Access denied.

Please note this is for a minimal example, I know the above would be very bad practice in production code. We are simply eliminating the possibility of permission issues.

Privileges:

enter image description here

Output:

enter image description here

What could I be missing here? I have tried everything I can think of:

  • Debugged it to verify the method being called is correct and the arguments on the stack are all valid

  • Verified arguments and return values for every function in the program

  • Tried every process and token access/duplication flag

  • Minimum 2 hours of Googling for random info with "StackOverflow ... token" (this one is a joke, but I really did)

etc.

So I figure it has to be a problem with the SID itself. But I have been able to find nothing on the internet about how this SID is constructed.

MinimalMain.cpp:

// TokenMinimalExample.cpp : This file contains the 'main' function. Program execution begins and ends there.

#include <windows.h>
#include <stdio.h>
#include "token_example.h"

int main()
{
    // Enable every privilege under the sun.
    if (!EnableEveryPrivilegeUnderTheSun())
    {
        printf("Failed to enable every privilege under the sun: %d\n", GetLastError());

        return 1;
    }
    
    // Get a duplicate token for the current process
    HANDLE hToken = GetCurrentProcessPrimaryToken();

    if (hToken == INVALID_HANDLE_VALUE)
    {
        printf("Failed to get a duplicate token for the current process: %d\n", GetLastError());

        return 1;
    }
    
    
    // Create a mandatory low integrity SID
    SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_MANDATORY_LABEL_AUTHORITY;
    PSID pSid = NULL;
        
    // Can be low, med, high, system, they're all 0x5
    if(!AllocateAndInitializeSid(&SIDAuth, 1, SECURITY_MANDATORY_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSid))
    {
        printf("Failed to create a mandatory low integrity SID: %d\n", GetLastError());
        system("PAUSE");
        return 1;
    }
    
    // Change the integrity level of the duplicate token
    TOKEN_MANDATORY_LABEL tml;
    tml.Label.Attributes = SE_GROUP_INTEGRITY;
    tml.Label.Sid = pSid;

    if (!SetTokenInformation(hToken, TokenIntegrityLevel, &tml, sizeof(tml)))
    {
        // Access denied ??????
        printf("Failed to set token information: %d\n", ::GetLastError());
        system("PAUSE");
        return 1;
    }
    system("PAUSE");
    return 0;
}

token_example.h:

#pragma once
#include <Windows.h>
#include <stdio.h>

// Helper function to adjust our processes current privileges
bool SetThreadProcessPrivilege(LPCWSTR PrivilegeName, bool Enable)
{
    HANDLE Token;
    TOKEN_PRIVILEGES TokenPrivs;
    LUID TempLuid;
    bool Result;

    if (!LookupPrivilegeValueW(NULL, PrivilegeName, &TempLuid))  return false;

    if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &Token))
    {
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &Token))  return false;
    }

    TokenPrivs.PrivilegeCount = 1;
    TokenPrivs.Privileges[0].Luid = TempLuid;
    TokenPrivs.Privileges[0].Attributes = (Enable ? SE_PRIVILEGE_ENABLED : 0);

    Result = (AdjustTokenPrivileges(Token, FALSE, &TokenPrivs, 0, NULL, NULL) && ::GetLastError() == ERROR_SUCCESS);

    // Even if AdjustTokenPrivileges returns TRUE, it may not have succeeded
    // check last error top confirm
    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
    {
        printf(" Unable to set privilege: %S Error: %d \n", PrivilegeName, GetLastError());
        CloseHandle(Token);
        return FALSE;
    }

    CloseHandle(Token);

    return Result;
}

// Test method, trying to figure out if I was missing any privileges
bool EnableEveryPrivilegeUnderTheSun()
{
    if (!SetThreadProcessPrivilege(L"SeBackupPrivilege", true))
    {
        printf("Failed to enable SeBackupPrivilege: %d\n", GetLastError());

        return false;
    }

    if (!SetThreadProcessPrivilege(L"SeRestorePrivilege", true))
    {
        printf("Failed to enable SeRestorePrivilege: %d\n", GetLastError());

        return false;
    }

    if (!SetThreadProcessPrivilege(L"SeIncreaseQuotaPrivilege", true))
    {
        printf("Failed to enable SeIncreaseQuotaPrivilege: %d\n", GetLastError());

        return false;
    }

    if (!SetThreadProcessPrivilege(L"SeAssignPrimaryTokenPrivilege", true))
    {
        printf("Failed to enable SeAssignPrimaryTokenPrivilege: %d\n", GetLastError());

        return false;
    }

    if (!SetThreadProcessPrivilege(L"SeTcbPrivilege", true))
    {
        printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());

        return false;
    }

    if (!SetThreadProcessPrivilege(L"SeDebugPrivilege", true))
    {
        printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());

        return false;
    }

    if (!SetThreadProcessPrivilege(L"SeSecurityPrivilege", true))
    {
        printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());

        return false;
    }

    if (!SetThreadProcessPrivilege(L"SeSystemtimePrivilege", true))
    {
        printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());

        return false;
    }

    if (!SetThreadProcessPrivilege(L"SeShutdownPrivilege", true))
    {
        printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());

        return false;
    }

    if (!SetThreadProcessPrivilege(L"SeSystemEnvironmentPrivilege", true))
    {
        printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());

        return false;
    }
    

    if (!SetThreadProcessPrivilege(L"SeTakeOwnershipPrivilege", true))
    {
        printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());

        return false;
    }
    
    if (!SetThreadProcessPrivilege(L"SeLoadDriverPrivilege", true))
    {
        printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());

        return false;
    }
    
    if (!SetThreadProcessPrivilege(L"SeManageVolumePrivilege", true))
    {
        printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());

        return false;
    }

    return true;
}

// Helper function to get a duplicate primary token for the current process
HANDLE GetCurrentProcessPrimaryToken()
{
    HANDLE tempproc;
    HANDLE realtoken, duplicatetoken;
    DWORD accessmode = TOKEN_ALL_ACCESS;

    // Enable SeDebugPrivilege.
    SetThreadProcessPrivilege(L"SeDebugPrivilege", true);

    // Open a handle to the process.
    tempproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());

    if (tempproc == NULL)
    {
        printf("OpenProcess failed with error %d\n", GetLastError());

        return INVALID_HANDLE_VALUE;
    }

    if (!OpenProcessToken(tempproc, accessmode, &realtoken))
    {
        printf("OpenProcessToken failed with error %d\n", GetLastError());

        CloseHandle(tempproc);

        return INVALID_HANDLE_VALUE;
    }

    CloseHandle(tempproc);
    
    SECURITY_ATTRIBUTES secattr = { 0 };
    secattr.nLength = sizeof(secattr);
    secattr.bInheritHandle = FALSE;
    secattr.lpSecurityDescriptor = NULL;

    if (!DuplicateTokenEx(realtoken, MAXIMUM_ALLOWED, &secattr, SecurityImpersonation, TokenPrimary, &duplicatetoken))
    {
        printf("DuplicateTokenEx failed with error %d\n", GetLastError());

        CloseHandle(realtoken);

        return INVALID_HANDLE_VALUE;
    }

    CloseHandle(realtoken);
        
    return duplicatetoken;
}

CodePudding user response:

Not sure why you cannot assign the system IL if you are running as System but the RID you are really looking for is SECURITY_MANDATORY_LOW_RID:

HANDLE hToken, hPrimTok = 0;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, &hToken))
{
    if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, 0, SecurityAnonymous, TokenPrimary, &hPrimTok)) hPrimTok = 0;
    CloseHandle(hToken);
}
if (hPrimTok)
{
    SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_MANDATORY_LABEL_AUTHORITY;
    TOKEN_MANDATORY_LABEL tml;
    tml.Label.Attributes = SE_GROUP_INTEGRITY;
    if (AllocateAndInitializeSid(&SIDAuth, 1, SECURITY_MANDATORY_LOW_RID, 0, 0, 0, 0, 0, 0, 0, &tml.Label.Sid))
    {
        if (!SetTokenInformation(hPrimTok, TokenIntegrityLevel, &tml, sizeof(tml)))
            printf("SetTokenInformation failed, %u\n", GetLastError());
        else
        {
            STARTUPINFOA si = { sizeof (STARTUPINFO) };
            PROCESS_INFORMATION pi;
            if (CreateProcessAsUserA(hPrimTok, 0, "Notepad.exe", 0, 0, FALSE, 0, 0, 0, &si, &pi))
            {
                // CloseHandle *2
            }
        }
        FreeSid(tml.Label.Sid);
    }
    CloseHandle(hPrimTok);
}

Please note that you should also call CreateRestrictedToken to really restrict the new process. Disable all groups and privileges you don't need.

  • Related