5

I'm trying to create a Medium or Low integrity process from a elevated process. I know there are other questions like this but they mostly focus on the workarounds like using Explorer or the Task Scheduler and I want to stick with CreateRestrictedToken()+CreateProcessAsUser().

I assume it must be possible to do this somehow since I believe UAC does it when you log in but I have not been able to get everything in the token to look like the normal UAC Medium IL token.

You can get 80% there by creating the token with CreateRestrictedToken(hThisProcessToken, LUA_TOKEN, ...) and then setting TokenOwner, TokenDefaultDacl and TokenIntegrityLevel before calling CreateProcessAsUser().

The remaining issues are TokenVirtualizationAllowed, TokenVirtualizationEnabled, TokenElevation, TokenElevationType and TokenMandatoryPolicy where SetTokenInformation() fails with ERROR_PRIVILEGE_NOT_HELD or ERROR_INVALID_PARAMETER.

If I run as SYSTEM @ SECURITY_MANDATORY_SYSTEM_RID with all privileges enabled as opposed to an Administrator @ SECURITY_MANDATORY_HIGH_RID then I'm able to set TokenMandatoryPolicy and TokenVirtualization* but setting TokenElevation* still fails! (Only tested on Windows 8 so far)

Not having the correct TokenElevation* values in the token is a big issue because Internet Explorer fails to start in Protected Mode because it thinks the token is elevated.

The documentation for SetTokenInformation() does not say which TOKEN_INFORMATION_CLASS items it is possible to set and which privileges, if any, are required and I don't understand why you would not be allowed to set these to lower security values that match the actual integrity level (TokenIntegrityLevel) of the token.

Using the Safer API to create a SAFER_LEVELID_NORMALUSER token does not fix any of these issues and also creates a token that is more restricted than the normal Medium IL token.

I found somebody with a similar issue from the early Vista/Longhorn days, has nothing changed since then?

Anders
  • 97,548
  • 12
  • 110
  • 164
  • Have you tried setting TokenLinkedToken? It's possible that the result of querying TokenElevation* is determined by the linked token status. Although there's probably already a restricted token linked to your elevated token, so that could be a complication. You should probably also note that when UAC generates the restricted token, it starts by logging the user in a second time - there are separate logon events for the elevated and restricted tokens. I don't know if the second logon is *necessary* in order to generate a fully functional restricted token, but it might be. – Harry Johnston Apr 21 '16 at 00:24
  • (I'm also not sure whether there will be any way to put the restricted token you're synthesizing into the same logon session as the rest of the user's restricted processes. If not, that will impact things like network drives. I'm not sure whether there are also security implications or not. Bottom line, using a synthesized token just isn't a good idea under normal circumstances, though there may be edge cases.) – Harry Johnston Apr 21 '16 at 00:27
  • 1
    @Harry You bring up some valid issues but you would think LUA_TOKEN would take care of those, after all it is documented as "The new token is a LUA token" and LUA = Limited/Least-Privilege User Account. It is of course possible that all this flag does is to use the UAC equivalent entries for SidsToDisable and PrivilegesToDelete but that renders the flag mostly useless because most processes fail to run unless you adjust some of the token attributes yourself and you never end up with a really usable token if you start at >= High IL. – Anders Apr 21 '16 at 16:22
  • I think it's worth trying SetTokenInformation with TokenLinkedToken to see what, if anything, happens. Also you might want to try this, and redo your other experiments, on a machine with UAC turned off or when logged in as the local Administrator account, i.e., in an account context where a restricted token hasn't already been created - on the grounds that the LSASS might not allow more than one restricted token per elevated token. – Harry Johnston Apr 21 '16 at 22:22
  • Setting TokenLinkedToken gives me ERROR_PRIVILEGE_NOT_HELD, giving up for now. – Anders May 24 '16 at 13:57
  • 2
    too late of course (only now view this), but solution in - first get *SeTcbPrivilege* and after this query *TokenLinkedToken*. you got primary token, if you have *SeTcbPrivilege*. (usually exist linked session if you have *ElevationType==Full*) this is most exactly solution. and then you need *SeAssignPrimaryTokenPrivilege* for *CreateProcessAsUserW* with this token. – RbMm Dec 16 '19 at 12:31

1 Answers1

2
function CreateLowProcess(szProcessName: WideString; const IntegritySid: UnicodeString=''): Boolean;
var
    hToken: THandle;
    hNewToken: THandle;
    szIntegritySid: WideString;
    pIntegritySid: PSID;
    TIL: TOKEN_MANDATORY_LABEL;
    ProcInfo: PROCESS_INFORMATION;
    startupInfo: TStartupInfo;
const
    SE_GROUP_INTEGRITY = $00000020;
    TokenIntegrityLevel = 25;
    SLowIntegritySid:    UnicodeString = 'S-1-16-4096';
    SMediumIntegritySid: UnicodeString = 'S-1-16-8192';
    SHighIntegritySid:   UnicodeString = 'S-1-16-12288';
    SSystemIntegritySid: UnicodeString = 'S-1-16-16384';
begin
    {
        Designing Applications to Run at a Low Integrity Level
        http://msdn.microsoft.com/en-us/library/bb625960.aspx

        To start a low-integrity process:

        - Duplicate the handle of the current process, which is at medium integrity level.
        - Use SetTokenInformation to set the integrity level in the access token to Low.
        - Use CreateProcessAsUser to create a new process using the handle to the low integrity access token.

        CreateProcessAsUser updates the security descriptor in the new child process and the security descriptor
        for the access token to match the integrity level of the low-integrity access token.
    }

    // Low integrity SID
    if IntegritySid <> '' then
        szIntegritySid := IntegritySid
    else
        szIntegritySid := SLowIntegritySid;
//  szIntegritySid  := 'S-1-5-32-545'; //BUILTIN\USERS

    ZeroMemory(@startupInfo, sizeof(startupInfo));

    if (not OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE or TOKEN_ADJUST_DEFAULT or TOKEN_QUERY or TOKEN_ASSIGN_PRIMARY,
                {var}hToken)) then
        RaiseLastOSError;
    try
        if (not DuplicateTokenEx(hToken, 0, nil, SecurityImpersonation, TokenPrimary, {var}hNewToken)) then
            RaiseLastOSError;
        try
            pIntegritySid := StringToSid(szIntegritySid); //free with LocalFree
            try
                TIL._Label.Attributes := SE_GROUP_INTEGRITY;
                TIL._Label.Sid := pIntegritySid;

                // Set the process integrity level
                if (not SetTokenInformation(hNewToken, TTokenInformationClass(TokenIntegrityLevel), @TIL,
                        sizeof(TOKEN_MANDATORY_LABEL) + GetLengthSid(pIntegritySid))) then
                    RaiseLastOSError;

                //Create the new process at Low integrity
                Result := CreateProcessAsUserW(
                        hNewToken,
                        nil,
                        PWideChar(szProcessName),
                        nil, //ProcessAttributes
                        nil, //ThreadAttributes
                        False, //bInheritHandles
                        0, //dwCreationFlags
                        nil, //lpEnvironment
                        nil, //lpCurrentDirectory
                        startupInfo,
                        ProcInfo);
            finally
                LocalFree(HLOCAL(pIntegritySid));
            end;
        finally
            CloseHandle(hNewToken);
        end;
    finally
        CloseHandle(hToken);
    end;
end;
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219