Android Apps: Thinking About Vulnerability Severities
Calibrate Your Enthusiasm
Vulnerability severities can be tricky, especially when you’re coming into a new platform. Sometimes they’re straight-forward: “I got RCE on your app server!” Obviously bad. Sometimes they’re more complicated: “when a video of Tay Zonday is playing, I can predict the next UUID!” Huh, alright. What do we do with that information? How often are videos of Lord Zonday playing? Do we care about the UUIDs? Don’t know! In these scenarios, the app’s threat model and the vuln context are important. So that’s what we’re talking about today, using Android apps as our focus.
So, lets say you found some vulnerabilities in your Android app. How bad is it? Let’s look at some examples of how their context affects them.
To be able to reason about a vuln’s context, we’ll need a basic threat model. That doesn’t mean we need to bust out some heavy-weight framework and data flow diagrams, but we do need to be able to zoom out and think about how the app fits into the overall system we’re assessing.
This should usually be your first step in an assessment! A basic understanding of the app’s threat model will help you spend your time on more high-value targets, and better reason about more complex issues and vuln chains. In this context, we can think of an issue’s severity as a mixture of the impact (“how bad is this if it happens?”) and exploitability (“how likely is this to happen?”).
Client or Server, A Tale of Two Codebases
Most (non-trivial) Android apps are interfaces for backend web services and large chunks of their functionality will live in an API somewhere. This means the app can be thought of as client-side code, similar to Javascript in a single-page web app. Also like a web app, security controls for the API, like authentication, authorization, and data validation, need to live in the server-side code.
Client-side vulnerabilities (in the Android app itself) will primarily be used to target an individual user’s device, compromising data or taking actions on their behalf. They will typically require some user interaction as well. Server-side vulnerabilities (in the API) can potentially impact all application users with no user interaction required.
Because of the potential for an increased severity, server-side issues are often targeted first and reviewed more thoroughly. This also means client-side issues can go overlooked and may be underestimated.
When Android app vulnerabilities happen on the client-side, the impact and exploitability of an issue can be changed by:
- The audience
- The environment
- The attack vector
The Audience
First, we should consider the core userbase for the app: is it the general public, subscribed customers, or a subset of employees? Is the app distributed to the users via public channels, corporate MDM, or sideloading?
To exploit a client-side issue, we’ll need to target the app’s users. Smaller, controlled audiences and limited distribution can both reduce the exploitability of an issue, by making it harder to deliver payloads or target the exploit effectively. Can we enumerate them or target en-masse with some sort of watering hole attack? If the audience is large, the exploitability may be fairly high. If the app is sideloaded, will everyone even be running the same version of the app?
Each of these user groups and distribution methods may also come with implicit levels of trust, which can increase the impact of a vuln. A smaller, restricted audience, like internal admins, may be harder to target, but can come with privileged access to sensitive data or functionality.
The Environment
The app’s runtime environment changes the attack surface and potential exploitability of an issue as well. Does the app run on a user-controlled device, like a phone? Maybe it runs on a car’s infotainment head-unit, or a kiosk in a company or hospital lobby? In these environments, what sort of data does the app store on the device?
This can drastically change the severity of vulnerabilities:
- A personal phone will likely only have a single user’s data. If it’s used for general web browsing, we may be able to get a link clicked or a malicious app installed to exploit an IPC vuln remotely. We probably won’t have physical or local network access.
- A kiosk tablet may collect the data of multiple users in a day, increasing the impact of data compromise. It may have limited UI and internet access, making vulns harder to exploit remotely. We might need to leverage physical access to exploit IPC or file handling vulns via
adb
.
In their usual use-case, how often are these devices being updated? Older devices are likely running an outdated versions of Android and may lack newer security features that would otherwise prevent a vulnerability.
The Attack Vector
When thinking about the app’s exposure to various vulnerabilities, it’s helpful to trace how data flows through the app. On the device, data can come into the app from various sources:
- Exported app components (IPC interfaces)
- File handling operations (read/write with user-controlled data)
- Network traffic (API request/response)
- Dynamically loading code (DEX or Javascript)
- Device components (bluetooth, camera, QR codes, USB, etc.)
To exploit an issue in one of these input sources, we’ll likely need to deliver a payload. The payload may require user interaction (like clicking a link, taking a photo, or installing an app) or some level of access (proximate, network, or physical). Each of these hurdles can reduce the exploitability of a vulnerability. For example, exploiting local network traffic may be easier on a public smart TV than a stranger’s phone.
Now We Fix It, Right?
Finally, let’s talk a bit about suggesting remediations. Any vulnerability report should have a suggested fix, otherwise it’s not real champagne, just sparkling functionality.
Cross-platform Frameworks
Cross-platform frameworks (like React Native and Expo) are popular choices for building an Android app, as you’ll get an iOS app for just a little bit of extra effort. They often make development easier and quicker in the average case, but can make it difficult to override default configs and functionality.
This can cause problems when trying to remediate vulnerabilities. To fully remediate an issue, an app may need to invest substatial engineering effort to re-implement framework functionality, or migrate off of a framework completely. It may make more sense, from a resource perspective, to simply accept the risk of a lower severity issue.
Additionally, from the severity perspective, a vuln in the framework itself can increase the exploitability, as it’s likely reusable in several apps.
Anti-debugging Bypasses
What if we found a root detection, certificate pinning, or obfuscation bypass and want to report it as a vulnerability? How does this fit into the app’s threat model?
Assuming a user-controlled Android device is a potentially hostile environment, the app’s functionality should be designed accordingly. For an unprivileged (non-root) app, trying to stop a root user from tampering is a no-win scenario. Users can easily instrument devices to bypass certificate pinning, root detection, and obfuscation. At an architectural level, most security controls should be implemented within backend services (which you control) and not on the device (which they control).
From a severity perspective, when your security controls live in the API, what is the impact if:
- Users can see their own network traffic?
- Users can read client-side code?
- Users can debug the app (with
Frida
,apktool
, etc)?
If these activities allow someone to discover vulnerabilities in the app, reducing the visibility isn’t a proper fix. The remediation shouldn’t be more obfuscation or pinning the certificate even harder. Should the app escalate privileges and fist-fight for ultimate root?
While there are use-cases for complicating reverse engineering or client-side debugging, these are generally speedbumps, not security controls. If you’re not sure, your app probably doesn’t fall into those use-cases. Your engineering time is usually better spent on identifying and fixing bugs, and hardening your backend services.