Intro
This is another attempt as part of my @vr_progress to hack my old, unpatched OnePlus phone which didn’t get any updates for years.
This time I chose CVE-2022-20201, a crafty little bug hiding in one of the subsystems used by Android’s package manager.
This vulnerability allows the leaking of memory from the installd
process/daemon.
The Fix
The fix was introduced in InstalldNativeService::getAppSize()
, in the Pixel June 2022 Bulletin. They didn’t provide the patch that fix it in the advisory, but after some digging I found it myself hehe
Commit 81061238c19d7ebabb453697a8c643324cf6c68e:
It verifies that the packageNames
and ceDataInodes
vectors have the same number of elements. These arguments can be controlled by the client that communicates with this service over IPC.
Analysis
Scrolling down the implementation of InstalldNativeService::getAppSize()
reveals a loop that iterates over the elements of packageNames
but also assumes that ceDataInodes
has the same length:
By sending a transaction over the binder driver/IPC with mismatched sizes of packageNames
and ceDataInodes
(i.e: 300 packages, 1 inode), it’s possible to exploit this vulnerability, leading to an Out-of-Bound access outside of the boundaries ceDataInodes
.
InstalldNativeService
InstalldNativeService
is a system-level service in Android that handles various storage and package management tasks. This service operates with elevated privileges and communicate with system_server
, making it a critical component in the security architecture of Android.
Reachability
This code path is reachable from PackageManagerService.java
, which uses the InstalldNativeService
and crafts queries to it.
Unfortunately I couldn’t find a gadget that creates those arrays/vectors in two different sizes(the only occurrences I found were with vectors of which I cannot manipulate their sizes to be different). So, for start, I crafted a PoC in C++ that communicates directly with the service over IPC just like PackageManagerService does.
Another important thing to note is that the In the methods in InstalldNativeService
are using the ENFORCE_UID(AID_SYSTEM);
macro, which validates that the calling process is system_server
. That means we cannot run the poc binary as untrusted_app
/ we will need to be with the permissions of system_server
(or higher/root).
PoC
While the OOB read happens, we also need a way to retrieve the information that was accessed. After digging deeper into the code flow, I found that the resolve_ce_path_by_inode_or_fallback()
function prints out the ‘node’ in case of failure. This node is an element from our ceDataInodes
vector.
In logcat, the leaks appears in the form of an “inode”:
12-12 17:56:29.578 1145 27226 W installd: Failed to resolve inode 502817544320; using /data/data/com.expl.cve_2022_20201
12-12 17:56:29.579 1145 27226 W installd: Failed to resolve inode 502817544336; using /data/data/com.expl.cve_2022_20201
12-12 17:56:29.579 1145 27226 W installd: Failed to resolve inode 502817585760; using /data/data/com.expl.cve_2022_20201
The following snippet demonstrates leaking memory from InstalldNativeService
and pulling it from logcat:
Output:
At first, it discloses a few strings, probably related to Android’s Parcel , or Intent (strings like ‘Action’, etc.), followed by heap pointers :D
To verify, I read the memory mappings of the installd daemon, to see if the leaked address are in the range of the mapped memory of the process:
OnePlus6T:/data/local/tmp $ cat /proc/$(pidof installd)/maps
606f742000-606f757000 r--p 00000000 08:0d 689 /system/bin/installd
606f757000-606f798000 --xp 00015000 08:0d 689 /system/bin/installd
606f798000-606f799000 rw-p 00056000 08:0d 689 /system/bin/installd
606f799000-606f79c000 r--p 00057000 08:0d 689 /system/bin/installd
...
7511400000-75123ce000 ---p 00000000 00:00 0
75123ce000-75123d0000 rw-p 00000000 00:00 0
75123d0000-7512400000 ---p 00000000 00:00 0
7512400000-7512800000 rw-p 00000000 00:00 0 <- leaks were from here
7512808000-75136b8000 ---p 00000000 00:00 0
75136b8000-75136ba000 rw-p 00000000 00:00 0
75136ba000-7513808000 ---p 00000000 00:00 0
...
7ff9a9c000-7ff9abd000 rw-p 00000000 00:00 0 [stack]
Conclusions
Even though I couldn’t find a way to reach it from PackageManagerService.java
, I think it was still a fun experience :)