A modern Android powered smartphone is a complex hardware device: Android OS runs on a multi-core CPU – also called an Application Processor (AP). And the AP is one of many such processors of a System On Chip (SoC). Other processors on the SoC perform various specialized tasks — such as security functions, image & video processing, and most importantly cellular communications. The processor performing cellular communications is often referred to as the baseband. For the purposes of this blog, we refer to the software that runs on all these other processors as “Firmware”.
Securing the Android Platform requires going beyond the confines of the Application Processor (AP). Android’s defense-in-depth strategy also applies to the firmware running on bare-metal environments in these microcontrollers, as they are a critical part of the attack surface of a device.
A popular attack vector within the security research community
As the security of the Android Platform has been steadily improved, some security researchers have shifted their focus towards other parts of the software stack, including firmware. Over the last decade there have been numerous publications, talks, Pwn2Own contest winners, and CVEs targeting exploitation of vulnerabilities in firmware running in these secondary processors. Bugs remotely exploitable over the air (eg. WiFi and cellular baseband bugs) are of particular concern and, therefore, are popular within the security research community. These types of bugs even have their own categorization in well known 3rd party exploit marketplaces.
Regardless of whether it is remote code execution within the WiFi SoC or within the cellular baseband, a common and resonating theme has been the consistent lack of exploit mitigations in firmware. Conveniently, Android has significant experience in enabling exploit mitigations across critical attack surfaces.
Applying years worth of lessons learned in systems hardening
Over the last few years, we have successfully enabled compiler-based mitigations in Android — on the AP — which add additional layers of defense across the platform, making it harder to build reproducible exploits and to prevent certain types of bugs from becoming vulnerabilities. Building on top of these successes and lessons learned, we’re applying the same principles to hardening the security of firmware that runs outside of Android per se, directly on the bare-metal hardware.
In particular, we are working with our ecosystem partners in several areas aimed at hardening the security of firmware that interacts with Android:
- Exploring and enabling compiler-based sanitizers (BoundSan, IntSan) and other exploit mitigations (CFI, kCFI, Shadow Call Stack, Stack Canaries) in firmware.
- Enabling further memory safety features (Auto-initialize Memory) in firmware.
Compiler-based sanitizers have no runtime requirements in trapping mode, which provides a meaningful layer of protection we want: it causes the program to abort execution when detecting undefined behavior. As a result, memory corruption vulnerabilities that would otherwise be exploitable are now stopped entirely. To aid developers in testing, troubleshooting, and generating bug reports on debug builds, both minimal and full diagnostics modes can be enabled, which require defining and linking the requisite runtime handlers.
Most Control Flow Integrity (CFI) schemes also work for bare-metal targets in trapping mode. LLVM’s1 CFI across shared libraries scheme (cross-DSO) is the exception as it requires a runtime to be defined for the target. Shadow Call Stack, an AArch64-only feature, has a runtime component which initializes the shadow stack. LLVM does not provide this runtime for any target, so bare-metal users would need to define that runtime to use it.
Enabling exploit mitigations in firmware running on bare metal targets is no easy feat. While the AP (Application Processor) hosts a powerful operating system (Linux) with comparatively abundant CPU and memory resources, bare metal targets are often severely resource-constrained, and are tuned to run a very specific set of functions. Any perturbation in compute and/or memory consumption introduced by enabling, for example, compiler-based sanitizers, could have a significant impact in functionality, performance, and stability.
Therefore, it is critical to optimize how and where exploit mitigations are turned on. The goal is to maximize impact — harden the most exposed attack surface — while minimizing any performance/stability impact. For example, in the case of the cellular baseband, we recommend focusing on code and libraries responsible for parsing messages delivered over the air (particularly for pre-authentication protocols such as RRC and NAS, which are the most exposed attack surface), libraries encoding/decoding complex formats (for example ASN.1), and libraries implementing IMS (IP Multimedia System) functionality, or parsing SMS and/or MMS.
Fuzzing and Vulnerability Rewards Program
Enabling exploit mitigations and compiler-based sanitizers are excellent techniques to minimize the chances of unknown bugs becoming exploitable. However, it is also important to continuously look for, find, and patch bugs.
Fuzzing continues to be a highly efficient method to find impactful bugs. It’s also been proven to be effective for signaling larger design issues in code. Our team partners closely with Android teams working on fuzzing and security assessments to leverage their expertise and tools with bare metal targets.
This collaboration also allowed us to scale fuzzing activities across Google by deploying central infrastructure that allows fuzzers to run in perpetuity. This is a high-value approach known as continuous fuzzing.
In parallel, we also accept and reward external contributions via our Vulnerability Rewards Program. Along with the launch of Android 13, we updated the severity guidelines to further highlight remotely exploitable bugs in connectivity firmware. We look forward to the contributions from the security research community to help us find and patch bugs in bare metal targets.
On the horizon
In Android 12 we announced support for Rust in the Android platform, and Android 13 is the first release with a majority of new code written in a memory safe language. We see a lot of potential in also leveraging memory-safe languages for bare metal targets, particularly for high risk and exposed attack surface.
Hardening firmware running on bare metal to materially increase the level of protection – across more surfaces in Android – is one of the priorities of Android Security. Moving forward, our goal is to expand the use of these mitigation technologies for more bare metal targets, and we strongly encourage our partners to do the same. We stand ready to assist our ecosystem partners to harden bare metal firmware.
Special thanks to our colleagues who contributed to this blog post and our firmware security hardening efforts: Diana Baker, Farzan Karimi, Jeffrey Vander Stoep, Kevin Deus, Eugene Rodionov, Pirama Arumuga Nainar, Sami Tolvanen, Stephen Hines, Xuan Xing, Yomna Nasser.