Circumventing MacOS FileVault Autologin Restrictions

I was recently working on a project to get the Docker daemon to run on boot on some MacOS systems. If you've tried to do this before, you know the internet doesn't really offer any solutions to the problem other than do a bunch of brew installs, manually apply some incredibly unreliable complex networking modifications, and install a third party hypervisor which is also apparently unreliable. My project was to find a better way to do that while still using the official upstream Docker application for Apple systems.

Sounds like fun, right?

I tried a lot of solutions to the problem, but the foundational reason that most solutions don't work is that the Docker daemon for MacOS requires a GUI (why?!) and MacOS doesn't let you launch GUI apps in the background. In other words, you _must_ be logged in run any apps with a GUI. So, the first problem to be solved was to automatically log in. Okay, moving right along. Just flip the switch for Autologin to the service account you want to use. Check.

Now, we'll just add the Docker application to the Login Items list so it gets launched on autologin, and we have Docker running on boot. Then, to keep things a tad more secure, we'll use an applescript script to lock the GUI after autologin has occurred. Not the most elegant solution, but we're trying to automate Apple systems after all. You can only get so far with that sandbox.

However, per the security team (and various security certification standards), _all_ systems much have full disk encryption enabled. Okay, great. This isn't too bad to do. Just flip the switch on FileVault. Check.

But wait, you may notice if you reboot the system at this point that autologin no longer works. If you check the autologin configuration again, you will see it is greyed out now.

The Problem

Since Apple introduced FileVault encryption, enabling it has forcibly disabled autologin capabilities. This means that if for some reason you must have FileVault enabled (for example: SOC 2 compliance), you can no longer use autologin and thus you cannot use the Login Items functionality to automatically launch applications on boot.

The Solution

After a lot of fruitless research, I arrived at the conclusion that I might be able to solve this problem if I could reverse engineer the autologin process so I could manually re-enable it when the System Preferences showed support for it to be disabled. My hunch was that Apply only "disabled" autologin support through making unavailable the UI for configuring it. It turns out that that hunch was right!

After a couple more days of research, I concluded that two operations were performed by Apple's process to enable autologin:

  1. The autologin user is added to the /Library/Preferences/com.apple.loginwindow.plist configuration database

  2. The user's password is encrypted and written to /etc/kcpassword

So all we have to do is add the user to the configured database so the autologin process tries to login as that user on boot, and then store the user's password in Apple's proprietery "encrypted" format under /etc/kcpassword so the process knows what the user's password is.

Updating com.apple.loginwindow

Out of band modifications of the com.apple.loginwindow config database is simple using the MacOS-provided defaults command. Just run:

sudo defaults write /Library/Preferences/com.apple.loginwindow.plist autoLoginUser <username>

Without the next step though, this step doesn't do anything, so let's get to it!

Generating /etc/kcpassword

This was the tricky part. When I first started this, I did not know how Apple encrypted this file. It didn't look too hard though, as changing the account password yielded similar changes in the byte counts of this file, in 12 byte increments. To save time, I did some reasearch on this and happened upon an old post from 2007 by Gavin Brock wherein Gavin had reverse engineered Apple's "encryption" algorithm and written up a perl script to encrypt and decrypt values.

In short, here are the rules for the algorithm:

  1. The password is XORed byte by byte with a hardcoded set of 11 bytes, unchanged for about 20 years, in a loop. So if your password is 12 bytes long, the 12th byte will be XORed with byte 0 in the hardcoded byte array.

  2. The file is written in 12 byte chunks. For example, if your password is 15 chars long, it will write 12 bytes, then 3 bytes, plus a null byte to terminate the string, plus 8 more random bytes, all XORed with the 11 hardcoded integers, for a total of 24 bytes [12 * 2]).

Here are a few ways in C to define the hardcoded XOR bytes:

// In Hexidecimal if you prefer
int hexbytes[] = {0x7D, 0x89, 0x52, 0x23, 0xD2, 0xBC, 0xDD, 0xEA, 0xA3, 0xB9, 0x1F}

// Or in decimal if you prefer...
int decbytes[] = {125, 137, 82, 35, 210, 188, 221, 234, 163, 185, 31}

Rather than writing up a huge post about how exactly this works, I wrote a simple C program that goes both directions on the kcpassword file. If you pass it a path to a file, it decrypts it. If you give it a password, it encrypts it and writes to stdout (so you can redirect into a file). It compiles on MacOS as well as linux, so it should be able to work just about everywhere.

To set an autologin password for your user on a FileVault encrypted system, run the following:

./kcpasswd "<account password>" | sudo tee /etc/kcpassword

Summary

With those two steps done, you should be able to reboot your system and it should bypass the login screen, automatically logging into the account you set com.apple.loginwindow "database", with the password set when the /etc/kcpassword file was set.

Last edited: 2021-01-04 23:22:37 UTC