This issue CVE-2026-0091 has been fixed for Android 14+ in June 2026 Android Security Bulletin. Click here to see the patch
The following is copied from my repo https://github.com/canyie/TransitionPlayer for backup purposes. For more info such as PoC code, please check the original repo.
Writeup
TODO
I’ll complete the writeup when I got a little free time
But before that I have to fight against school work and exams
I decided to complete the section before exams are finished
Good luck to me 😇
Introduction to IApplicationThread
IApplicationThread is a private callback provided to system from apps so that system can use it to send commands (load specified app, notify changes of component lifecycle, etc) to app
It is intended to only usable from system, so there are no permission checks, and security is guaranteed solely by the fact that the object is not obtained by a malicious process. This sounds somewhat similar to the concept of “cookies” or “tokens” in web. Such access control model is called as Capability-based Security
If someone else managed to obtain an IApplicationThread from other processes, they can send arbitrary commands and the victim app would just process the fabricated commands like they were generated by system. In a previous exploit of CVE-2022-20452 the trick was used to perform arbitrary code execution
RemoteTransition
While IApplicationThread should only be passed to system process, it can be unexpectedly sent out of system_server
No one would implement an getIApplicationThreadForApp(String packageName) API that is exposed to any one, it is an obvious security violation
But if IApplicationThread is wrapped in another object and the outer wrapper object gets sent, it is a more likely scenario
RemoteTransition is such a wrapper which contains an IApplicationThread in order to boost priority of app that is running an animation
One user of that API is Launcher3, the default home application on AOSP and Pixel devices, which creates an ActivityOptions using a RemoteTransition and then pass it to startActivity()
While Launcher3 itself does not expose the object to untrusted factors, the system_server sometimes does
CVE-2022-20419 occurred because system_server forwarded caller-passed ActivityOptions to launched app but forgot to strip RemoteTransition, allowing launched app to receive it and load arbitrary code inside launcher process
TransitionPlayer
During shared-element transition animation, a lot of work has to done and communications need to happen between WMCore and WMShell, where WM means Window Manager
You can read this article to understand WMShell
As WMCore and WMShell runs in different processes (WMCore runs in system_server and WMShell runs in SystemUI), they utilize the Binder mechanism to talk with each other
WMCore exposed a Binder registerTransitionPlayer API and WMShell uses it to register its own binder
When animation is started, WMCore calls requestStartTransition and TransitionRequestInfo is passed to remote, which includes the initial RemoteTransition
So if we can replace the transition player, we will be able to retrieve Launcher’s IApplicationThread and achieve arbitrary code execution inside a privileged process
However, registerTransitionPlayer is protected by MANAGE_ACTIVITY_TASKS permission which 3rd party app can’t get
But adb shell can also run untrusted user code, and the shell is granted MANAGE_ACTIVITY_TASKS permission, so luckily we can launch attack from adb shell
Impact
It’s a funny question that what can attackers do through this vulnerability
As most permissions granted to Launcher are also held by adb shell, attackers who already able to execute code under shell identity don’t need to exploit this vulnerability to compromise device
This is more like an educational example project for learning IApplicationThread than an exploit that can be used by malicious app
However, someone may be still be interested in this one
For example, this can be used to extract Launcher’s private files, which can be useful in forensic analysis for malicious Launcher apps without rooting device. Previously this was achieved by exploiting CVE-2024-31317, and my finding reveals another method after the previous one was fixed
This also allows users to use Fabricated Runtime Resources Overlay (FRRO) without first rooting their device, so rootless custom themes is back after CVE-2021-39630 was patched. My exploit has demonstrated this by setting android:integer/config_multiuserMaximumUsers to 100
In addition, the Launcher also hosts the Recents screen component by default and thus is allowlisted for some privileged actions. I think the Launcher is allowed to start arbitrary activity in an existing task regardless of exported/permission settings of launched Activities which might be wanted by some device management tool apps, although I haven’t tested it myself
Test
https://github.com/canyie/TransitionPlayer
Build the project, install generated apk file (if you use the Run button inside Android Studio, turn on “Always install with package manager”)
Run the following command on PC
1 | adb shell app_process '-Djava.class.path=$(pm path top.canyie.transitionplayer | cut -c9-) /system/bin top.canyie.transitionplayer.Main' |
Then launch an arbitrary app by tapping its icon from the launcher
A notification should be sent from the launcher app, and if you are on Android 14+, a fabricated overlay will be injected into the system, so adb shell cmd overlay lookup android android:integer/config_multiuserMaximumUsers should return 100
Fixes
- The animation delegate mechanism has been refactored and IApplicationThread handle is no longer being sent out of WindowManagerService
- Starting from Android 17, call to IApplicationThread will be rejected if it is from non-system. I don’t think it is an effective way to mitigate such exploits as I think attackers can trick ActivityManagerService into making calls with attacker-controlled apk file to target process (although I haven’t tested it myself), but it’s a signal that Android Security Team starting to taking actions
