Android 14 Still Allows Modification of System Certificates
Tim Perry recently claimed in an article that “Android 14 blocks all modification of system certificates, even as root”. This sparked significant discussion on Hacker News. Thankfully my tests show that it is still possible to adjust the system certificate store in Android 14.
Update Sept. 15 2023, 6:34 AM Pacific: After I’d already written this post but before I posted it, it looks like Perry already discovered that he can solve this problem for his purposes by bind mounting the
/apex/ mount back to the
/system/ partition in the namespace of
zygote and the children of
zygote. The core of the idea came from a Mastodon thread with @tbodt and @tmw. This post still provides a slightly alternative method, so it is still relevant, as well as still being relevant to the broader discussion.
Users Can Still Revoke System Certificate Trust Through the Settings App
The headline of Perry’s article asserts that even root users cannot “modify” the system certificates at all. In fact, even in Android 14, all users can still remove certificate authorities (CAs) from the list of trusted CAs in the Settings app, just like they could in previous versions of Android.
To confirm that the Settings app still works, I used an Android 14 emulator to revoke trust from all the Amazon and Starfield Technologies CAs. I then accessed Amazon Trust Services Repository’s test pages using Chrome. They failed to load due to an untrusted CA. I also confirmed the revocation of trust persists across reboots.
Based on this, I think the headline of Perry’s article is overly strong. Most of the more impassioned comments on Hacker News took the headline’s strong claim at face value. It is important to be clear: users still retain ultimate control over the trust anchors in Android 14.
Developers Can Still Add Trusted System Certificates via ADB
When I revisited the article after reading Perry’s comments on HN and Mastodon, it became clear that his primary concern was the inability to easily add temporary system CAs via ADB. This feature is critical for his product, HTTP Toolkit. It is also critical for many other security researchers and app developers, myself included.
While Perry correctly notes that the traditional method no longer functions with Android 14, the feature hasn’t been discarded.
I initially anticipated a deep dive into APEX and its signing intricacies. Thankfully, a simpler, albeit somewhat crude, workaround exists for this as a proof-of-concept. This is not suitable for production use. For a production environment, it’s crucial to carefully limit SELinux and tmpfs security options to grant only the minimal privileges. Alternatively, one can construct a properly signed repacked APEX.
# These commands assume a root shell.
# Deal with the fact that there will be executables on the /apex tmpfs
# !! Not suitable for production usage !!
mount -o remount,exec /apex
# Make a copy of the current conscrypt APEX contents
cp -r -p /apex/com.android.conscrypt /apex/com.android.conscrypt-bak
# Lazily unmount because conscrypt might be in active use
umount -l /apex/com.android.conscrypt
rm -rf /apex/com.android.conscrypt
# Put contents of conscrypt APEX on /apex tmpfs mount
mv /apex/com.android.conscrypt-bak /apex/com.android.conscrypt
# Soft userspace reboot to get everything back into a consistent state
# Clear system trust anchors, if desired
mv /apex/com.android.conscrypt/cacerts /apex/com.android.conscrypt/cacerts-bak
Executing these steps clears the system CA list in the “Trusted credentials” section of the Settings app - the result desired in the original post.
This breaks through the mount namespaces by re-writing the directory entry for
/apex/com.android.conscrypt on the
/apex tempfs. The actual filesystem is shared between all namespaces, so overwriting this mount point has an immediate effect visible from all namespaces, even though the unmount event itself is not propagated. Hat tip to @tbodt on Mastodon for pushing me to dig into this until I fully understood it.
After this setup, adding trusted certificates is almost identical to prior versions. The only change is using /apex/com.android.conscrypt/cacerts as the destination path. I confirmed this by adding my personal CA as a system trust anchor.
This process only necessitates root access and doesn’t require altering /system. If you need persistence beyond a single boot, you’ll need to modify the /system partition, just as in older Android versions. Be prepared for the slight complexity of either deleting or repacking the com.android.conscrypt APEX. I leave that as an exercise to the reader.
Verdict: All Is Well That Ends Well
Yes, Android 14 has altered the behavior of its system certificate store. But users’ freedoms remain intact. The modifications just add a layer of indirection facilitating over-the-air updates to the certificate store. Given Android’s update delays have previously hurt CAs like LetsEncrypt, this is a positive move. I can see how the new layer may seems complicated at first, but it does have to meet some pretty tough constraints1.
Kudos to Perry for highlighting this change. Various tools will need to be updated to work with Android 14. Once that is done, I am optimistic that security researchers, developers, and reverse engineers will retain their previous capabilities to tinker and make.
I am friends with Googlers and former Googlers, but I have never worked for Google. I have previously worked as a security-focused developer for a small Android OEM, which is why this caught my attention. I do not currently work for anyone, so these opinions can only be my own.
Most notably, it must maintain security while allowing for different organizations in different positions in the physical smartphone supply chain to sign different parts of the Android system image. If you’re curious about theses trade-offs, Google discusses the alternative designs they considered here. ↩