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);
}
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]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With