Microsoft’s Local Administrator Password Solution (LAPS) makes it easy for administrators to securely manage the local account passwords on all their domain-joined computers. But that doesn’t mean its security isn’t open to being challenged. While LAPS has been available since 2015, there has been lots of chatter on the Microsoft TechNet social network, suggesting that the difficulties of using it haven’t all been ironed out. This was brought home to me in a discussion I had with colleague Mark Van Noy who manages the virtual desktop infrastructure at the University of Colorado Boulder in Colorado. Mark piqued my interest in the subject when he emailed me the following comment recently. Mark said that “Microsoft’s LAPS storing the password in clear text with the computer’s AD record has been bothering me for a while now. So, I decided to write up a proof-of-concept PowerShell script on how this could potentially be exploited. My PowerShell does not leverage any component of LAPS; it is straight-up AD accessed information using knowledge of how LAPS works.” I asked Mark for a copy of his script and a brief writeup describing how to use it, and Mark responded with the following, which I’m sharing here for the benefit of our TechGenix readers.
Installing and configuring LAPS
Microsoft’s LAPS is a tool created to remediate the problem of using the same password across multiple servers. LAPS can also significantly help with ensuring greater password complexity by generating up to 64-character passwords randomly for each computer it manages and automatically generating new passwords on a schedule. On the surface, this is a great tool that prevents some of the most common security risks to infrastructure resources: low complexity passwords that get reused and infrequently changed. Functionally, the tool actually does do just what it states it will do, and installing it is straightforward:
The Word documentation that comes with LAPS does an excellent job of walking through how to install and configure LAPS. The LAPS installer needs to be run on each client machine, most likely servers, that will be managed. Since the installer is a conventional MSI, it is simple to distribute and perform a silent install. At least one management computer also needs to have the remaining tools installed to it. From the management computer, a simple PowerShell command that comes with the package is used to extend the Active Directory Schema. The documentation also walks through four custom GPOs that can be applied to managed computers as well as how to secure the OU’s of the managed systems. Finally, there is a short section on enabling auditing of password access. The shortcoming of the auditing is that the 4662 event ID logs the updated AD schema GUID of the password object, which is not human-readable and makes the audit relatively obscure. The documentation’s example shows that CONTOSO\Administrator accessed {bo4b21db-0992-4551-a813-4d8e2a27ff1e}, which probably will not raise any red flags when scanning security logs unless additional tools that recognize that GUID are used.
My concern with LAPS
This brings me to the concern I have with LAPS. The schema extension ties the computer’s local administrative password directly to the AD computer record in plain text. This is the digital equivalent of writing the password on a sticky note and placing it on the monitor. Perhaps a better metaphor would be placing the password for each server on a label placed on each server then claiming it is highly secure because the datacenter is locked. Placing the security credentials with the object they are protecting is questionable at best.
I am certainly no hacker. Not a white hat or black hat; I do not even like wearing hats. However, when I saw what looked like a glaring security issue, I began trying to think of how that might be exploited by someone with ill intent. I wrote a short PowerShell script called Get-LAPSPasswords.ps1 (see listing at the end of this article) as a proof of concept of what I suspect hacker types might do. The script first checks to see if the AD schema has been extended to include LAPS:
If so, then every computer object is iterated over, and any record that has a readable password then has the computer name, password, IP address, and Distinguished Name printed to the screen:
I see this kind of script being used in an attack, such as phishing, where an account has already been compromised. It is an easy way to effortlessly find out what, if any, systems the compromised account has access to. It would be trivially easy to extend this kind of dump to place a key logger or other malicious software onto any systems found to grow an attack. Also, while probably not the most efficient way of pulling the information from the AD, I did measure that it took just under sixteen minutes to touch every record in a domain of over 25,000 computer objects.
In a properly secured environment with zealously guarded administrative accounts running with least privilege, LAPS is likely an excellent tool to help secure resources. Like all security measures and practices, all tools are only as strong as their weakest link. I have lost track of how many compromises I have read about where a Domain Admin or similar credentials were compromised. With that in mind, if LAPS simply encrypted the passwords in the AD while they are at rest, that would certainly help.
Listing of Get-LAPSPasswords.ps1 script
f<# .SYNOPSIS Displays information on all computers in the Active Directory that have a readable LAPS password set. .DESCRIPTION Checks to see if the Active Directory schema has been updated to support Microsoft LAPS. If it has then it pulls in all computer objects in the Active Directory and stores them in a collection variable. Finally, the collection is iterated over and any computers where a LAPS password is set and readable will be returned including the LAPS password. If the computer object does not have a LAPS password set or the user account the script is run under lacks appropriate permissions to read the password then the computer's information will not be returned. Since computer and password information is stored in the same place with LAPS enabled, this script shows all the information needed to log in as Administrator for all computers the account that runs the script has access to that are managed by LAPS. .EXAMPLE .\Get-LAPSPasswords.ps1 Scans the Active Directory for any computers with LAPS passwords readable by the user whom executes the script. .NOTES FileName: Get-LAPSPasswords.ps1 Author: Mark Van Noy Oganization: University of Colorado Boulder Created: 06/04/2020 Version history: 1.0.0 - (06/04/2020) Script created #> # Just to be safe. Was not required on test computer. Import-Module ActiveDirectory function Check-SchemaSupportsLAPS() { # Set a boolean flag to indicate if LAPS extensions exist in the schema. $schemaLAPS = $false # Check to see if the schema has been updated. $schema = [directoryservices.activedirectory.activedirectoryschema]::getcurrentschema() $optionalProperties = $schema.FindClass("computer").OptionalProperties | Select name,oid $mandatoryProperties = $schema.FindClass("computer").MandatoryProperties | Select name,oid # Based on the LAPS documentation the extensions are expected in the OptionalProperties foreach ($property in $optionalProperties) { if (($property.Name -eq "ms-Mcs-AdmPwd") -and ($property.oid -eq "1.2.840.113556.1.8000.2554.50051.45980.28112.18903.35903.6685103.1224907.2.1")) { $schemaLAPS = $true } } # Just in case something unusual happened with the Schema, check MandatoryProperties. if (!$schemaLAPS) { foreach ($property in $mandatoryProperties) { if (($property.Name -eq "ms-Mcs-AdmPwd") -and ($property.oid -eq "1.2.840.113556.1.8000.2554.50051.45980.28112.18903.35903.6685103.1224907.2.1")) { $schemaLAPS = $true } } } # Let the end user know if the Schema has not been extended to support LAPS if (!$schemaLAPS) { Write-Host -ForegroundColor Red "LAPS Schema extentsions are not present." } return $schemaLAPS } function Get-Computers() { # Need to specify properties or it is too slow with too many returned results. # If ms-Mcs-AdmPwd does not exist in the Schema and is specified Get-ADComputer will error out. $all_Computers = Get-ADComputer -Filter * -Properties DNSHostName, ms-Mcs-AdmPwd, IPv4Address, IPv6Address, DistinguishedName # It can take a long time to get all computers so let end user know we are done. Write-Host -ForegroundColor Green "All domain computer objects have been gathered" # Iterate through ALL computers in the active directory and return the # LAPS password for any computers the user is entitled to view. foreach ($computer in $all_Computers) { # If the ms-Mcs-AdmPwd field exists proceed returning information. if ($computer.'ms-Mcs-AdmPwd') { Write-Host -NoNewline $computer.DNSHostName, "`t" Write-Host -ForegroundColor Yellow $computer.'ms-Mcs-AdmPwd' Write-Host -NoNewline $computer.IPv4Address, "`t" Write-Host $computer.IPv6Address Write-Host $computer.DistinguishedName, "`r`n`r`n[------------]`r`n" } } } # LAPS also uses ms-Mcs-AdmPwdExpirationTime that could be useful for attackers # to know how soon the passwords they have gathered will be rotated. # SO add the ms-Mcs-AdmPwdExpirationTime to the property list and output if # knowing the password rotation is important to you. # If the Schema has been extended to support LAPS then start looking for computers. if (Check-SchemaSupportsLAPS) { Get-Computers }
Featured image: Shutterstock
This is a well written article, but I feel it skips over one fundamentally important point: that you are relying on having permission to read the ms-Mcs-AdmPwd property. Microsoft dedicate an entire section of their Operations Guide to finding users with Extended rights and removing them, and adding specific users/groups to have access to this property. As with anything in technology, properly configured (and this is not in any way an onerous task) LAPS is perfectly secure, and when it is then your script would be useless to anyone not on the approved list.
If anyone is further concerned, it’s easy enough to lock down access to the LAPS properties to a single, secure service account and then make use of a third party interface (look up OVERLAPS for example) which implements a more easily managed and granular permissions system and produces easily audited logs.
This is weird. There once was a GPO setting to define the Local Admin password. Later, this GPO setting was disabled because the password was stored unencrypted in the GPO. I guess LAPS was introduced as a replacement for this GPO setting. So you would expect the password to be stored encrypted…
Now there’s a difference between a GPO and AD object, where the GPO is readable by anyone, and AD not so. But still, the Golden Rule is IMHO to NEVER store password unencrypted.
As somebody already pointed out, if an attacker has Domain Admin… that’s game over. That said, it’s possible to set an expiration on LAPS. For instance, I set the expiration on LAPS for 8 hours in my org. I set an even less expiration time for servers… something like every couple of hours. I figure an 8 hour workday is enough time for a Helpdesk tech to use the local admin account on workstations for the day. After 8 hours, the LAPS password rotates. If the Helpdesk needs to pick up with a user the next day, he/she can just lookup the new LAPS password. No biggie. Also, another person mentioned protecting the readability of the LAPS password extended attribute. If you don’t end up using something like OVERLAPS, then just issue Helpdesk a username-a or username.adm account, limit which systems that account can be used on in Active Directory (example server/jumpbox specifically designed for Helpdesk to perform admin functions) and then set ACL to permit that account (or drop account into group) read rights to the attrib while denying everything else except for Domain Admins. Another person mentioned GPO storing local admin password in plaintext. I get it, but even though LAPS stores the password in plaintext the added advantage for LAPS over GPO is that even though you could keep generating random passwords for local admins on machines in an org, the randomly generated password would apply to all machines in the org, unless you end up creating 10’s if not 100’s of GPOs to set various random password across the org. LAPS creates unique, random passwords for every object and thus no password can be used on more than a single machine at a time.
LAPS passwords are stored in Active Directory (AD) and protected by ACL, so only eligible or privileged users (domain admins) can read the password.
I understand your point but I think the bigger concern would be a domain admin account has been compromised. Once that has happened all the bad things are already bad.
I guess it’s kind of like saying no point in backing things up because if someone gets access to the backup system they can just delete the backups.