Using macOSLAPS and retrieving the local admin account from Active Directory

After a laptop theft at my office, I am implementing Joshua Millers macOSLAPS.

The laptop had turned up in the Netherlands after a guy had got in touch with us wehn he had carried out an internet recovery. Because it was still under our DEP account, it pulled down our configuration and so the logon screen had our branding!

Now it’s all very well that our machine is still on DEP but it raised the question why we still had LAP and firmware passwords that were ages old and known by everyone and their dog. So going forward and to be compliant a bit better with security, I had to come up with a solution to rotate the passwords but have them on record.

So implementing macOSLAPS should be fairly straight forward, you’d think but I have some legacy scripts that use a local admin account as well as newer ones such as creating a securetoken for a user as we image the machine before handing it to the user.

Therefore how do you access the local admin password?

It’s kept in 2 places when using macOSLAPS: Active Directory computerobject under ms-Mcs-AdmPwd and in the System keychain

It is neigh on impossible to get it from the System keychain as the machine presents a GUI to enter the admin password so it cannot be done programmatically.

So that leaves accessing Active Directory but the problems I encountered were as follows:

  1. Using dscl presented an error regarding the DC URL when using credentials but then couldn’t access the attribute without them
  2. Trying through Python and the LDAPS3 modulewas problematic (mostly because I’m such a Python novice!)

So it came down to trying to find another way to do this through tools embedded into macOS and accessed through Terminal.

It came down to this:

  1. Use security to get the machines domain password
  2. Use that to carry out an ldapsearch to find the computer object
  3. grep the results to get the ms-Mcs-AdmPwd:
  4. Write this all into a function to easily add into Shell scripts
  5. Go on a search through every single script to find any instance where the local admin credentials were required (mostly securetoken and filevault 2 ones)
# Gets the hostname

# Gets the domain
domain="$(dscl localhost -list "/Active Directory)"

# Do a clever bit here to split the domain into dc= bits

# Gets the machine domain password
dcPwd="$(security find-generic-password -w -s "/Active Directory/$(dscl localhost -list "/Active Directory")" /Library/Keychains/System.keychain)"

# Gets the LAPS attribute from Active Directory
laps="$(ldapsearch -H ldaps:// -x -W -D "$cName" -b "dc=domain,dc=name" "(cn=$cName)" | grep ms-Mcs-AdmPwd: | awk '{print $2}')"

As for the firmware password, I’m planning out a Python script that will have the following steps:

  • Generate an alphanumeric string of 12 characters
  • Save it to a file locally & hash the contents (so we have previous pwds for any eventuality)
  • Attempt to update the FW password
  • If successful, write to the JSS API into an extension attribute