Lilac: The Device Policy Story
An epic writeup of how I created lilac, the ChromeOS device policy editor.
Posted on April 4, 2023
Some background
If you aren't coming from the lilac github repo, you'll definitely want to read through this section. Check the repo out after reading this post too!
Lilac is a device policy editor for ChromeOS that exploits a flag called --disable-policy-key-verification
in Chrome.
It basically gobbles up a device policy blob, changes the policies, and spits it out with a brand new signature and changed values, using the boringssl
library along the way. Most of the signature code was taken from
the Chrome equivalents, just because at that point I was thinking that this wasn't going to work out and to maximize the chances of it working, I took the signature code directly from Chrome.
How to actually compile it
For all the people out there who tried (and failed) at compiling lilac, here's a less cryptic guide:
- Install protobuf
yay -S protobuf
for Arch and Arch-based distro users like me- All other distros: You have to figure it out, there have been issues with compiling on other distros for some reason
- Compile boringssl (included in the repo)
cd boringssl
mkdir build; cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/path/to/lilac/boringssl/install -DBUILD_SHARED_LIBS=on
make
make install
- Compile the protobuf files
protoc proto/* -Iproto --cpp_out=.
in the lilac dir
- Run
make
and hope for the best
To actually run it, you may need to export LD_LIBRARY_PATH="boringssl/install/lib"
if you get undefined symbol errors.
Discovering the flag
One night a few months ago I was trying to see if I could set device policies with Pollen. That obviously failed, but I was looking at Code Search too and I saw a list of all the policy sources.
One of those interested me the most: POLICY_SOURCE_COMMAND_LINE
. While jumping around source files trying to figure out how to set a policy with this source, I saw the golden flag: --disable-policy-key-verification
.
I moved on at first, trying to see if I could set policies via the command line. Unfortunately setting policies via command line was only possible on Android, but I decided to mess around with a policy editor Rory
McNamara made for an old root escalation to see if I could get something to work.
Wait, I see the Guest Mode button!
After looking through the protobuf bindings some more, I was able to (kind of) morph the old Rory policyeditor into one that supposedly sets the guest mode policy (see in the source tree here).
I didnt know it at the time, but this broken code basically erased all policies from the policy blob. I continued on by overwriting the policy blobs in /var/lib/devicesettings
with the patched one, intercepting
/opt/google/chrome/chrome
to add --disable-policy-key-verification
, and restarting. As Chrome loaded, there was a short period of time where I couldn't move the mouse or input on the keyboard, but could see the
guest mode button sitting there. And then, of course, I got kicked to OOBE because the policy signature verification failed (according to logs). At first I was puzzled, as --disable-policy-key-verification
was
supposed to fix that.
How the policy blob signature system works
But after diving back into the Chromium code (the Mercury Workshop members sometimes joke about how much I use Code Search), it actually disables the verification of the signature of the signature
of the policy blob. This may sound weird at first, but I think this is actually a decent system. The policy blob gets sent with a signature to make sure it wasn't tampered with, but the policy blobs must come from Google
so the signature of the policy blob is signed by Google and sent along with the policy blob. At this point, I didn't feel like learning how to create my own signature with a different library, so I looked in the Chromium code again,
this time in where the --disable-policy-key-verification
flag was meant to be used: tests.
I dived
through
the
code
until I got to the actual signing implementation. Then I condensed it all down into
one function (see in lilac source).
Still doesn't work... Wait a minute.
I then once again replaced the policy files and restarted, and got kicked back to OOBE again. Back to Code Search I went and this time looked at how the policies are loaded.
I soon realized my mistake: I forgot to export owner.key
to overwrite the owner.key
stored along with the policy files. After a quick fix
(it's fitting that the commit is named "oops"), I was up and running with a fancy new login screen background and guest mode enabled. But of course, anything in C doesn't come without segfaults. Oh, and I found out that my code was broken a while later.
A ChromeOS VM whose policy has been patched with lilac
Credits
- lilac: r58playz
- moral support to r58playz while they slowly went insane while reading chromiumOS code: all of Mercury Workshop and thenamedhuman from TN