I'm trying to check if the current user is in the administrator group on Windows in my C application. I already tried the WinAPI function "IsUserAnAdmin", but it seems that this function only returns True if the current process also has admin privileges. But only because the current process is running with medium integrity doesn't mean that the user isn't in the administrator group. While I was searching for alternatives I discovered how metasploit does detect this: It simply runs the command "cmd.exe /c whoami /groups" and checks if the output contains "S-1-5-32-544", which is the administrators SID.
I was wondering if I could do the same thing in a more efficient way using the WinAPI without the cmd command?
if user member of BUILTIN\Administrators (S-1-5-32-544) group (alias) this sid present in it token groups. and usually only in this case (of course possible create token for not admin user with S-1-5-32-544 and for admin user without it). so simply and effective check - list token groups and look - are S-1-5-32-544 present here, with any attributes. IsUserAdmin
not simply check for this sid, but
Even if a SID is present in the token, the system may not use the SID in an access check. The SID may be disabled or have the SE_GROUP_USE_FOR_DENY_ONLY attribute. The system uses only enabled SIDs to grant access when performing an access check.
when admin user (member of S-1-5-32-544 Alias) interactive login to system and UAC active - system filter it token, and set SE_GROUP_USE_FOR_DENY_ONLY attribute for S-1-5-32-544 (except built-in Administrator - S-1-5-32-500)
so code can be next:
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
ULONG IsUserInAdminGroup(BOOLEAN* pb)
{
*pb = FALSE;
HANDLE hToken;
ULONG dwError = BOOL_TO_ERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken));
if (dwError == NOERROR)
{
// /RTCs must be disabled !
static volatile UCHAR guz = 0;
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = 0x100;
union {
PVOID buf;
PTOKEN_GROUPS ptg;
};
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
dwError = BOOL_TO_ERROR(GetTokenInformation(hToken, ::TokenGroups, buf, cb, &rcb));
} while (dwError == ERROR_INSUFFICIENT_BUFFER);
CloseHandle(hToken);
if (dwError == NOERROR)
{
if (ULONG GroupCount = ptg->GroupCount)
{
static const SID_IDENTIFIER_AUTHORITY NT_AUTHORITY = SECURITY_NT_AUTHORITY;
PSID_AND_ATTRIBUTES Groups = ptg->Groups;
do
{
PSID Sid = Groups++->Sid;
if (*GetSidSubAuthorityCount(Sid) == 2 &&
*GetSidSubAuthority(Sid, 0) == SECURITY_BUILTIN_DOMAIN_RID &&
*GetSidSubAuthority(Sid, 1) == DOMAIN_ALIAS_RID_ADMINS &&
!memcmp(&NT_AUTHORITY, GetSidIdentifierAuthority(Sid), sizeof(SID_IDENTIFIER_AUTHORITY)))
{
*pb = TRUE;
break;
}
} while (--GroupCount);
}
return NOERROR;
}
}
return dwError;
}
also possible do direct check of user sid from token - are it member of DOMAIN_ALIAS_RID_ADMINS
alias. here question - how exactly is the task, why this necessary at all. example of code (used ntsam.h and linked with samlib.lib - part of standard windows SDK)
HRESULT IsUserInAdminGroup(PSID UserSid, BOOLEAN* pb)
{
SAM_HANDLE ServerHandle, DomainHandle;
NTSTATUS status = SamConnect(0, &ServerHandle, SAM_SERVER_LOOKUP_DOMAIN, 0);
if (0 <= status)
{
ULONG len = GetSidLengthRequired(1);
PSID BuiltIn = (PSID)alloca(len);
static const SID_IDENTIFIER_AUTHORITY NT_AUTHORITY = SECURITY_NT_AUTHORITY;
InitializeSid(BuiltIn, const_cast<SID_IDENTIFIER_AUTHORITY*>(&NT_AUTHORITY), 1);
*GetSidSubAuthority(BuiltIn, 0) = SECURITY_BUILTIN_DOMAIN_RID;
status = SamOpenDomain(ServerHandle, DOMAIN_READ, BuiltIn, &DomainHandle);
SamCloseHandle(ServerHandle);
if (0 <= status)
{
ULONG MembershipCount, *Aliases;
status = SamGetAliasMembership(DomainHandle, 1, &UserSid, &MembershipCount, &Aliases);
SamCloseHandle(DomainHandle);
if (0 <= status)
{
PVOID buf = Aliases;
if (MembershipCount)
{
do
{
if (*Aliases++ == DOMAIN_ALIAS_RID_ADMINS)
{
*pb = TRUE;
break;
}
} while (--MembershipCount);
}
SamFreeMemory(buf);
}
}
}
return HRESULT_FROM_NT(status);
}
HRESULT IsUserInAdminGroup(BOOLEAN* pb)
{
*pb = FALSE;
HANDLE hToken;
ULONG dwError = BOOL_TO_ERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken));
if (dwError == NOERROR)
{
// /RTCs must be disabled !
static volatile UCHAR guz = 0;
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = 0x80;
union {
PVOID buf;
PTOKEN_USER ptu;
};
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
dwError = BOOL_TO_ERROR(GetTokenInformation(hToken, ::TokenUser, buf, cb, &rcb));
} while (dwError == ERROR_INSUFFICIENT_BUFFER);
CloseHandle(hToken);
if (dwError == NOERROR)
{
return IsUserInAdminGroup(ptu->User.Sid, pb);
}
}
return HRESULT_FROM_WIN32(dwError);
}