Elevated rights, process token and security identifier

When writing malware it becomes essential that the software is running under all circumstances. Especially today we have a variety of opearting systems and versions, 2000 up to 7, 32-bit and 64-bit. The thing that worry malware developer more than the diversity of OSes is user/admin rights and elevated or non-elevated under Vista and 7. The optimal malware works under all systems and at least has a known behaviour on different rights of the executed process.

Peter Kleissner, Software Engineer

Elevated rights, non-elevated rights & the UAC

In Vista and 7 you can specify in a manifest if your application requires elevated rights, and if UAC is activated Windows shows the Consent UI and if you have only user rights then the Credential UI is shown. This is option is bad for malware because it has no control of whether shown or not. Instead it can use the API to check if the process has elevated using a process token:

    // check Windows version
    OSVERSIONINFO VersionInfo;
    VersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx(&VersionInfo);

    if (VersionInfo.dwMajorVersion >= 6)
    {
        // check if elevated on Vista and 7
        HANDLE Token;
        TOKEN_ELEVATION Elevation;      // Token type only available with Vista/7
        DWORD ReturnSize;

        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &Token)  ||
            !GetTokenInformation(Token, TokenElevation, &Elevation, sizeof(Elevation), &ReturnSize))
            return 0;
        
        if (!Elevation.TokenIsElevated)
        {
            // process is not elevated
        }
    }

The token "TokenElevation" is only available with Vista, so there must be first the operating system version checked. Using GetTokenInformation the information if elevated or not can be retrieved. Subsequently (if elevated) it can be checked through a security identifier if the user has administrator rights:

BOOL IsUserAdmin()
{
    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
    PSID SecurityIdentifier;
    if (!AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &SecurityIdentifier))
        return 0;

    BOOL IsAdminMember;
    if (!CheckTokenMembership(NULL, SecurityIdentifier, &IsAdminMember))
        IsAdminMember = FALSE;

    FreeSid(SecurityIdentifier);
    return IsAdminMember;
}

Bypassing UAC

One method to bypass the UAC is writing a Windows Service that does the elevation (it duplicates the security token of winlogon.exe and starts a new process by CreateProcessAsUser). A legal way to get elevated rights is to start another process with ShellExecute() and lpVerb = "runas", however, the UAC will still prompt. This gives the social engineering vulnerability I discussed shortly at Black Hat USA 2009 to call ShellExecute in a loop until the user clicks "Yes":

wchar_t FileName[MAX_PATH];
GetModuleFileName(NULL, FileName, MAX_PATH);

SHELLEXECUTEINFO Info;
Info.hwnd = NULL;
Info.cbSize = sizeof(Info);
Info.fMask = NULL;
Info.hwnd = NULL;
Info.lpVerb = L"runas";
Info.lpFile = FileName;
Info.lpParameters = NULL;
Info.lpDirectory = NULL;
Info.nShow = SW_HIDE;
Info.hInstApp = NULL;
while (!ShellExecuteEx(&Info));

References