Testing for Local Administrator Privilege with PowerShell

When writing PowerShell scripts it is sometimes necessary to know whether the user account running the script has local administrator privileges.  A piece of code often used for this is:


([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")

However, this code only returns true when run in an elevated context, so it is not really a test of whether the user has local administrator privilege, but whether the code is running in an elevated context.

capture
Non-elevated
capture
Elevated

The reason for this is that since Windows Vista and the introduction of  User Account Control (UAC), an account that is a member of the local administrator group will get a split user access token.  For most tasks, the account will use a filtered access token, which has the same rights as a standard user token.  When a task is performed that requires elevation of privilege, the full access token will be used instead.  Hence we get different results from the above command depending on which access token is being used.

Another way of ensuring that code is run elevated (since PowerShell 4) is to use the requires statement


#requires –runasadministrator

But I want to know if the user is a member of the local administrator group.  One way to do that is simply get the username of the logged-on user from WMI, then use net localgroup:


$LoggedOnUsername = (Get-WmiObject -Class Win32_ComputerSystem -Property Username | Select -ExpandProperty Username).Split('\')[1]
Net localgroup administrators | Select-String $LoggedOnUsername

And here is another method using WMI:


$userToFind = (Get-WmiObject -Class Win32_ComputerSystem -Property Username | Select -ExpandProperty Username).Split('\')[1]
$administratorsAccount = Get-WmiObject Win32_Group -filter "LocalAccount=True AND SID='S-1-5-32-544'"
$administratorQuery = "GroupComponent = `"Win32_Group.Domain='" + $administratorsAccount.Domain + "',NAME='" + $administratorsAccount.Name + "'`""
$user = Get-WmiObject Win32_GroupUser -filter $administratorQuery | select PartComponent |where {$_ -match $userToFind}
$user 

The problem with both of these methods is that they wont work with nested groups.  So if a user is a member of a group that is a member of the local administrators group, rather than a direct member, I can’t use these methods.

It can be done, however.

If we go back to our original code we can see that we are creating a Windows Identity object for the current user.  This object has a Claims property, that lists the rights that the user has, mostly in the form of local or domain users or groups.  In the Value property of Claims, we can see the SIDs of those users and groups.  This will include nested groups, because even though the user may not be a direct member of the group, they still have the rights of that group.

capture
Partial listing of SIDs

SIDs are not too friendly by themselves, so lets create a custom object and translate the SIDs to group names:


$Claims = @()
[Security.Principal.WindowsIdentity]::GetCurrent().Claims | foreach {
    $Claim = New-Object psobject
    try
    {
        $SID = New-Object Security.Principal.SecurityIdentifier -ArgumentList $_.Value
        $SID = $SID.Translate([System.Security.Principal.NTAccount])
        Add-Member -InputObject $Claim -MemberType NoteProperty -Name Claim -Value $SID
        Add-Member -InputObject $Claim -MemberType NoteProperty -Name SID -Value $_.Value
        Add-Member -InputObject $Claim -MemberType NoteProperty -Name Type -Value $_.Type.Split('/')[8]
        Add-Member -InputObject $Claim -MemberType NoteProperty -Name Properties -Value $_.Properties.Values
        $Claims += $Claim
    }
    Catch {}
    }
$Claims | sort Claim

capture
User Identity Claims

You’ll notice that the “BUILTIN\Administrators” group is listed in the Claims.  This is the local Administrators group.  Since the SID for this group is the same on all systems, we can search the Claims of the User Identity for this SID, and thereby determine if he or she is a member of the local Administrators group, either directly or indirectly.

We can either use the HasClaim() method:


$LoggedOnUsername = (Get-WmiObject -Class Win32_ComputerSystem -Property Username | Select -ExpandProperty Username).Split('\')[1]
$ID = New-Object Security.Principal.WindowsIdentity -ArgumentList $LoggedOnUsername
$ID.HasClaim('http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid','S-1-5-32-544')

This will return True or False based on whether the Identity has the claim of that group.

Or we can also simply do a text search on the Value property:


$LoggedOnUsername = (Get-WmiObject -Class Win32_ComputerSystem -Property Username | Select -ExpandProperty Username).Split('\')[1]
$ID = New-Object Security.Principal.WindowsIdentity -ArgumentList $LoggedOnUsername
$ID.Claims.Value.Contains('S-1-5-32-544')

To make it even simpler, we can do a one-liner using the GetCurrent() method:


[Security.Principal.WindowsIdentity]::GetCurrent().Claims.Value.Contains('S-1-5-32-544')

Now I can use this in a script:


if ([Security.Principal.WindowsIdentity]::GetCurrent().Claims.Value.Contains('S-1-5-32-544'))
{
    "You got the power!"
}
Else
{
    "You don't got the power :("
}

Have I got the power? Oh yeah 🙂

capture

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.