This is my realization of the Local Administrator Temporary Elevation Request (LATER) concept Mathias R. Jessen (@IISResetMe) talked about in the session “Attack Surface Reductions – for the Adventurous Admin” at PowerShell Conference Europe 2019, Hannover.
If you couldn’t attend the session in person the great people of #PSConfEU have uploaded the recording on Youtube. I highly recommend that you stop reading and go watch it now!
As you saw in the presentation there are no actual code-snippets or demos just pure awesomeness from Mathias philosophies around Security Engineering. So what I’ve tried to do is convert all L.A.T.E.R topics mentioned in the session to actual PowerShell code and let the session from Mathias speak for it self regarding on how it works.
So basically the picture, down below, is what I’ve got to work with to realize the concept.
The first thing I did was create a rough to do list.
- WinForms or WPF application/GUI-script for the end user.
- A service account with R/W permissions to the SQL database and Read+Reset permissions for target LAPS clients.
- LAPS installed and configured for target clients.
- Windows Server to host the JEA endpoint.
- SQL database for logging and throttling.
- PowerShell code to run in the JEA endpoint.
- PowerShell code to run client side when the end user clicks “Request Administrator”.
The Later module, the JEA endpoint settings and the SQL database with all tables is published at GitHub and licensed under the MIT license.
Then I created a service account (CONTOSO\svc-admpwd) and set the appropriate permissions to request and reset LAPS passwords.
Notice: If utlilizing auditing for LAPS, Set-AdmPwdAuditing
, will only show the service account as the requester not the actual end user.
1 2 |
Set-AdmPwdReadPasswordPermission -Identity 'OU=Computers,OU=Contoso,DC=contoso,DC=com' -AllowedPrincipals 'svc-admpwd' Set-AdmPwdResetPasswordPermission -Identity 'OU=Computers,OU=Contoso,DC=contoso,DC=com' -AllowedPrincipals 'svc-admpwd' |
After LAPS was configured I created a database named Later and set the appropriate service account permissions.
1 2 3 4 5 6 7 8 |
USE [Later] GO CREATE USER [CONTOSO\svc-admpwd] WITH DEFAULT_SCHEMA=[dbo] GO ALTER ROLE [db_datareader] ADD MEMBER [CONTOSO\svc-admpwd] GO ALTER ROLE [db_datawriter] ADD MEMBER [CONTOSO\svc-admpwd] GO |
And of course the database needed some tables and columns which took some trial and error to figure out, how many tables was needed, which columns in each table, what data to log in each column etc. But in the end this is what I came up with.
All tables can be found on GitHub.
The next step was to configure a Windows Server to host the JEA endpoint. For this lab purpose I reused the SQL server.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
$SessionParams = @{ 'Path' = "C:\JEA\Requester.pssc" 'LanguageMode' = 'NoLanguage' 'ExecutionPolicy' = 'RemoteSigned' 'SessionType' = 'Default' 'TranscriptDirectory' = 'C:\PSTranscripts\' 'Author' = 'Omnicit AB' 'ModulesToImport' = 'LATER' 'RoleDefinitions' = @{ 'CONTOSO\res-sys-later requester' = @{ RoleCapabilities = 'Requester' } } } New-PSSessionConfigurationFile @SessionParams $RegisterParams = @{ 'Name' = 'LATER' 'Path' = "C:\JEA\Requester.pssc" 'ShowSecurityDescriptorUI' = $True } Register-PSSessionConfiguration @RegisterParams $RunAsCred = (Get-Credential 'CONTOSO\svc-admpwd') Set-PSSessionConfiguration -Name LATER -RunAsCredential $RunAsCred |
Most up to date version found on GitHub.
The actual magic happens in the module that that runs under the context of svc-admpwd. The module, that I called LATER, includes two public and one private function.
Inside the LATER module I created a JEA role capability file which simply grants the session configuration “LATER” the “Request” capability.
1 2 3 4 5 6 7 8 9 |
$CapabilityFileParams = @{ 'Path' = "C:\Program Files\WindowsPowerShell\Modules\LATER\RoleCapabilities\Requester.psrc" 'Author' = "Omnicit AB" 'CompanyName' = 'Omnicit AB' 'Description' = 'JEA capability file for LATER.' 'VisibleFunctions' = @('Get-CurrentComputerLATER', 'Reset-CurrentComputerLATER') } New-PSRoleCapabilityFile @CapabilityFileParams |
The source on GitHub already includes the Requester role capability file.
When the JEA endpoint and the LATER module was working I had to figure out the code behind the “Request Administrator” button event. Basically the button creates a PowerShell session targeting the JEA endpoint and runs Invoke-Command {Get-CurrentComputerLATER}
to retrive the LAPS password and then starts another PowerShell process to Add-LocalGroupMember -Group Administrators -Member 'NT Authority\Interactive'
.
So what happens in depth when the User clicks on the “Request Administrator” button?
- First,
whoami /groups
runs and checks against hard coded strings (regex) if the user is eligible (Member of) the necessary LATER request groups, otherwise hard fail. - A PowerShell session is created using the ConfigurationName, LATER. If the session creation fails for some reason, the user is presented with an error.
- If the session was successfully created, the next step is to run
Invoke-Command
with a scriptblock that runs theGet-CurrentComputerLATER
function with the current computer name ($using:env:COMPUTERNAME
) as value to the parameterComputerName
.
123$Later = Invoke-Command -Session $Session -ScriptBlock {Get-CurrentComputerLATER -ComputerName $using:env:COMPUTERNAME -ErrorAction Stop} -ErrorAction Stop - Now we jump inside the
Get-CurrentComputerLATER
.
The first thing that happens is that the functionGet-LaterRequesterInfo
runs to retrieve information about the session from the automatic variable $PSSenderInfo. The information returned contains the requesting user’s username, the IP-address of the PSSession source, the resolved name from the IP-address and all Claims (groups) which is evaluated against the same regex as used in step 1.
12345678[PSCustomObject]@{UserId = $PSSenderInfo.UserInfo.WindowsIdentity.User.ValueComputerName = $ComputerNameComputerNameByAddress = $ComputerNameByAddressComputerIPAddress = $WSManInstance.ClientIPComputerNameMatchAddress = $AddressMatchNameUserPolicyGroups = $UserPolicyGroups} - When the session information is retrieved we query the Policy table in the database with all Claims (group SIDs) that the user has. This will determine how many requests the user is eligible for. If the user has more than one policy group assigned, the effective policy will be the policy that contains the least number of requests.
- Now we query the Requests table to determine if the user has done Later requests in the past.
Based on past requests and the effective policy, the “throttling” begins. Example of one throttling policy reached.
If no past requests was found the script continues to the next part, request the LAPS password. - If the user was not throttled, the Cmdlet
Get-AdmPwdPassword
tries to retrieve the current LAPS password and outputs it to the pipeline. After a successful retrieval the request gets logged to the Requests table. - Now back in the end user context. We have retrieved the current LAPS password using
Invoke-Command
and will now issue agpupdate /force
. This is so the user actually have at least 60 minutes of admin privileges before the Group Policy automatically refreshes and the Restricted Groups policy kicks in. The automatic refresh interval is fully configurable. - The GUI/script will now start another PowerShell process which will run as the local administrator account using the retrieved LAPS password.
12345678910<#As encoded commandAdd-LocalGroupMember -Group Administrators -Member 'NT Authority\Interactive';Add-Type -AssemblyName System.Web;Set-LocalUser -Name Administrator -Password ([System.Web.Security.Membership]::GeneratePassword(24, 5) | ConvertTo-SecureString -AsPlainText -Force)#>$Command = 'QQBkAGQALQBMAG8AYwBhAGwARwByAG8AdQBwAE0AZQBtAGIAZQByACAALQBHAHIAbwB1AHAAIABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByAHMAIAAtAE0AZQBtAGIAZQByACAAJwBOAFQAIABBAHUAdABoAG8AcgBpAHQAeQBcAEkAbgB0AGUAcgBhAGMAdABpAHYAZQAnADsADQAKAEEAZABkAC0AVAB5AHAAZQAgAC0AQQBzAHMAZQBtAGIAbAB5AE4AYQBtAGUAIABTAHkAcwB0AGUAbQAuAFcAZQBiADsADQAKAFMAZQB0AC0ATABvAGMAYQBsAFUAcwBlAHIAIAAtAE4AYQBtAGUAIABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByACAALQBQAGEAcwBzAHcAbwByAGQAIAAoAFsAUwB5AHMAdABlAG0ALgBXAGUAYgAuAFMAZQBjAHUAcgBpAHQAeQAuAE0AZQBtAGIAZQByAHMAaABpAHAAXQA6ADoARwBlAG4AZQByAGEAdABlAFAAYQBzAHMAdwBvAHIAZAAoADIANAAsACAANQApACAAfAAgAEMAbwBuAHYAZQByAHQAVABvAC0AUwBlAGMAdQByAGUAUwB0AHIAaQBuAGcAIAAtAEEAcwBQAGwAYQBpAG4AVABlAHgAdAAgAC0ARgBvAHIAYwBlACkA'$Cred = [pscredential]::new('Administrator', (ConvertTo-SecureString -String $Later.Password -AsPlainText -Force))Start-Process -WindowStyle Hidden -FilePath PowerShell.exe -ArgumentList "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -EncodedCommand $Command" -Credential $CredNT Authority\Interactive
to the local group Administrators and resets it’s own password to a random string. - After the
NT Authority\Interactive
was added, we connect again to the JEA endpoint usingInvoke-Command
. But instead of runningGet-CurrentComputerLATER
we runReset-CurrentComputerLATER
to update the LAPS password for the current computer. - We close the PSSession and start to validate if
NT Authority\Interactive
actually was added to the local Administrators group. And if everything went smoothly we receive a success. - Now the user can actually install the print driver or missing application that required local administrator privileges in the first place.
One caveat to this is is that the user must provide credentials at the UAC prompt. If the user logoff and login again, then there is just a prompt to consent. This is because of how the Local Security Authority (LSA) hands out it’s access tokens which only happens at login. More info about this can be found here.
One last thing, you can start the Later.exe as another user and the end result will still be the same. The current logged on user will be local administrator since the user already got the NT Authority\Interactive
in their ticket.
A use case for this is when you don’t trust all endusers but you trust certain “Local IT Persons” in each branch office. Those “Local IT Persons” will have a more broader throttling policy assigned so they can actually help the enduser if necessary.
“Shift + Right click” will retrieve the option “Run as diffrent user”.
If you have any question, please free to drop a comment down below or post an issue at the repository.