BLOG
BLOG
When teams think about Android app security, the focus is usually on code for encryption, obfuscation, or binary protection.
But in practice, many of the most critical Android app vulnerabilities don’t originate in code at all.
They come from misconfigurations.
Issues in the AndroidManifest, insecure component exposure, and unsafe inter-app communication often create direct entry points for attackers. These are not edge cases. They are common, repeatable, and frequently exploited.
What gets shipped may be tested, but what is exposed is not always controlled.
Most Android app vulnerabilities come from misconfigurations, not code flaws.
Mobile app security depends on controlling exposure and behavior, not just scanning code.
Android applications rely heavily on configuration:
These are essential to the app's functionality and define how it behaves, not just internally but also within the broader app ecosystem.
In enterprise environments, however, these configurations are often treated as:
This creates a fundamental gap.
Security is validated at build time, but exposure is defined by configuration and exploited at runtime.
Learn how mature teams enforce these checks directly in CI/CD pipelines.
Check out: How to Implement Mobile AppSec in a CI/CD Pipeline
Misconfigured Android components are not theoretical risks.
Configuration-level issues enable attackers to interact with the app in unintended ways. In practice, this leads to unauthorized access to internal components, data leakage across app boundaries, and execution of sensitive actions without user intent.
These vulnerabilities are particularly dangerous because they are:
Unlike traditional vulnerabilities, they don’t require sophisticated techniques, just access to exposed surfaces.
Unlike complex vulnerabilities, they rely on what the app exposes, not on how sophisticated the attack is.
Most teams ask:
Did we scan the app for vulnerabilities?
But Android security operates differently.
The more relevant question is:
What parts of this app are accessible to other apps and under what conditions?
Every exported component, permission, or intent handler becomes a potential entry point. Mature teams focus not just on identifying vulnerabilities but also on controlling exposure and continuously validating behavior.
Because:
In Android, security is defined by exposure, not just implementation.
Android components, Activities, Services, and Broadcast Receivers, can be exposed to other apps using the android:exported attribute in the AndroidManifest.xml.
If a component is marked as exported="true" without any protection, any app can launch it, send it data, or trigger its functionality. This can lead to unauthorized access to sensitive parts of the application.
For apps targeting Android 12 (API 31) and higher, an explicit value for android:exported is required for any component with an <intent-filter>.
In real-world scenarios, this allows attackers to:
Leak data
An attacker could launch a hidden activity that displays sensitive user data.
Initiate unauthorized actions
A malicious app could start a service that performs a sensitive operation, like deleting data or making a payment, without user consent.
Denial of Service (DoS)
An attacker could repeatedly launch a component, causing the app to become unstable or crash.
Bypassing UI flow
Attackers can launch activities deep within the application, bypassing login screens or other security checks.
This is not a theoretical risk. It is a direct consequence of leaving internal functionality externally accessible.
Review the AndroidManifest.xml for components with android:exported="true" that lack a permission check.
<activity
android:name=".SensitiveDataActivity"
android:exported="true" />
<service
android:name=".BackgroundDataSyncService"
android:exported="true" />
<activity
android:name=".SensitiveDataActivity"
android:exported="false" />
<service
android:name=".BackgroundDataSyncService"
android:exported="true"
android:permission="com.example.app.permission.SYNC_DATA" />
An attacker can use the adb shell to directly launch an unprotected exported component.
# Launch an exported activity
adb shell am start -n com.victim.app/.SensitiveDataActivity
# Start an exported service
adb shell am startservice -n com.victim.app/.BackgroundDataSyncService
Even when developers attempt to secure components using custom permissions, the protection level is critical.
A permission with android:protectionLevel="normal" or "dangerous" can be requested and granted to any other application.
For components that should be accessible only to apps signed with the same developer key, the protection level must be set to "signature". If it's not, a malicious app can simply request the permission and gain access to the component, defeating the intended security control.
Privilege escalation
A malicious app can gain the same privileges as the victim app to access sensitive components or data.
Trust model violation
This flaw breaks the trust model between apps from the same developer, allowing a counterfeit or malicious app to interact with a legitimate one as if it were trusted.
Data theft and manipulation
The attacker's app can communicate directly with the protected component to exfiltrate data or send malicious commands.
Review the AndroidManifest.xml for custom permissions and the components that use them. Ensure any permission guarding sensitive, internal components uses protectionLevel="signature".
<permission
android:name="com.victim.app.permission.ACCESS_DATA"
android:protectionLevel="dangerous" />
<activity
android:name=".InternalToolActivity"
android:exported="true"
android:permission="com.victim.app.permission.ACCESS_DATA" />
The attacker's app simply requests the "dangerous" permission in its own manifest and can then launch the component.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.attacker.app">
<uses-permission android:name="com.victim.app.permission.ACCESS_DATA" />
...
</manifest>
Developers can create custom permissions to protect access to their app's components. However, the security of these permissions depends entirely on their protectionLevel.
Using normal or dangerous for permissions that guard sensitive data or functionality is a critical mistake, as it allows any third-party app to potentially gain access.
This is a common cause of Android app data leakage vulnerabilities.
Malicious apps can gain access to functionality that should have been internal, leading to data leaks or unintended app behavior.
Developers may believe their components are protected, while in reality, the protection can be easily bypassed by any app that requests the permission.
Audit all <permission> tags in the AndroidManifest.xml. Any permission that protects access between your own apps or to highly sensitive functionality should have android:protectionLevel="signature".
<permission
android:name="com.victim.app.permission.READ_USER_PROFILE"
android:label="Read User Profile"
android:protectionLevel="normal" />
A malicious app only needs to declare <uses-permission android:name="com.victim.app.permission.READ_USER_PROFILE" /> in its manifest. The system will grant it automatically without user interaction, giving it access to the protected component.
Content Providers are a primary mechanism for sharing data between applications and a major source of Android data exposure risks.
Their security is managed through android:readPermission and android:writePermission attributes, or more granularly with <path-permission>. If a Content Provider is exported but these permissions are missing, it may become world-readable or world-writable.
Another common mistake is defining a readPermission but forgetting a writePermission, allowing any app to insert, update, or delete data. SQL injection is also a risk if the provider's query methods build SQL statements directly from user-controlled input.
Mass data leakage
An attacker can dump the entire contents of the application's database or access its private files.
Data tampering
Malicious apps can corrupt, delete, or modify the application's data, leading to app malfunction, user data loss, or financial fraud.
Application crashing
Injecting malformed data can cause the application to crash when it attempts to process it.
Review all <provider> tags in the manifest. Ensure they are not exported or are protected with appropriate signature-level permissions. Check the provider's query, insert, update, and delete methods for SQL injection vulnerabilities.
<provider
android:name=".NotesProvider"
android:authorities="com.victim.app.notes"
android:exported="true"
android:readPermission="com.victim.app.READ_NOTES" />
An attacker can use adb to interact with the misconfigured provider.
# Query a world-readable provider
adb shell content query --uri content://com.victim.app.notes/notes
# Insert data into a world-writable provider
adb shell content insert --uri content://com.victim.app.notes/notes --bind title:s:"Malicious Note" --bind content:s:"Injected data
Component hijacking, often called Task Hijacking, exploits the way Android manages application tasks. The android:taskAffinity attribute in the AndroidManifest.xml allows activities from different apps to exist in the same task stack.
If misused, this can enable attackers to hijack application flows.
For example, if a malicious app sets its taskAffinity to the same value as a victim app, it can intercept the user's attempt to launch the victim app. When the user clicks the victim app's icon, Android may instead bring the malicious app's activity to the foreground, presenting a convincing phishing UI.
This is the basis for known vulnerabilities such as StrandHogg.
The impact here goes beyond technical compromise. It directly affects user trust and brand integrity.
Credential theft
This is a highly effective phishing attack. Users, believing they are on a legitimate login screen, will enter their credentials directly into an attacker-controlled UI.
UI redressing
The malicious activity can mimic any part of the victim app to trick the user into performing sensitive actions or granting permissions.
Complete loss of user trust
An attack that subverts the app's own UI is devastating to user confidence and brand reputation.
Review the taskAffinity for all activities in your manifest.
For most apps, this attribute should not be set, allowing the system to use the default affinity based on the package name.
For sensitive activities, explicitly set android:taskAffinity="" to prevent them from being moved to another task.
<activity
android:name=".PhishingActivity"
android:taskAffinity="com.victim.app"
android:launchMode="singleInstance"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
The exploit is the bypass. By installing an app with a carefully crafted manifest, an attacker can hijack another application's task.
Intent redirection occurs when an app receives an Intent and uses it to launch another component without proper validation.
In such cases, an attacker can craft an Intent that targets private or non-exported components.
A common pattern is an exported activity that accepts a nested Intent object within its extras. The activity then calls startActivity() on this nested Intent. If the application does not validate the contents of the nested Intent, a malicious app can supply a crafted Intent that points to a private, un-exported component within the victim app. This effectively turns the exported activity into a proxy that gives the attacker access to internal components.
Internal component access
Attackers can launch private activities, start internal services, or send broadcasts to private receivers, bypassing access controls.
Data exfiltration
The redirected Intent could be used to launch a private component that returns sensitive data to the attacker's app.
Privilege escalation
The malicious app can perform actions using the victim app's permissions.
Review code where getParcelableExtra() is used to extract an Intent. Ensure the component and action of the nested Intent are validated against an allowlist before use.
// An exported activity receives an Intent to forward
val redirectIntent = intent.getParcelableExtra<Intent>("redirect_intent")
if (redirectIntent != null) {
// The app launches the received Intent without validation!
startActivity(redirectIntent)
}
The attacker crafts an Intent that targets a private activity and passes it as an extra.
# Create an intent that points to a private activity
adb shell am start -n com.victim.app/.RedirectProxyActivity \
-e redirect_intent "new Intent().setComponent(new ComponentName('com.victim.app', 'com.victim.app.PrivateSettingsActivity'))"
Broadcast Receivers registered at runtime using registerReceiver() are often implicitly exposed unless explicitly protected.
While many broadcast receivers are declared in the manifest, they can also be registered dynamically at runtime using Context.registerReceiver().
A dynamically registered receiver is implicitly exported unless a permission is specified during registration. If a receiver that performs a sensitive action is registered without permission, any app on the device can invoke it by sending a broadcast Intent.
Triggering sensitive actions
An attacker could send a broadcast that causes the app to delete files, upload data, or enter a
bad state.
Denial of Service (DoS)A malicious app could flood the dynamic receiver with broadcasts, consuming resources and potentially crashing the victim application.
Data injection
If the receiver processes data from the broadcast Intent, an attacker can inject malicious or malformed data.
Search the codebase for all calls to registerReceiver(). Ensure that any receiver handling sensitive actions is registered with a signature-level permission.
// Receiver is registered without any permission
val receiver = MySensitiveReceiver()
val filter = IntentFilter("com.victim.app.DO_SENSITIVE_ACTION")
registerReceiver(receiver, filter)
val receiver = MySensitiveReceiver()
val filter = IntentFilter("com.victim.app.DO_SENSITIVE_ACTION")
// Register with a signature-level permission
registerReceiver(receiver, filter, "com.victim.app.permission.SENSITIVE", null)
An attacker can use adb or a malicious app to send a broadcast to the unprotected receiver.
adb shell am broadcast -a com.victim.app.DO_SENSITIVE_ACTION
Fragment injection vulnerability occurs when an exported activity dynamically loads Fragments based on a class name provided as an Intent extra.
If the app does not validate the provided class name, an attacker can force the activity to load any exported Fragment from the app's classpath, or even from libraries included by the app. This could allow an attacker to display a misleading UI or trigger unintended logic within the loaded fragment.
UI manipulation
An attacker could replace an expected fragment with one that phishes for user credentials or displays false information.
Information leakage
If a fragment intended for internal use is loaded, it might display sensitive device or user information.
Logic bypass
Loading an unintended fragment could bypass security steps or business logic in the app's normal flow.
Review exported activities that load fragments.
If the fragment class name is derived from an Intent extra, ensure it is validated against a strict allowlist of safe fragments.
// This activity is exported
class DynamicFragmentActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val fragmentClassName = intent.getStringExtra("fragment_to_load")
// The class name is used without validation!
val fragment = Class.forName(fragmentClassName).newInstance() as Fragment
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit()
}
}
An attacker can use adb to launch the activity and specify a fragment they want to inject.
adb shell am start -n com.victim.app/.DynamicFragmentActivity \
-e fragment_to_load "com.attacker.MaliciousFragment"
The android:allowBackup attribute in the manifest controls whether the application's data can be backed up using Android's backup mechanisms, including adb backup. If this flag is set to true (which is the default for apps targeting below Android 6.0), an attacker with physical access to an unlocked device can use ADB to extract the application's private data from its files and shared_prefs directories.
Complete data exfiltration
An attacker with temporary physical access can steal sensitive data stored on the device, such as authentication tokens, cached user data, API keys, and other secrets.
Offline analysis
Once the data is extracted, the attacker can analyze it offline to plan further attacks or steal user credentials.
Inspect the <application> tag in AndroidManifest.xml. It should explicitly be set to android:allowBackup="false".
<application
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:allowBackup="true">
...
</application>
<application
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:allowBackup="false">
...
</application>
An attacker with USB debugging enabled on the device can run the following command.
adb backup -f backup.ab com.victim.app
The resulting backup.ab file can then be unpacked to reveal the application's private data.
This category covers several common network-related flaws. A primary one is allowing cleartext (unencrypted HTTP) traffic by setting android:usesCleartextTraffic="true" in the manifest or in a Network Security Configuration file.
Allowing cleartext traffic exposes applications to interception attacks, like Man-in-the-Middle (MitM) attacks on insecure networks
Data interception
An attacker on the same network (e.g., public Wi-Fi) can intercept and read sensitive data sent between the app and its server, including credentials and personal information.
Session hijacking
Intercepted session cookies can be used to hijack a user's session.
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
An attacker can use a proxy tool like Burp Suite or mitmproxy to intercept and modify the application's unencrypted traffic.
This vulnerability occurs when an application directly connects to external backend services, such as databases (MySQL, Firebase) or caching servers (Redis), from the client-side code. It is a severe architectural flaw because the connection details, including IP addresses, ports, and often credentials, must be stored within the APK.
An attacker can easily decompile the app, extract these hardcoded secrets, and gain direct access to the backend infrastructure.
This type of flaw extends beyond the app as it can lead to full system compromise.
Backend system compromise
An attacker can connect directly to the database or service, potentially stealing, modifying, or deleting all application data.
Infrastructure takeover
Gaining access to one service can provide a foothold for an attacker to move laterally and compromise other parts of the backend infrastructure.
Reputational and financial catastrophe
A direct breach of backend systems is one of the most damaging security incidents possible, leading to massive data loss, regulatory fines, and a complete collapse of user trust.
Decompile the application and search the source code (Java/Kotlin and native libraries) for hardcoded IP addresses, hostnames, and credentials. Look for connection strings or APIs related to direct database or service connections.
// DO NOT DO THIS IN A CLIENT APP
val redisHost = "192.168.1.100"
val redisPort = 6379
val redisPassword = "insecure_password123"
// Code to connect directly to Redis...
An attacker decompiles the app, finds the credentials, and connects to the service.
# After finding credentials in the decompiled code
redis-cli -h 192.168.1.100 -p 6379 -a "insecure_password123"
192.168.1.100:6379> KEYS *
1) "user:123:session"
2) "user:456:profile"
These vulnerabilities may appear distinct, but they share a common root cause:
Lack of continuous visibility into how the app is exposed and behaves beyond development.
Most issues:
Traditional approaches focus on scanning code or binaries, but exposure is defined by configuration and exploited at runtime.
Platforms like Appknox help address this by enabling continuous validation of application behavior and configuration across build and release stages, ensuring these issues are identified before they become production risks.
Android applications rarely fail because of a single critical bug. They fail because exposure is not fully understood or controlled.
Configuration defines what is accessible. Runtime behavior determines how it is exploited.
What you expose is what attackers use.
The shift is not toward more scanning, but toward continuous visibility, validation, and control across the lifecycle.
You’ve seen where mobile app security breaks in practice. The next step is choosing a platform that actually closes these gaps.
Use the mobile AppSec evaluation framework to assess what works in real-world environments.
Common vulnerabilities include improperly exported components, weak permission models, insecure Content Providers, intent redirection issues, and task hijacking. These are typically caused by configuration flaws rather than code-level bugs.
Exported components allow external apps to interact with internal functionality. Without proper restrictions, they can be used to access sensitive data, trigger actions, or bypass intended application flows.
Intent redirection occurs when an app launches another component using an unvalidated Intent received from an external source. Attackers can exploit this to access internal components or perform unauthorized actions.
Permissions with weak protection levels can be requested by any app, allowing unauthorized access to sensitive components. This breaks the intended security boundary between applications.
Component hijacking allows a malicious app to intercept or mimic parts of another app’s UI or workflow, often to steal credentials or mislead users into performing sensitive actions.
If not properly secured, Content Providers can allow attackers to read, modify, or delete application data. They may also be vulnerable to injection attacks if inputs are not validated.
Teams should restrict component exposure, enforce strong permission models, validate all external inputs, avoid hardcoded credentials, and disable insecure configurations. Most importantly, they should continuously validate these controls across the app lifecycle.
Hackers never rest. Neither should your security!
Stay ahead of emerging threats, vulnerabilities, and best practices in mobile app security—delivered straight to your inbox.
Exclusive insights. Zero fluff. Absolute security.
Join the Appknox Security Insider Newsletter!