An example of least privilege implementation (part III)

This is the last part of the 3 posts serie where we’ll examine how we can ensure that the user remains in the local administrators group for only 1 hour.

We just need a watchdog who runs at user logoff, runs every 15 minutes and at computer startup. It seems simple but it has to be very robust because of the nature of the tasks to be achieved, i.e. remove users from the local administrators group. Here comes fully powershell into play and like any security measure, there’s a trade-off between risks, costs and performance.

We will define 2 scheduled tasks. The tasks that run at logoff and startup launch the same powershell script and are therefore merged into a single task that has actually multiple triggers.
UAC watchdog tasks

The task at startup and logff just runs the following script with the -Force parameter whereas it runs without parameters every 15 minutes.

#Requires -version 2.0            
[CmdletBinding()]            
param            
(            
    [Parameter(Mandatory=$false, Position=0)]            
    [System.Management.Automation.SwitchParameter]${Force}            
)            
            
# Format date             
$date = (Get-Date).ToString('yyyyMMddHHmmss')            
            
# Get a unique id            
$guid = $Host.InstanceId.Guid.ToString()            
             
# Define a header            
$header = "$date ; $guid"            
            
Function Remove-UsersFromLocalGroup            
{            
    param            
    (            
    [parameter(Mandatory=$true,Position=0)]            
    [system.string]$GroupName,            
            
    [parameter(Mandatory=$true,Position=1)]            
    [system.string[]]$Userids            
    )             
    Begin            
    {            
        $ADSIcomputer = [ADSI]("WinNT://.,computer")             
        $usercontext = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()            
        $IsAdmin = $usercontext.IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")                               
            
        if (-not($IsAdmin))            
        {            
            Write-Warning "Must run powerShell as Administrator to perform these actions"            
            return            
        }             
    }            
    Process            
    {            
        try            
        {            
            $group = $ADSIcomputer.psbase.children.find($GroupName)            
        } catch {            
            Write-Warning -Message "Encountered the following error while searching group $GroupName : $($_.Exception.Message)"            
            return            
        }            
            
        $Userids | ForEach-Object -Process {            
            try            
            {            
                $group.Remove($_)            
            } catch {            
                Write-Warning -Message "Encountered the following error while removing user: $($_.Exception.Message)"            
                return            
            }            
            "$header ; $_ removed from $GroupName on $($env:computername)" | Out-File -filepath "$env:systemroot\UACjob.log" -Append -NoClobber -Encoding ASCII            
        }            
    }            
    End {}            
} # end of function            
            
Function Get-LocalGroupMembers             
{            
    [CmdletBinding()]            
    param(            
    [parameter(Mandatory=$true,ValueFromPipeLine=$true,Position=0)]            
    [ValidateNotNullOrEmpty()]            
    [system.string[]]$GroupName            
    )             
    Begin {            
     $isPartofDomain = (Get-WmiObject Win32_ComputerSystem).PartOfDomain            
     if ($isPartofDomain)            
     {            
      $Domain = $env:userdomain            
     } else {            
      $Domain = (Get-WmiObject Win32_ComputerSystem).Workgroup            
     }            
    }            
    Process {            
        $GroupName | ForEach-Object -Process {            
            try            
            {            
                @(([ADSI]"WinNT://$env:computername/$_").psbase.Invoke("Members")) | ForEach-Object -Process {            
                                
                    switch($_.GetType().InvokeMember("AdsPath","GetProperty",$null,$_,$null).ToString())            
                    {            
                        {$_ -match [regex]"^WinNT://S\-\d{1}\-"} {$_}            
                        {$_ -match [regex]"^WinNT://$Domain/$env:computername/"} {$_ -replace "$Domain/", ""}            
                        default {$_}            
                    } # end of switch            
                }            
            } catch {            
                Write-Warning -Message "Encountered the following error while group members: $($_.Exception.Message)"            
                Return            
            }            
        }            
    }            
    End{}            
} # end of function             
            
# Create a global whitelist            
$whitelist = @()            
$whitelist += "$env:computername/Administrator"            
$whitelist += "$env:userdomain/Domain Admins"            
$whitelist += "S-1-5-21-2224021702-1381044862-585381713-1002"            
            
# Add specific locally defined users             
if (Test-Path $env:systemroot\noUAC.ok)            
{            
    Get-Content $env:systemroot\noUAC.ok -Encoding ASCII | ForEach-Object -Process {            
        # Replace a \ by a / as \ is the escape character and add the user to our whitelist            
        $whitelist += $_ -replace "\\","/"            
    }            
}            
            
# Get the list of local administrators' group members            
$members = Get-LocalGroupMembers -GroupName "Administrators"            
            
if ($Force)            
{            
    # Remove any user that does not appear to be in the whitelist            
    $members | ForEach-Object -Process {            
        if(-not($whitelist | Select-String -Pattern (($_ -replace "WinNT://","")+"$") -Quiet))            
        {            
            Remove-UsersFromLocalGroup -GroupName 'Administrators' -Userids $_            
        }            
    }            
} else {            
    # Import the list of allowed users and get an array of unique usernames filtered by removing whitelisted users            
    if (Test-Path "$env:systemroot\UAC.Allowed.dat")            
    {            
        $allUACAllowedUsers = Import-Csv -Path "$env:systemroot\UAC.Allowed.dat" -Delimiter ";" -Header Ticks,Username            
        $uniqueUsernames = $allUACAllowedUsers | ForEach-Object -Process {            
            if ($_.Username -ne $null)            
            {            
                $_.Username -replace "\\","/"            
            }            
        } | Sort-Object -Unique | Select-String -Pattern $whitelist -NotMatch             
    }            
            
    # Cycle through unique username and get the lastest timestamp            
    foreach ($item in $uniqueUsernames)            
    {            
        $tempitem = @()            
        $tempitem += $allUACAllowedUsers | Where-Object -FilterScript { ($_.Username -replace "\\","/") -eq $item} | Sort-Object -Descending:$true -Property Ticks            
        if ($tempitem.Count -ne $null)            
        {            
            if ($tempitem[0].Username -ne $null)            
            {            
                if ((Get-Date).Ticks -gt $tempitem[0].Ticks)            
                {            
                    Write-Host -ForegroundColor Yellow -Object ("Attempt to remove: " + $tempitem[0].Username)            
                    $id = $tempitem[0].Username -replace "\\","/"            
                    if ($members | Select-String -Pattern "WinNT://$id$" -Quiet)            
                    {            
                        Remove-UsersFromLocalGroup -GroupName 'Administrators' -Userids "WinNT://$id"            
                    } else {            
                        Write-Host -ForegroundColor Yellow -Object ("Has already been removed: " + $tempitem[0].Username)            
                    }            
                }            
            }            
        }            
    }            
}
Advertisements

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 )

Google+ photo

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

Connecting to %s