Sunday, November 13, 2022

UI Windows Sandboxing

What I love about programming is that system programming can strike you back even if you are writing a simple desktop UI tool. For example, if you want it to be more secure, as I do. (it is Fancy Viewer tool in my case https://www.fancy-viewer.com/)

The tool uses ImageMagick library (plus plugins) which I completely trust, but vulnerabilities happen and it is better to run the parsers in some isolated environment, i.e. sandbox. 

There are some Windows API functions for that:

CreateRestrictedToken
CreateProcessAsUserW
which work as charm:

Except you have to rewrite token's default DACL or the app refuses to start on Windows 7 (or on Windows 10 with "run as administrator").

Like here:

BOOL CreateProcessRestrictedW(LPWSTR lpCommandLine,
    LPSTARTUPINFOW lpStartupInfo,
    BOOL bInheritHandles,
    DWORD dwCreationFlags,
    LPVOID lpEnvironment,
    LPCWSTR lpCurrentDirectory,
    OUT LPPROCESS_INFORMATION lpProcessInformation)
{
    HANDLE hProcessToken = 0, hRestrictedToken = 0;

    if (!OpenProcessToken(GetCurrentProcess(),
        TOKEN_ALL_ACCESS | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY,
        &hProcessToken))
    {
        return FALSE;
    }
    lsvu::HandleGuard processTokenGuard(hProcessToken);

    // get current user sid
    std::vector<char> currentUserSID;
    if (QueryTokenSID_Silent(hProcessToken, &currentUserSID))
    {
        return FALSE;
    }

    // collect other system sids
    std::vector<char> adminSID;
    if (GetWellKnownSid_Silent(WinBuiltinAdministratorsSid, adminSID))
    {
        return FALSE;
    }
    std::vector<char> localSystemSID;
    if (GetWellKnownSid_Silent(WinLocalSystemSid, localSystemSID))
    {
        return FALSE;
    }

    SID_AND_ATTRIBUTES sidToDisable = { adminSID.data(), 0 };

    // create the restricted token
    if (!CreateRestrictedToken(hProcessToken,
        DISABLE_MAX_PRIVILEGE | LUA_TOKEN,
        1, &sidToDisable,
        0, 0,
        0, 0,
        &hRestrictedToken))
    {
        return FALSE;
    }
    lsvu::HandleGuard restrictedTokenGuard(hRestrictedToken);

    // Set the token to low integrity:
    TOKEN_MANDATORY_LABEL tokenLabel = { 0 };
    tokenLabel.Label.Attributes = SE_GROUP_INTEGRITY;
    if (!ConvertStringSidToSidW(L"S-1-16-4096", &tokenLabel.Label.Sid))
    {
        return FALSE;
    }
    {
        LocalGuard sidLabelGuard(tokenLabel.Label.Sid);
        if (!SetTokenInformation(hRestrictedToken,
            TokenIntegrityLevel,
            &tokenLabel,
            sizeof(tokenLabel) + GetLengthSid(tokenLabel.Label.Sid)))
        {
            return FALSE;
        }
    }

    // Create new DACL
    std::vector<char> dacl;
    if (CreateACL_Silent(dacl, currentUserSID, adminSID, localSystemSID))
    {
        return FALSE;
    }
    
    TOKEN_DEFAULT_DACL newDefaulDACL = { (PACL)dacl.data() };
    if (!SetTokenInformation(hRestrictedToken, TokenDefaultDacl, 
&newDefaulDACL, sizeof(newDefaulDACL))) { return FALSE; } // Create a new process using the restricted token BOOL result = CreateProcessAsUserW(hRestrictedToken, NULL, lpCommandLine, NULL, NULL, bInheritHandles, dwCreationFlags, (LPVOID)lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); return result; } 
 

It isn't a full compilable source, but demonstrates the idia well enough.

No comments: