For the complete documentation index, see llms.txt. This page is also available as Markdown.

Breaking the Android Sandbox and How to Defend Against It

A deep-dive into CVE-2024-0044 and how Runtime Application Self-Protection can stop it

In this blog, we will be analyzing CVE-2024-0044, a critical run-as any app vulnerability that sent shockwaves through the Android security community.

Marked as High Severity by Google, this bug allowed attackers to bypass the fundamental Application Sandbox on Android 12, 13 and 14 using a surprisingly simple command injection technique and access any app's internal files without even rooting the device.

We will observe how a tiny validation error in the Android Framework allowed anyone with ADB access (Remote or Physical) to trick the system into thinking any production app was debuggable. We will explore what this meant for millions of user devices where private data suddenly became public property for anyone with a USB cable.

Finally, we will look beyond the OS patch. We will demonstrate how Runtime Application Self-Protection (RASP) technologies, like Talsec freeRASP, could have prevented this exploit from succeeding even on non-rooted unpatched devices by detecting the anomaly the moment the attacker tried to break in.

How Android Apps Run

Android applications run in one of two build states:

  • Debuggable

  • Production

Debuggable state is designed for developers. It grants deep access to the app's data and internal files. However, this can be dangerous in the hands of an attacker, as it exposes the configuration files that control the app's behavior.

Production state is for apps distributed publicly. These builds are not debuggable, ensuring that private internal files are protected (at least on non-rooted devices). We simply need to set android:debuggable="false" in the manifest file to secure our authentication tokens and databases.

This distinction was considered a hard security boundary. CVE-2024-0044 broke it.

Package installation

Another important thing to understand is how the package manager installation process works so the OS creates a record of the installed package.

The Android OS maintains multiple registries and XML files containing configuration for processes on the device. One of these files is packages.list - registry that stores info about installed packages. Since it resides in the privileged /data/system/ directory, it can only be written to by the Package Manager.

It stores the configuration of each app on the device in this format:

The Package Manager writes these lines through Settings.writePackageListLPr by concatenating the fields. This straightforward appending of fields turned out to be a massive vulnerability in the context of this bug.

The run-as binary

To understand the exploit, we need to look into the tool it tricks the run-as binary. This is an Android command-line tool that is designed specifically for debuggable apps of the device that acts as superuser of data of the debuggable apps.

What does it actually do ?

The run-as tool allows a user to execute commands as a target application.

  • It grants read/write access to the data directories of the target app.

  • It also grants the shell user ability to impersonate the target application's UID's user.

Due to these privileges provided by this tool , it is only allowed to work for debuggable apps.

Parsing

To know if the app is debuggable or not, the run-as binary checks the debuggable_flag field of the packages.list file and totally relies on that for this validation.It also parses the Data Directory and UID fields from the packages.list file to call setuid and setgid without further verification

How it decides

To decide whether an app is debuggable, run-as checks the debuggable_flag field in packages.list and fully trusts it. It also parses the Data Directory and UID fields from that file and uses them in setuid and setgid calls without further validation.

CVE-2024-0044

How it worked

The vulnerable code can be traced to commit I840c9c9af5752b3901d4719a13e7908faa43ab04.

Here the first half of the vulnerability lies in the part where there is no verification apart from the length and null checking of the packageName. Also ADB is allowed to override the installing Package Name of the package installed giving the attacker ability to inject the payload using the -i flag.

The -i flag in the command pm install .... allows the user to set the installing Package name of a package. By default, it is com.android.vending that refers to the Google PlayStore. This is written as the last field of the configurations inside the packages.list file discussed before .

The second half of the vulnerability lies inside the run-as tool parsing that does not parse using any XML parser but parses the file line by line thinking every newline means configuration a new app. A person can simply inject a carefully crafted malicious payload to change the configurations of an application inside the device.

Proof of Concept

For the POC, we are using a physical Xiaomi Redmi Note 11 5G device with Android 13 which is affected by this vulnerability.

  • In the POC video, we can observe that on a normal adb shell we are not allowed to iterate into the private directories of non-debuggable apps like the production grade Google Chrome app and the run-as tool denies that the app is non-debuggable.

  • Then we run exploit binary on the local system attached using wireless debugger with the Android phone.

  • Using the injection vulnerability inside the Package Manager's package.list file and the lack of validation checks allows us to change the contents of this file according to our desire.

  • In a normal adb shell, we cannot enter the private directory of a non-debuggable app such as Google Chrome.

  • The run-as tool correctly denies access because the app is not debuggable.

  • Next, we run the local exploit binary while connected to the phone through wireless debugging.

  • The payload abuses the Package Manager write path and rewrites packages.list with attacker-controlled data.

  • After that, run-as parses the malicious entry and treats Chrome as debuggable.

  • Because the payload injects a fake alias and fake directory path, we can use run-as <fake_name> to enter the target app’s private directory.

  • This makes non-debuggable apps accessible even on non-rooted devices.

  • The demo also uses tar -cf streaming to copy data into /data/local/tmp, where it can be pulled through a normal ADB shell.

Exploit development

The exploit POC for the vulnerability contains mostly helper functions which run ADB shell commands to let the attacker choose any package from the available devices and prepare the exploit payload for that specific package.

The main payload looks like this:

Here is what it does:

  1. It opens an adb shell session.

  2. It uses a crafted pm install -i value with an injected newline.

  3. It inserts the target app’s private directory and UID into a forged entry.

That produces content like this inside packages.list:

Once that entry exists, run-as grants privileges based on the forged record.

Mitigation Patch

Google mitigated this vulnerability by adding some more verification in the function responsible for package installation using ADB pm command. It not just checked the length of the installer package name but also validated the name using a separate function.

This new function restricted the use of digits and underscores as the first character of the package name. Also it banned use of special characters like \n in the name causing our malicious payload for this vulnerability to fail.

This update in the Android OS code was distributed on millions of devices soon to prevent any severe effects of this exploit in any device globally. But what if the attacker himself owns the device ? He will not update the device and then easily can have a privileged shell inside a non-rooted device.

Impact on Production Apps

The most alarming aspect of CVE-2024-0044 wasn't just that it existed, but where it struck - the production grade apps. For years, the security model of Android relied on a simple rule: "If the app is not debuggable, its data is safe on a non-rooted device." This vulnerability shattered that assumption.

By tricking the OS into treating a production app as debuggable, attackers gained the same level of access as the developers of the application. This meant the Application Sandbox which is the primary wall separating one app's data from another effectively vanished and the security rule of the Android OS broke.

Banking and fintech impact

The impact was catastrophic as financial apps rely heavily on the sandbox to protect local storage. With run-as access, an attacker could:

  • Bypass biometrics: Modify local configuration files to disable client-side checks or flags.

  • Cookie stealing: Standard Android sandboxing makes this folder impossible to read. With run-as, the attacker can simply copy the Cookies database file and steal important webviews data of the application.

Gaming and digital goods impact

Mobile games often store game state locally to ensure easier offline experience.

  • Economy Exploits: Attackers could easily modify local XML files to give themselves unlimited currency or premium items and other advantages regarding in-app purchases.

  • Save File Tampering: Competitive integrity was compromised as users could edit their stats or inventory directly in the file system without needing a rooted device.

Why conventional defenses failed

Security teams often rely on a standard checklist of defenses: Root Detection, Signature Verification, and Anti-Tampering. None of these worked against CVE-2024-0044.

  • Root Detection was Useless: The device was not rooted. The attacker was using a legitimate feature of the Android OS (run-as) that was simply misbehaving. Since the su binary wasn't present and the system partition wasn't modified, standard root detection libraries inside most of the prominent fintech and banking apps saw nothing wrong.

  • Tamper Detection Failed: The attack did not modify the APK file or tampered with any of its memory region. The application signature remained valid, and the checksums matched. The integrity of the installer was compromised, not the app package itself.

This is where the necessity of active self-protection arises instead of just passive checking. We need a security layer that detects the anomalies inside the application in real-time and check the memory of the application in runtime, regardless of what is described by the OS. This brings us to Runtime Application Self-Protection.

How RASP helps

RASP technology operates independently of the Operating System, they do not rely on the corrupted packages.list file or system permissions. Instead, they actively monitor the application's runtime memory for the specific signatures of an attack such as a debugger presence or an active ADB connection in the device. Because the exploit requires these tools to function, RASP detects the intrusion instantly and terminates the app before the attacker can execute a single command, effectively neutralizing the threat for a production app even on a vulnerable device.

Talsec freeRASP

Built around the idea of Security for Everyone, freeRASP provides a lightweight, community-driven layer against common Android attack vectors, including the ones used here: debugging, ADB access, and dynamic instrumentation.

Unlike basic root detection, freeRASP does not just ask the OS whether something is wrong. It actively monitors the app’s runtime memory and integrity.

If an attacker attaches a debugger or uses a run-as-style path to interfere with the app, freeRASP can detect it independently of the OS view and react before private data is exposed.

How to Prevent Debugging Using freeRASP

Integrating freeRASP to block the CVE-2024-0044 attack path is straightforward. The SDK provides callbacks for events such as debugger attachment and active ADB access. That lets you crash the app, block the session, or wipe sensitive state as soon as the attack starts.

For implementation details, use the freeRASP documentation.

Conclusion

CVE-2024-0044 is a reminder that even well-established security boundaries can be undermined by a single missing validation. A newline character in an installer package name was all it took to corrupt Android’s app registry and hand an attacker developer-level access to any app on the device - no root required.

The OS patch from Google closes the specific injection vector. But it does not help users on unpatched devices, and it illustrates a broader point: passive defenses that ask the OS for the ground truth are only as reliable as the OS itself.

Runtime Application Self-Protection represents a different model - one that monitors what is actually happening inside the app at runtime, independent of whatever the system reports. Against an attack that works precisely by corrupting the system’s own records, that independence is the difference between detection and compromise.

Last updated

Was this helpful?