10

My prototype code works fine on every Windows OS I've been able to test on, except Windows XP.

When I run this on Windows XP as Administrator, I'm given Access is denied (5) when calling OpenProcessToken.

Is there some difference I'm not aware of?

#include "stdafx.h"
#include <Windows.h>
#include <userenv.h>

#pragma comment(lib, "userenv")

void DisplayError(LPWSTR pszAPI)
{
    LPVOID lpvMessageBuffer;

    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, GetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPWSTR)&lpvMessageBuffer, 0, NULL);

    wprintf(L"ERROR: API        = %s.\n", pszAPI);
    wprintf(L"       error code = %d.\n", GetLastError());
    wprintf(L"       message    = %s.\n", (LPWSTR)lpvMessageBuffer);

    LocalFree(lpvMessageBuffer);

    ExitProcess(GetLastError());
}

void SetDebugPrivileges()
{
    void* tokenHandle;

    TOKEN_PRIVILEGES privilegeToken;
    LookupPrivilegeValue(0, SE_DEBUG_NAME, &privilegeToken.Privileges[0].Luid);
    privilegeToken.PrivilegeCount = 1;
    privilegeToken.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle);
    AdjustTokenPrivileges(tokenHandle, 0, &privilegeToken, sizeof(TOKEN_PRIVILEGES), 0, 0);
    CloseHandle(tokenHandle);
}

void wmain(int argc, WCHAR *argv[])
{
    DWORD     dwSize;
    HANDLE    hToken;
    LPVOID    lpvEnv;
    PROCESS_INFORMATION pi = { 0 };
    STARTUPINFO         si = { 0 };
    WCHAR               szUserProfile[256] = L"";

    si.cb = sizeof(STARTUPINFO);

    if (argc != 4)
    {
        wprintf(L"Usage: %s [user@domain] [password] [cmd]", argv[0]);
        wprintf(L"\n\n");
        return;
    }

    if (!LogonUser(argv[1], NULL, argv[2], LOGON32_LOGON_INTERACTIVE,
        LOGON32_PROVIDER_DEFAULT, &hToken))
        DisplayError(L"LogonUser");

    if (!CreateEnvironmentBlock(&lpvEnv, hToken, TRUE))
        DisplayError(L"CreateEnvironmentBlock");

    dwSize = sizeof(szUserProfile) / sizeof(WCHAR);

    if (!GetUserProfileDirectory(hToken, szUserProfile, &dwSize))
        DisplayError(L"GetUserProfileDirectory");

    if (!CreateProcessWithLogonW(argv[1], NULL, argv[2],
        LOGON_WITH_PROFILE, NULL, argv[3],
        CREATE_UNICODE_ENVIRONMENT, lpvEnv, szUserProfile,
        &si, &pi))
        DisplayError(L"CreateProcessWithLogonW");

    if (!DestroyEnvironmentBlock(lpvEnv))
        DisplayError(L"DestroyEnvironmentBlock");

    //Sleep(5000);

    SetDebugPrivileges();

    HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pi.dwProcessId);
    if(process == NULL)
        DisplayError(L"OpenProcess");

    // Not working on Windows XP
    HANDLE token;
    if(!OpenProcessToken(process, TOKEN_QUERY, &token))
        DisplayError(L"OpenProcessToken");

    CloseHandle(token);
    CloseHandle(process);
    CloseHandle(hToken);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
}
wulfgarpro
  • 6,666
  • 12
  • 69
  • 110
  • 1
    PROCESS_QUERY_LIMITED_INFORMATION does not exist on XP so it should not even get that far. – Anders Nov 22 '17 at 04:22
  • You're correct; updated the prototype... but still getting the same error _Access is denied_. – wulfgarpro Nov 22 '17 at 04:34
  • are you sure that after open process with `PROCESS_QUERY_INFORMATION` on xp you still got access denied ? impossible – RbMm Nov 22 '17 at 06:56
  • See if this helps https://groups.google.com/forum/#!topic/microsoft.public.windowsxp.embedded/Jy_HxTQdAyg – Tarun Lalwani Nov 25 '17 at 09:37
  • @TarunLalwani, I've tested using `GetCurrentProcessId();` (as per the example in the link), and it results in the same error. Looking at the trace with drstrace, I can see it's failing on `NtOpenProcessToken` with NTSTATUS code _0xC00000022_ (Access denied). – wulfgarpro Nov 27 '17 at 22:42
  • @RbMm, hence my confusion. – wulfgarpro Nov 27 '17 at 22:42
  • I can only guess that you not open process handle with `PROCESS_QUERY_INFORMATION`. if you have this access simply impossible error on call `NtOpenProcessToken`. internally this api simply call `ObReferenceObjectByHandle(hProcess, PROCESS_QUERY_INFORMATION,..);` if `ObReferenceObjectByHandle` return error - you got it. otherwise no another checks. access denied you can got only if you not have `PROCESS_QUERY_INFORMATION` on process. – RbMm Nov 27 '17 at 22:54
  • @RbMm, I'm running this code as a user in the Administrator group. It works fine as SYSTEM user, why does it fail for an admin user? – wulfgarpro Nov 28 '17 at 01:05
  • if you set `READ_CONTROL` in call `OpenProcessToken(process, READ_CONTROL, &token)` - this will be ok (i mean no error in api call)? – RbMm Nov 28 '17 at 01:22
  • i look - on xp system process tokens have full access for *system* (*S-1-5-18*) and `READ_DACL|TOKEN_QUERY` for Administrators group (*S-1-5-32-544*). and owner is *S-1-5-32-544* the user process tokens have full access for *system* and concrete user (*S-1-5-21-...*). no any access for administrators group. as result when you try open token for another logged user (another sid) you got access denied – RbMm Nov 28 '17 at 02:06

1 Answers1

6

on XP processes which run under LocalSystem(S-1-5-18) have next DACL on token:

A 00 000F01FF S-1-5-18 'SYSTEM'
A 00 00020008 S-1-5-32-544 'Administrators'
Owner: S-1-5-32-544 'Administrators' @ 'BUILTIN' [Alias]

this mean that SYSTEM have full access TOKEN_ALL_ACCESS (000F01FF) and users from Admin group (S-1-5-32-544) have READ_DACL|TOKEN_QUERY(00020008) access

processes wich run under any another account have next Dacl on Token:

A 00 000F01FF S-1-5-18 'SYSTEM'
A 00 000F01FF <UserSid>
Owner: <UserSid>

where UserSid some unique (not group!) sid. say for services run not under LocalSystem it look like S-1-5-80-.., for users - S-1-5-21-.. this mean that this dacl grant full access to 'SYSTEM' and concrete user. but not grant any access to Administrators group (S-1-5-32-544). so you can open any process token which run under same user. but if you try open process toke, which run under another user (another Sid) you have no any access to it and got access denied. you can not even read it dacl (you not owner and have no READ_CONTROL).

in code you try open token from another user process (LogonUser used). in this token dacl - no your user sid or Administrators sid. as result and access denied

however if you have takeownership privilege - you can first open token with WRITE_OWNER and set self as owner, after this open it with WRITE_DAC (owner have this access) and change dacl. or, if you have debug provilege, you can impersonate system thread, by using NtImpersonateThread and have full access to token.

util code:

#ifdef __cplusplus
extern "C" {
#endif

NTSYSCALLAPI
NTSTATUS 
NTAPI 
NtOpenThread(
             _Out_ PHANDLE ThreadHandle, 
             _In_ ULONG DesiredAccess, 
             _In_ POBJECT_ATTRIBUTES ObjectAttributes, 
             _In_ PCLIENT_ID ClientId
             );

extern "C"
NTSYSCALLAPI
NTSTATUS
NTAPI
NtImpersonateThread(
                    _In_ HANDLE ServerThreadHandle,
                    _In_ HANDLE ClientThreadHandle,
                    _In_ PSECURITY_QUALITY_OF_SERVICE SecurityQos
                    );

#ifdef __cplusplus
}
#endif

ULONG gOsVersion;

volatile UCHAR guz;
OBJECT_ATTRIBUTES zoa = { sizeof(zoa) };

void GetVersionEx()
{
    RTL_OSVERSIONINFOW VersionInformation;
    RtlGetVersion(&VersionInformation);
    gOsVersion = (VersionInformation.dwMajorVersion << 8) + VersionInformation.dwMinorVersion;
}

PCSTR GetSidNameUseName(::SID_NAME_USE snu)
{
    switch (snu)
    {
    case SidTypeUser: return "User";
    case SidTypeGroup: return "Group";
    case SidTypeDomain: return "Domain";
    case SidTypeAlias: return "Alias";
    case SidTypeWellKnownGroup: return "WellKnownGroup";
    case SidTypeDeletedAccount: return "DeletedAccount";
    case SidTypeInvalid: return "Invalid";
    case SidTypeUnknown: return "Unknown";
    case SidTypeComputer: return "Computer";
    case SidTypeLabel: return "Label";
    case SidTypeLogonSession: return "LogonSession";
    }
    return "?";
}

#define MAX_DOMAIN_NAME_LEN 128 

void DumpAcl(PACL acl, PCSTR caption)
{
    DbgPrint(caption);

    if (!acl)
    {
        DbgPrint("NULL\n");
        return;
    }

    USHORT AceCount = acl->AceCount;

    if (!AceCount)
    {
        DbgPrint("empty\n");
        return;
    }

    DbgPrint("T FL AcessMsK Sid\n");

    union {
        PVOID pv;
        PBYTE pb;
        PACE_HEADER pah;
        PACCESS_ALLOWED_ACE paaa;
    };

    pv = acl + 1;

    char sz[16], sz2[16];

    do
    {
        switch (pah->AceType)
        {
        case ACCESS_ALLOWED_ACE_TYPE:
        case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
        case ACCESS_DENIED_ACE_TYPE:
        case ACCESS_DENIED_CALLBACK_ACE_TYPE:
        case SYSTEM_MANDATORY_LABEL_ACE_TYPE:
            break;
        default:
            DbgPrint("AceType=%u\n", pah->AceType);
            continue;
        }

        UNICODE_STRING us;
        if (0 <= RtlConvertSidToUnicodeString(&us, (PSID)&paaa->SidStart, TRUE))
        {
            WCHAR name[256], DomainName[MAX_DOMAIN_NAME_LEN];
            ULONG cch = RTL_NUMBER_OF(name);
            ::SID_NAME_USE snu;
            DWORD cchReferencedDomainName = MAX_DOMAIN_NAME_LEN;

            if (!LookupAccountSidW(0, (PSID)&paaa->SidStart, name, &cch, DomainName, &cchReferencedDomainName, &snu))
            {
                name[0]=0;
            }

            ACCESS_MASK Mask = paaa->Mask;
            sprintf(sz2, "%08X", Mask);

            switch (pah->AceType)
            {
            case ACCESS_ALLOWED_ACE_TYPE:
            case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
                sz[0] = 'A', sz[1] = 0;
                break;
            case ACCESS_DENIED_ACE_TYPE:
            case ACCESS_DENIED_CALLBACK_ACE_TYPE:
                sz[0] = 'D', sz[1] = 0;
                break;
            case SYSTEM_MANDATORY_LABEL_ACE_TYPE:
                sz[0] = 'L', sz[1] = 0;
                sz2[0] = Mask & SYSTEM_MANDATORY_LABEL_NO_READ_UP ? 'R' : ' ';
                sz2[1] = Mask & SYSTEM_MANDATORY_LABEL_NO_WRITE_UP ? 'W' : ' ';
                sz2[2] = Mask & SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP ? 'E' : ' ';
                sz2[3] = 0;
                break;
            default: __assume(false);
            }
            DbgPrint("%s %02X %s %wZ '%S'\n", sz, paaa->Header.AceFlags, sz2, &us, name);
            RtlFreeUnicodeString(&us);
        }

    } while (pb += pah->AceSize, --AceCount);
}

void Dump(HANDLE hToken)
{
    ULONG cb = 0, rcb = 128;

    PVOID stack = alloca(guz);

    union {
        PVOID buf;
        PSECURITY_DESCRIPTOR psd;
        PTOKEN_USER ptu;
    };

    UNICODE_STRING us;
    ::SID_NAME_USE snu;
    WCHAR name[256], DomainName[MAX_DOMAIN_NAME_LEN];
    ULONG cch, cchReferencedDomainName;

    NTSTATUS status;

    do 
    {
        if (cb < rcb)
        {
            cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
        }

        if (0 <= (status = NtQueryInformationToken(hToken, TokenUser, buf, cb, &rcb)))
        {
            if (0 <= RtlConvertSidToUnicodeString(&us, ptu->User.Sid, TRUE))
            {
                cch = RTL_NUMBER_OF(name);
                cchReferencedDomainName = RTL_NUMBER_OF(DomainName);

                if (!LookupAccountSidW(NULL, ptu->User.Sid, name, &cch, DomainName, &cchReferencedDomainName, &snu))
                {
                    *name = 0;
                    *DomainName = 0;
                }
                DbgPrint("User: %wZ '%S' @ '%S' [%s]\r\n", &us, name, DomainName, GetSidNameUseName(snu));
                RtlFreeUnicodeString(&us);
            }

            break;
        }

    } while (status == STATUS_BUFFER_TOO_SMALL);

    if (0 > status)
    {
        DbgPrint("TokenUser=%x\n", status);
    }

    SECURITY_INFORMATION SecurityInformation = gOsVersion < _WIN32_WINNT_VISTA 
        ? OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION 
        : OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION|LABEL_SECURITY_INFORMATION;

    do 
    {
        if (cb < rcb)
        {
            cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
        }

        if (0 <= (status = NtQuerySecurityObject(hToken, SecurityInformation, psd, cb, &rcb)))
        {
            PACL Acl;
            BOOLEAN bPresent, bDefault;

            if (0 <= RtlGetDaclSecurityDescriptor(psd, &bPresent, &Acl, &bDefault))
            {
                DumpAcl(bPresent ? Acl : 0, "DACL:\n");
            }

            if (0 <= RtlGetSaclSecurityDescriptor(psd, &bPresent, &Acl, &bDefault))
            {
                DumpAcl(bPresent ? Acl : 0, "LABEL:\n");
            }

            PSID Owner;
            if (0 <= RtlGetOwnerSecurityDescriptor(psd, &Owner, &bDefault) && Owner)
            {
                if (0 <= RtlConvertSidToUnicodeString(&us, Owner, TRUE))
                {
                    cch = RTL_NUMBER_OF(name);
                    cchReferencedDomainName = RTL_NUMBER_OF(DomainName);

                    if (!LookupAccountSidW(NULL, Owner, name, &cch, DomainName, &cchReferencedDomainName, &snu))
                    {
                        *name = 0;
                        *DomainName = 0;
                    }
                    DbgPrint("Owner: %wZ '%S' @ '%S' [%s]\r\n", &us, name, DomainName, GetSidNameUseName(snu));
                    RtlFreeUnicodeString(&us);
                }
            }
        }

    } while (status == STATUS_BUFFER_TOO_SMALL);

    if (0 > status)
    {
        DbgPrint("QuerySecurityObject=%x\n", status);
    }
}

void Dump(ACCESS_MASK DesiredAccess, PSYSTEM_PROCESS_INFORMATION pspi)
{
    HANDLE hProcess, hToken;

    CLIENT_ID cid = { pspi->UniqueProcessId };

    DbgPrint("==============\n%p %wZ\n", cid.UniqueProcess, &pspi->ImageName);

    NTSTATUS status = NtOpenProcess(&hProcess, DesiredAccess, &zoa, &cid);

    if (0 > status)
    {
        DbgPrint("OpenProcess=%x\n", status);
        return;
    }

    status = NtOpenProcessToken(hProcess, READ_CONTROL|TOKEN_QUERY, &hToken);

    NtClose(hProcess);

    if (0 > status)
    {
        DbgPrint("OpenProcessToken=%x\n", status);
    }
    else
    {
        Dump(hToken);
        NtClose(hToken);
    }
}

void DumpProcessAndTokens(PVOID buf)
{
    union {
        PVOID pv;
        PBYTE pb;
        PSYSTEM_PROCESS_INFORMATION pspi;
    };

    pv = buf;

    ULONG NextEntryOffset = 0;

    ACCESS_MASK DesiredAccess = gOsVersion < _WIN32_WINNT_VISTA 
        ? PROCESS_QUERY_INFORMATION : PROCESS_QUERY_LIMITED_INFORMATION;

    do 
    {
        pb += NextEntryOffset;

        if (pspi->UniqueProcessId)
        {           
            Dump(DesiredAccess, pspi);
        }

    } while (NextEntryOffset = pspi->NextEntryOffset);
}

NTSTATUS GetSystemToken(PCLIENT_ID ClientId)
{
    HANDLE hThread;

    NTSTATUS status = NtOpenThread(&hThread, THREAD_DIRECT_IMPERSONATION, &zoa, ClientId);

    if (0 <= status)
    {
        static SECURITY_QUALITY_OF_SERVICE sqos = {
            sizeof sqos, SecurityImpersonation, SECURITY_STATIC_TRACKING, FALSE
        };

        if (0 <= (status = NtImpersonateThread(NtCurrentThread(), hThread, &sqos)))
        {
            HANDLE hToken;
            if (0 <= (status = NtOpenThreadTokenEx(NtCurrentThread(), TOKEN_QUERY,FALSE, 0, &hToken)))
            {

                ULONG cb = 0, rcb = 32;
                PVOID stack = alloca(guz);

                union {
                    PVOID buf;
                    PTOKEN_USER ptu;

                };

                do 
                {
                    if (cb < rcb)
                    {
                        cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
                    }

                    if (0 <= (status = NtQueryInformationToken(hToken, TokenUser, buf, cb, &rcb)))
                    {
                        static _SID LocalSystem = { SID_REVISION, 1, SECURITY_NT_AUTHORITY, { SECURITY_LOCAL_SYSTEM_RID } };

                        if (!RtlEqualSid(&LocalSystem, ptu->User.Sid))
                        {
                            RevertToSelf();
                            status = STATUS_SERVER_SID_MISMATCH;
                        }

                        break;
                    }

                } while (status == STATUS_BUFFER_TOO_SMALL);

                NtClose(hToken);
            }
        }

        NtClose(hThread);
    }

    return status;
}

NTSTATUS ImpersonateLocalSystem(PVOID buf)
{
    union {
        PVOID pv;
        PBYTE pb;
        PSYSTEM_PROCESS_INFORMATION pspi;
    };

    pv = buf;

    ULONG NextEntryOffset = 0;

    do 
    {
        pb += NextEntryOffset;

        if (pspi->UniqueProcessId && pspi->NumberOfThreads)
        {           
            if (0 <= GetSystemToken(&pspi->TH->ClientId))
            {
                return STATUS_SUCCESS;
            }
        }

    } while (NextEntryOffset = pspi->NextEntryOffset);

    return STATUS_UNSUCCESSFUL;
}

void DumpProcessTokens()
{
    BOOLEAN b;
    NTSTATUS status = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &b);

    if (0 > status)
    {
        return ;
    }

    ULONG cb = 0x8000;

    do 
    {
        status = STATUS_INSUFFICIENT_RESOURCES;

        if (PVOID buf = new UCHAR[cb])
        {
            if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
            {
                if (0 <= ImpersonateLocalSystem(buf))
                {
                    DumpProcessAndTokens(buf);
                    RevertToSelf();
                }
            }

            delete [] buf;
        }

    } while (status == STATUS_INFO_LENGTH_MISMATCH);
}

    GetVersionEx();
    DumpProcessTokens();

some result from xp:

00000004 System
User: S-1-5-18 'SYSTEM' @ 'NT AUTHORITY' [User]
DACL:
T FL AcessMsK Sid
A 00 000F01FF S-1-5-18 'SYSTEM'
LABEL:
NULL
Owner: S-1-5-32-544 'Administrators' @ 'BUILTIN' [Alias]

==============
0000021C smss.exe
User: S-1-5-18 'SYSTEM' @ 'NT AUTHORITY' [User]
DACL:
T FL AcessMsK Sid
A 00 000F01FF S-1-5-18 'SYSTEM'
A 00 00020008 S-1-5-32-544 'Administrators'
LABEL:
NULL
Owner: S-1-5-32-544 'Administrators' @ 'BUILTIN' [Alias]
==============
000003B0 svchost.exe
User: S-1-5-20 'NETWORK SERVICE' @ 'NT AUTHORITY' [WellKnownGroup]
DACL:
T FL AcessMsK Sid
A 00 000F01FF S-1-5-18 'SYSTEM'
A 00 000F01FF S-1-5-80-979556362-403687129-3954533659-2335141334-1547273080 ''
LABEL:
NULL
Owner: S-1-5-80-979556362-403687129-3954533659-2335141334-1547273080 '' @ '' [WellKnownGroup]

==============
0000047C svchost.exe
User: S-1-5-20 'NETWORK SERVICE' @ 'NT AUTHORITY' [WellKnownGroup]
DACL:
T FL AcessMsK Sid
A 00 000F01FF S-1-5-18 'SYSTEM'
A 00 000F01FF S-1-5-20 'NETWORK SERVICE'
LABEL:
NULL
Owner: S-1-5-20 'NETWORK SERVICE' @ 'NT AUTHORITY' [WellKnownGroup]

==============
000004C4 svchost.exe
User: S-1-5-19 'LOCAL SERVICE' @ 'NT AUTHORITY' [WellKnownGroup]
DACL:
T FL AcessMsK Sid
A 00 000F01FF S-1-5-18 'SYSTEM'
A 00 000F01FF S-1-5-19 'LOCAL SERVICE'
LABEL:
NULL
Owner: S-1-5-19 'LOCAL SERVICE' @ 'NT AUTHORITY' [WellKnownGroup]

==============
000005EC explorer.exe
User: S-1-5-21-839522115-2025429265-725345543-500 'Administrator' @ '*' [User]
DACL:
T FL AcessMsK Sid
A 00 000F01FF S-1-5-21-839522115-2025429265-725345543-500 'Administrator'
A 00 000F01FF S-1-5-18 'SYSTEM'
LABEL:
NULL
Owner: S-1-5-21-839522115-2025429265-725345543-500 'Administrator' @ '*' [User]
RbMm
  • 31,280
  • 3
  • 35
  • 56