Recently, several projects, including Akijo’s and n00py’s work, have emerged that exploit misconfigurations of Microsoft’s Local Administrator Password Solution (LAPS) in Active Directory environments.
This begs the question: how to make sure their LAPS implementation is secure?
It’s a solution to manage passwords for privileged accounts. If this breaks, like cpassword values in Group Policy broke before, or if it breaks because of misconfiguration, people might start fearing for their job (although job security is a different field, altogether).
So, let’s break this down:
1. When you don’t delegate viewing the LAPS passwords, you’re fine
The Local Administrator Password Solution (LAPS) uses a new attribute for computer objects; mS-MCS-AdmPwd. The password for the local administrator account on the domain-joined device is stored as clear-text.
This attribute is marked as a confidential attribute. This means the following accounts have access to the clear-text passwords:
- Members of the Domain Admins group, by default
- SYSTEM on the domain-joined devices in scope for LAPS, per device, by default
- User accounts and members of any group with “All Extended Rights” to an Organizational Unit
- User accounts and members of any group with “CONTROL_ACCESS” or “READ” permissions on the mS-MCS-AdmPwd attribute
To get the information we need for this task, we rely on the awesome PowerShellAccessControl Module from the Microsoft Script Center by Rohn Edwards.
After you install this PowerShell module, the following lines of Windows PowerShell show who has permissions to view LAPS passwords for a particular Organizational Unit (OU):
Import-Module PowerShellAccessControl
Import-Module ActiveDirectory
$OU = Get-ADOrganizationalUnit "OU=Servers,DC=mim,DC=local"
$OU | Get-AccessControlEntry -ObjectAceType 'ms-Mcs-AdmPwd' -ActiveDirectoryRights ExtendedRight
Change the Distinguished Name (DN) of the Organizational Unit (OU) on line 3 to view permissions for another OU.
2. When you run Windows Server 2003 Service Pack 1 and up Domain Controllers, you’re fine
The security framework surrounding LAPS is based on the mS-MCS-AdmPwd attribute being marked as confidential. However, as Microsoft KnowledgeBase article 922836 points out, the only way to make sure confidential attributes work as intended, you need to:
- Make sure any Windows Server 2003-based Domain Controllers have Windows Server 2003
SP1 or a later version installed - Upgrade or remove any Windows 2000-based Domain Controllers
Only Domain Controllers that are running Windows Server 2003 SP1 or a later version enforce the read access check for confidential attributes. The confidential attributes feature is tied to the installation of Windows Server 2003 SP1 or a later version. This feature does not depend on whether a domain or a forest functional level is enabled.
Use the script below to find any incompatible Domain Controllers:
Import-Module ActiveDirectory
$DCs = Get-ADDomainController
$DCs | select HostName, OperatingSystem, OperatingSystemHotfix, OperatingSystemServicePack, OperatingSystemVersion
3. When all devices in scope have the LAPS Client Side Extension installed, you’re fine
Windows, by default, doesn’t know how to interpret the Local Administrator Password Solution (LAPS) group policy settings. For this functionality, the LAPS Client Side Extensions (CSE) need to be installed. Without the CSE, the domain-joined device will not change its local password.
The below script checks domain-joined devices for the presence of the LAPS CSE and checks the signature for the AdmPwd.dll file, so maliciously overwritten files can be detected, if the following requirements are met:
- The account used to run the script has administrative privileges on the device it is run on and on every device in scope
- WinRM is available, which means that TCP 5985 / TCP5986 is open and the WinRM service runs on every device in scope
First, get the file hash from a device that runs the a correct version of the LAPS client:
Get-item "C:\Program Files\LAPS\CSE\AdmPwd.dll" | Get-FileHash
Then, run the following lines of Windows PowerShell, replacing the value for ValidHashes and Organizational Units (OU) to check:
Import-Module ActiveDirectory
$ValidHashes = @(
"16B20A3AD485B4FBBE3028C7E743B226DB21EA93CACC8B3D7D7D4A731BF02333"
)
$OUs = @(
"OU=Servers,DC=domain,DC=tld";
"CN=Computers,DC=domain,DC=tld"
)
$Report = @()
Foreach ($OU in $OUs){
$ADComputers = Get-ADComputer -SearchBase $OU -Filter *
Foreach ($ADComputer in $ADComputers){
$Session = New-PSSession -ComputerName $ADComputer.DNSHostName
$Entry = Invoke-Command -Session $Session -ScriptBlock {
$LapsIsInstalled = $null
$FileHash = $null
$WmiObject = $null
$WmiObject = Get-WmiObject -Class WIN32_PRODUCT -Filter {Name='Local Administrator Password Solution'}
[string]$LapsIsInstalled = ($WmiObject | Measure-Object).Count -eq 1
If ($LapsIsInstalled -eq "True"){
$FileHash = (Get-item "C:\Program Files\LAPS\CSE\AdmPwd.dll" | Get-FileHash).Hash
If ($ValidHashes -contains $fileHash){
$ValidHashesIsValid = "True"
}else{
$ValidHashesIsValid = "False"
}
}else{
$ValidHashesIsValid = "NA"
}
$Entry = New-Object -Type psobject -Property @{
"Laps is Installed" = $LapsIsInstalled
"Laps Version" = $WmiObject.Version
"Hash of installed AdmPwd.dll" = $FileHash
"Hash is Valid" = $ValidHashesIsValid
}
return $entry
}
$Report += $Entry
Remove-PSSession -Session $session
}
}
$Report | select PSComputername, "Laps is Installed", "Laps Version", "Hash of installed AdmPwd.dll" | Out-GridView
4. When you don’t fiddle with the confidentiality bit, you’re fine
For the above script, the assumption is made that the mS-MCS-AdmPwd attribute is still marked as confidential, as this is the basis for the security framework surrounding LAPS.
Attributes can be marked and unmarked using the SearchFlags value for an attribute in the Active Directory schema. I don’t see any reason to fiddle with the SearchFlags value for the mS-MCS-AdmPwd attribute, but you can check whether the default value is still set, using the below script:
Add-Type -TypeDefinition @'
[System.Flags]
public enum SearchFlags
{
fATTINDEX = 0x0001,
fPDNTATTINDEX = 0x0002,
fANR = 0x0004,
fPRESERVEONDELETE = 0x0008,
fCOPY = 0x0010,
fTUPLEINDEX = 0x0020,
fSUBTREEATTINDEX = 0x0040,
fCONFIDENTIAL = 0x0080,
fNEVERVALUEAUDIT = 0x0100,
fRODCFilteredAttribute = 0x0200,
fEXTENDEDLINKTRACKING = 0x0400,
fBASEONLY = 0x0800,
fPARTITIONSECRET = 0x1000
}
'@
(Get-ADObject -SearchBase (Get-ADRootDSE).SchemaNamingContext -Filter {LdapDisplayName -eq "ms-Mcs-AdmPwd"} -Properties *) | Select Name, @{n='SearchFlags'; e={[Enum]::Parse('SearchFlags', $_.SearchFlags)}} | Format-List
When the ouput contains the SearchFlag fCONFIDENTIAL, then you’re fine.
5. When your Active Directory replicates fine, you’re fine
In the highly unlikely case, your Active Directory Domain Controllers don’t replicate fine, you might run into problems when deploying the Local Administrator Password Solution (LAPS). While the schema can only be extended by a member of the Schema Admins group, the schema change containing the two attributes might not end up at certain Domain Controllers. When devices communicate to these Domain Controllers, their LAPS CSE might not work correctly.
When changes to group memberships and/or delegation of control permissions don’t get replicated, people who expect to be able to view the LAPS attributes might find themselves dead in the water, when connected to some Domain Controllers.
Use repadmin.exe regularly to determine non-replicating Domain Controllers and other Active Directory replication issues.
6. When people can’t change the mS-MCS-AdmPwdExpirationTime attribute, you’re fine
The mS-MCS-AdmPwdExpirationTime attribute for computer objects stores the LAPS password reset date/time value in integer8 format. This value is blank until the first time the device sets the LAPS password. When the LAPS password is changed, the value in this attribute is updated based on the LAPS password change threshold (Password Age in days) configured in the group policy settings for LAPS.
To increase the chances of brute-forcing the device or maintaining a foothold on the device by malicious person, the person might try to set a value for the mS-MCS-AdmPwdExpirationTime attribute that is far in the future.
In LAPS, the setting Do not allow password expiration time longer than required by policy is aimed to mitigate this scenario. Set it to Enabled. Only when it’s Disabled or not configured, the above malicious trick can be pulled.
Concluding
You’re fine.
Further reading
Security Thoughts: Microsoft Local Administrator Password Solution (LAPS)
Microsoft LAPS Security & Active Directory LAPS Configuration Recon
Using the Confidentiality Bit to Hide Data in Active Directory
How to mark an attribute as confidential in Windows Server 2003 Service Pack 1
Dumping LAPS Passwords from Linux
Malicious use of Microsoft LAPS
The PowerShellAccessControl module is no longer available in the Microsoft Script Center / PowerShell Gallery, but you can find it on GitHub.
Download the files and place them sowehere (unzipped) on your device or server.
Install it using the following lines of Windows PowerShell in an elevated window, with the location of where you downloaded these files:
New-Item "C:\Program Files\WindowsPowerShell\Modules\PowerShellAccessControl" -ItemType Directory -Force
Copy-item *.* "C:\Program Files\WindowsPowerShell\Modules\PowerShellAccessControl\" -Force
Then, import the module using the following line of Windows PowerShell:
Import-Module "C:\Program Files\WindowsPowerShell\Modules\PowerShellAccessControl\PowerShellAccessControl.psm1"