LAPS
LAPS is a Microsoft solution for managing the credentials of a local administrator account on every machine, either the default RID 500 or a custom account. It ensures that the password for each account is different, random, and automatically changed on a defined schedule.
The Active Directory schema is extended and adds two new properties to computer objects, called
ms-Mcs-AdmPwd
andms-Mcs-AdmPwdExpirationTime
.By default, the DACL on
ms-Mcs-AdmPwd
only grants read access to Domain Admins but each computer object is given permission to update these properties on itselfRights to read
AdmPwd
can be delegated to other principals (users, groups etc), which is typically done at the OU levelA new GPO template is installed, which is used to deploy the LAPS configuration to machines to apply different policies to different OUs
The LAPS client is also installed on every machine
When a machine performs a
gpupdate
, it will check theAdmPwdExpirationTime
property on its own computer object in AD. If the time has elapsed, it will generate a new password (based on the LAPS policy) and sets it on thems-Mcs-AdmPwd
property.
LAPS' presence can be enumerated from BloodHound by looking at the GPOs in place or, if we have access to a host, we can check if C:\Program Files\LAPS\CSE\AdmPwd.dll
is on disk. Just like from BloodHound, we can look for the needed GPOs from powershell
or query the ms-Mcs-AdmPwdExpirationTime
attribute in the domain to get a list of the hosts LAPS is enabled on
Seatbelt can also detect LAPS with
If we find the right GPO for it and can access the file we can get more information about the password that gets generated by the policy by parsing it with GPRegistryPolicyParser
This gives us information about the password complexity, minimum and maximum length, lifetime, the name of the account that is being managed by LAPS and whether password expiration protection is enabled or not.
BloodHound has a query to detect the ReadLAPSPassword
privilege which does a good job at detecting users that can read the LAPS password on the hosts that have it enabled on LAPS (LAPS1) but (at the time of writing) doesn't detect the privilege for LAPS2.
Since reading the plaintext password is just a LDAP query and doesn't involve any other resource it's a relatively low-risk action; if we are dealing with LAPS2 and have no way to enumerate whether a user can read the password or not we can just try to read it and see if we get a plaintext value back.
To read a plaintext password from LAPS we can use
To discover which principals are allowed to read the ms-Mcs-AdmPwd
attribute we can check the DACL on each computer onbject
Another great tool is LAPSToolkit which can be used to find delegated LAPS read access
LAPS has a PwdExpirationProtectionEnabled
configuration setting that we can also read from the Registry.pol
file: when enabled, this policy prevents a user or computer setting the expiration date of a password beyond the password age specified in the PasswordAgeDays
setting (which we can also read in the policy file).
If the policy setting is left "not configured" in the GPO, then password expiration protection is disabled by default.
If we compromise a host using its LAPS password, we are able to change the current password's expiration date as a persistence mechanism.
Mind that the timestamp is in EPOC format (we can convert it here) so we'll need to specify the new expiration date in the right format as well
The newly-set expiration date will still be visible to the admins and a manual password reset will also restore the previous expiration date.
For persistence's sake we could backdoor LAPS to quickly get access to the current password even after the refresh.
One idea is to modify the code of the Get-AdmPwdPassword
cmdlet which we can find at C:\Windows\System32\WindowsPowerShell\v1.0\Modules\AdmPwd.PS
. To do this we can download the AdmPwd.PS.dll
and AdmPwd.Utils.dll
files and use a tool like dnSpy
to modify their contents.
A thing we can do is make the cmdlet send the cleartext LAPS password to our server, of course this is really bad OPSEC since everyone can see a normal HTTP GET request but we can get more creative and can use this repo to get the original AdmPws.PS.dll
source code.
Of course changing the contents of this file also changes its signature which can easily be detected.
Now if we execute the cmdlet we should get a hit on our server
Last updated