The CommonsBlog


The Stagefright Bug, and Your App

By now, it is fairly likely that you have heard about the Stagefright security vulnerability existing in most current versions of Android.

There is not much that developers can do to help prevent their apps from being the means for malware to exploit this bug. The only absolute way to prevent an app from causing this problem is to not play any multimedia obtained from arbitrary sources through Stagefright, and that restriction is impractical for many apps.

In theory, one could attempt to devise some sort of scanner that looks for the sorts of media content that would signal an attempt to exploit Stagefright. However, creating such a scanner is likely to be rather difficult, and it implies that you are in position to scan all media ahead of time. For apps reliant upon streams, that alone may be a show-stopper.

Until we understand more about the types of media and forms of playback that could trigger this vulnerability, it is unclear if switching to a different playback engine (e.g., ExoPlayer) will help mitigate the risk or not. Hopefully, we will learn more after the presentations on this vulnerability scheduled for Black Hat USA and DEF CON 23 in August.

If your app automatically plays media obtained from outside sources, consider disabling that, so users at least have to specifically request the media playback. In effect, this is what the “prevent MMS messages from automatically loading” guidance is suggesting to users. Fortunately, many SMS clients have the ability for users to toggle off MMS auto-playback. If your app has a similar auto-play capability, if nothing else, provide an equivalent setting where the user can disable the auto-play feature.

Where this is really going to be a problem is with ads.

If your ad network might be sending down something other than plain images or simple HTML, such as audio or video, see if you can disable that type of ad. If that is not possible, contact your ad network and ask them when they will integrate this sort of control, or what else the ad network is doing to ensure that the ad network is not taking on ads that might trigger the Stagefright vulnerability.

In the meantime, we need to wait patiently for somebody to provide us a bit more to go on with respect to the scope of the vulnerability, so that we can perhaps come up with more specific guidance for developers.

Jul 29, 2015


The Limits of ContentProvider Security

ContentProvider has a fairly robust integration with the Android permissions system. You can make a ContentProvider publicly available, yet limit access via android:readPermission and android:writePermission manifest attributes. Or, you can make a ContentProvider private, yet “poke pinholes in the firewall” via android:grantUriPermissions to allow third-party access to your content in limited cases. An example would be an email client making email attachments available to third-party file viewers (e.g., Adobe Reader for PDFs), but only on user demand, rather than making all attachments available at all times.

The dirty little secret with all of this is that once your data is in the hands of another app, that app can do whatever it wants with the data, including making that data available to yet other apps. Permissions are not transitive: if A gives data to B (where B has specific permission to get this data), and B gives that data to C, C has no direct relationship with A and does not necessarily need any permissions that A requires of B. From Android’s standpoint, the fact that B happens to hand the same data over to C that it got from A is immaterial.

Now, your reaction to this description may be that B is some form of malware. However, that is not necessarily the case.

For example, suppose B is an email client. Reasonable attention is paid to the scenario where B receives emails with attachments. At the same time, though, B probably allows users to send emails with attachments. Those attachments are not going to be written in B itself, but rather will represent photos, documents, or other files that exist somewhere on the Android device. B can use ACTION_GET_CONTENT or the Storage Access Framework (ACTION_OPEN_DOCUMENT) to allow the user to pick something to attach. However, not only does B have to have the right to send that attachment to the mail server, but B also needs to be able to show that attachment to the user via B’s “Sent Items” or equivalent roster of past sent emails. If the user attached a photo to an email, B probably should have the feature whereby the sender can view that photo. But, we have two problems:

  1. At the time the email is being crafted and the user picks the photo, the Uri that B gets may be good only for B. B may not be able to pass that Uri along to an image viewer. And while it could be argued that B could implement an image viewer itself, there are an arbitrary number of possible MIME types for attachments, and B cannot implement all of them, realistically.

  2. What happens if the user deletes, moves, or otherwise modifies the photo, such that the original Uri is no longer valid? Now, even B cannot get to the image again, let alone pass that image off to a third-party app.

To address both #1 and #2, B could make a local copy of the photo. However, not only does this represent duplication of data, but now any protections that A had for who could access that photo are lost with respect to B’s copy, and it is now B’s protections that matter.

The security around a ContentProvider is securing an API. It is not securing the data. The same holds true for any application of the Android permission system, such as for securing a service.

The fact that clients of your ContentProvider can make local copies of data and share that with others does not mean that you should abandon the Android permission system, or that you should abandon ContentProvider. However, you do need to think through what data you are exposing through your app’s APIs (including a ContentProvider) and whether the benefits for the user of making that data available are worth the risk of the data being distributed more widely than your permissions might imply.

Jul 13, 2015


Returning to Presenting

Seven months ago, I said that I was going on a presentation sabbatical. I will be starting to deliver presentations again, at least to some degree.

If you run a Meetup, a GDG, or similar grassroots community group for Android development, and it’s in the eastern PA/north-central NJ/NYC area, and you would like me to speak, just ask!

I’ll be starting to submit presentation proposals for larger events in a somewhat larger “home territory”, the Boston/Pittsburgh/DC triangle.

Elsewhere in the US, if I am uniquely suited to help your conference or similar event, let me know. There are a lot of people looking to present on Android app development topics, and I do not want to be stealing presentation opportunities from others. But if there is some subject that you really want covered, that you think that I can cover, and that nobody else is covering at your event, feel free to reach out.

Jul 01, 2015


Hey, Where Did These Permissions Come From?

We are getting a wave of questions like this one on Stack Overflow recently. Developers ask for a few permissions in their manifest, then go to install their app (by means other than the IDE) and wonder where all these other permissions are coming from.

The vast majority of the time, they are coming from libraries.

Android library projects — both in source and AAR form — can publish manifests. Those manifests can have <uses-permission> elements in them. Those permissions will blend with the ones from your manifest(s) and those of other libraries that you are using, and all of them will be requested by your app.

This is one highly-visible impact of manifest merging.

To diagnose what is going on, you can turn to two resources:

  1. The actual manifest in your app

  2. The manifest merger report

Let’s see how this works.


If you create a brand-new Android Studio 1.2.2 project, using the Blank Activity template, you will wind up with a manifest that looks a bit like this:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.commonsware.myapplication" >

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Note: there are no <uses-permission> elements here.

Your app module’s build.gradle file will also be fairly bland:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.0 rc2"

    defaultConfig {
        applicationId "com.commonsware.myapplication"
        minSdkVersion 15
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.0'
}

But let’s make one change: adding compile 'com.google.android.gms:play-services:7.5.0' to the dependencies closure, to pull in all of Play Services.

Then, after building the project, we pull up app/build/intermediates/manifests/full/debug/AndroidManifest.xml. This is the manifest that will actually go into our APK, representing all the contributions from our own app and libraries.

As you can see, we have a few <uses-permission> elements now, even though our own manifest does not have any:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.commonsware.myapplication"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="22" />

    <!-- Include required permissions for Google Mobile Ads to run -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <android:uses-permission
        android:name="android.permission.READ_EXTERNAL_STORAGE"
        android:maxSdkVersion="18" />

    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.commonsware.myapplication.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!-- Include the AdActivity and InAppPurchaseActivity configChanges and themes. -->
        <activity
            android:name="com.google.android.gms.ads.AdActivity"
            android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
            android:theme="@android:style/Theme.Translucent" />
        <activity
            android:name="com.google.android.gms.ads.purchase.InAppPurchaseActivity"
            android:theme="@style/Theme.IAPTheme" />

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.gms.wallet.api.enabled"
            android:value="true" />

        <receiver
            android:name="com.google.android.gms.wallet.EnableWalletOptimizationReceiver"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.google.android.gms.wallet.ENABLE_WALLET_OPTIMIZATION" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

That tells us what our app will ask for. It does not tell us exactly why it wants those permissions, other than that one XML comment hinting that Play Services is to blame.

To get a better sense of where those <uses-permission> elements are coming from, we can look at app/build/outputs/logs/manifest-merger-debug-report.txt. Since this file is 500+ lines long, here are some sample lines that relate to our permission issue:

uses-permission#android.permission.INTERNET
ADDED from com.google.android.gms:play-services-ads:7.5.0:20:5
MERGED from com.google.android.gms:play-services-analytics:7.5.0:21:5
MERGED from com.google.android.gms:play-services-analytics:7.5.0:21:5
MERGED from com.google.android.gms:play-services-appinvite:7.5.0:19:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:21:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:21:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:21:5
MERGED from com.google.android.gms:play-services-wallet:7.5.0:20:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:21:5
  android:name
    ADDED from com.google.android.gms:play-services-ads:7.5.0:20:22
uses-permission#android.permission.ACCESS_NETWORK_STATE
ADDED from com.google.android.gms:play-services-ads:7.5.0:21:5
MERGED from com.google.android.gms:play-services-analytics:7.5.0:22:5
MERGED from com.google.android.gms:play-services-analytics:7.5.0:22:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:20:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:20:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:20:5
MERGED from com.google.android.gms:play-services-nearby:7.5.0:19:5
MERGED from com.google.android.gms:play-services-maps:7.5.0:20:5
  android:name
    ADDED from com.google.android.gms:play-services-ads:7.5.0:21:22

What adding compile 'com.google.android.gms:play-services:7.5.0' really does is pull in a bunch of other Play Services artifacts as transitive dependencies. So, we get play-services-maps and play-services-ads and play-services-analytics and so on. Many of those have manifests, asking for certain permissions. In the case of ACCESS_NETWORK_STATE, we see that play-services-ads, play-services-analytics, play-services-maps, and whatever play-services-nearby is all want that permission. So, it gets added to our app’s manifest.

If you look at the generated manifest and the manifest merger report for your app, you too can see where these extra permission requirements are coming from.


Of course, the obvious follow-on question is “how do I make it stop?” Maybe you don’t want all those <uses-permission> requests, because yours is a smaller brand and you don’t want to scare away prospective users.

The best answer is: use fewer, more targeted dependencies.

For example, maybe you are using Play Services just for Google Cloud Messaging (GCM). Rather than pulling in play-services as a dependency, you can pull in play-services-gcm instead. Now, with that one change, not only is your APK going to be smaller, but all those rogue <uses-permission> elements will go away from your merged manifest, because GCM itself does not need any permissions.

It is possible that you will find yourself in a situation where you want a dependency but you don’t want the permissions that the dependency wants. You can try to block the permission from the merger process, by having a <uses-permission> element in your own manifest (e.g., in the main sourceset) with tools:node="remove":

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" tools:node="remove" />

The above element would tell the build system, “yes, some dependencies want ACCESS_COARSE_LOCATION, but do not include it in my manifest”.

(note: you may need to add xmlns:tools="http://schemas.android.com/tools" to the root <manifest> element if it is not already there)

But at this point, you have one main thing to do: write the biggest damn test suite you can. The author of the library is expecting to be able to access location data, and some code in that library may fail with a SecurityException due to your manifest shenanigans. So long as you never execute that code, life is good… at least until the library gets an update that you want to take on. But the only way you can feel comfortable that you are not executing that permission-dependent code is to test, test, test.

Ideally, library authors will use PackageManager to see whether or not their app has the permission and either gracefully degrade or fail fast. For many permissions, Android M is going to shake up the world, particularly for library authors, and so this is a fine time to go in and adding in that sort of logic.


So, in summary: if permissions are showing up in your app that you are not expecting, you have nobody to blame but yourself.

Well, yourself and the library authors for the libraries that you are using.

Well, OK, really it’s yourself, the library authors, and the Android tools team for offering this slick manifest merger process rather than forcing developers to hand-merge all of this stuff in.

But given the knowledge you gain from the merged manifest and the merger report, you can better tailor your library use and try to avoid permissions that you fear your users will not appreciate.

Jun 25, 2015


Safely Unzipping ZIP Archives

ZIP archives are a convenient packaging format, and many developers use them. However, ZIP archives are very generous as to what they support, such as relative paths (e.g., a ZIP archive containing an entry whose path is ../../../let/us/overwite/something). This particular bit of flexibility can represent a security flaw, if a Martian-in-the-middle (MITM) attack replaces some downloaded ZIP with another one. NowSecure pointed this out last week, demonstrating how a malformed ZIP, a MITM attack, and the multidex backport allow attackers to execute arbitrary code on devices.

I can’t fix multidex. I have been begging people to use SSL properly (thereby making MITM harder) for a while now. So, there is not a lot that I can do to help on those issues.

I can help on unpacking a ZIP archive, though.

CERT published sample safe-unziping code a while back. I have made a variation on this code, to make it more Android friendly, and added it as the unzip() method on the ZipUtils class on my CWAC-Security library.

As with the CERT code, my unzip() fails on three types of malformed ZIP archive:

  1. Does the ZIP archive attempt to use relative paths to write to locations outside of the directory where we are trying to unzip the contents? This is the attack that NowSecure outlined.

  2. Does the ZIP archive expand to more than X bytes? This is the so-called ZIP bomb attack.

  3. Does the ZIP archive contain more than X entries?

In the case of any of these failures, or a more generic exception (e.g., some IOExecption), unzip() will delete all that was previously unzipped, to not leak storage space.

As this code is doing heavy disk I/O and decompression logic, please call unzip() on a background thread.

If you find flaws in the unzip() logic, please file an issue, and I will work on it.

Jun 22, 2015


Older Posts