A year ago, Microsoft released the Local Administrator Password Solution (LAPS) which aims to prevent the reuse of local administrator passwords by setting, “…a different, random password for the common local administrator account on every computer in the domain.” This post will cover a brief background on LAPS and how to use PowerView to perform some specific LAPS-specific enumeration. Sean Metcalf has a detailed post about LAPS here with much more information for anyone interested.
Note: this functionality is in the dev branch of PowerSploit.
LAPS Overview
LAPS accomplishes its approach by first extending the Active Directory schema to include two new fields, ms-MCS-AdmPwd (the password itself) and ms-MCS-AdmPwdExpirationTime (when the password expires). The LAPS client that rotates the plaintext password on systems and stores the result in Active Directory is installed on endpoints, and the schema is restricted by default to only allow specific users to read the ms-MCS-AdmPwd attribute. This password information can be retrieved using standard LDAP enumeration tools, a LAPS GUI tool that Microsoft released with the solution, or a set of PowerShell cmdlets (the AdmPwd.PS module) released with the package as well. A bit after LAPS was released, Karl Fosaaen also released a great post titled “Running LAPS Around Cleartext Passwords” which described how to use PowerShell to retrieve the plaintext LAPS passwords for machines where the current user has read access to the password field (his script is available here on GitHub).
Most setup guides I’ve seen involve extending the schema, delegating read rights for specific users/groups, installing the LAPS client (often through a GPO package push), and pushing out the “Policies -> Administrative Templates -> LAPS” group policy to kick everything off by applying the GPO to specific OUs.
The security of this solution depends on who has read access to the ms-MCS-AdmPwd field. As Microsoft states, “Domain administrators using the solution can determine which users, such as helpdesk administrators, are authorized to read passwords.” If you’re on an offensive engagement and don’t have detailed knowledge of how LAPS was set up for the environment, you’ve probably just checked if your current user context has read access rights to the field by running something like Karl’s script, relying upon a massive misconfiguration (like ‘Domain Users’ being granted read access). I wonder if we can be a bit more targeted?
LAPS and PowerView
Even if our current user context can’t read the ms-MCS-AdmPwd, we can still read the permissions for specific computer and organizational unit Active Directory objects. In my largely default test environment, this includes the ability to enumerate the permission entries that reveal which groups/users are granted read access on the protected attribute. This means we can figure out who can enumerate the LAPS password for a target machine with existing PowerView functionality and target those users for compromise.
Here’s the big nasty one-liner that lets us enumerate who can view the LAPS password for the LAPSCLIENT.test.local machine:
Get-NetComputer -ComputerName 'LAPSCLIENT.test.local' -FullData | Select-Object -ExpandProperty distinguishedname | ForEach-Object { $_.substring($_.indexof('OU')) } | ForEach-Object { Get-ObjectAcl -ResolveGUIDs -DistinguishedName $_ } | Where-Object { ($_.ObjectType -like 'ms-Mcs-AdmPwd') -and ($_.ActiveDirectoryRights -match 'ReadProperty') } | ForEach-Object { Convert-NameToSid $_.IdentityReference } | Select-Object -ExpandProperty SID | Get-ADObject
Let’s go through this step by step. First, we’ll retrieve the full data object for Get-NetComputer -FullData. We then extract and expand the distinguishedname property, find the index of ‘OU’, and return just that section of the string. All we’re doing here is enumerating the OU that a particular machine belongs to.
Next, we enumerate the ACLs for that specified OU with Get-ObjectAcl, resolving GUIDs to common display names with -ResolveGUIDs. We then filter the permission entries, returning only those that include read rights on the ms-Mcs-AdmPwd field. We can’t be sure if the name returned from the IdentityReference field is a group or user, so we can then use PowerView’s Convert-NameToSid cmdlet to translate the object to a straight security identifier (SID), which we can finally pipe into Get-ADObject to return the full active directory user/group object that has the read permissions for the field. We can see from the results that the “LAPS_recover” domain group is granted read rights.
Now what if we wanted to enumerate ALL LAPS applications and who had read access to them? Thanks to a few recent optimizations to Get-ObjectACL‘s parameter pipelining, this is easier and faster than ever:
Get-NetOU -FullData | Get-ObjectAcl -ResolveGUIDs | Where-Object { ($_.ObjectType -like 'ms-Mcs-AdmPwd') -and ($_.ActiveDirectoryRights -match 'ReadProperty') } | ForEach-Object { $_ | Add-Member NoteProperty 'IdentitySID' $(Convert-NameToSid $_.IdentityReference).SID; $_ }
Let’s break this one-liner down bit by bit again. Get-NetOU -FullData will return full data objects for all OUs in the domain, and piping this to Get-ObjectAcl -ResolveGUIDs will return the permissions for all current OUs. We do this because LAPS is normally applied to OUs through group policy. We then filter for the same fields as in the first example, and add in the SID of the converted IdentityReference back into the object for display. We don’t return the full object here so we can separate out which OU/object the permissions applies to, in the case of multiple OUs with LAPS enforced.
Wrapup
LAPS is a great solution, and if set up properly can be an effective way for an enterprise to manage the local administrator passwords organization-wide. However, like with any solution, misconfigurations are inevitable in some environments, and PowerView can help you enumerate whether LAPS is misconfigured and which users may have read access to the protected password attribute.
Pingback: Interesting Reads for the week of 4/29/16 | Thoughts on AppSec
Pingback: Active Directory Kill Chain Attack 101 – syhack