When Angry Birds Attack: Android Edition
Saturday, May 28, 2011
It's been about six months since I reported a vulnerability in the Android mobile platform that allowed the unprompted installation of arbitrary applications with arbitrary permissions on a victim's device. While the vulnerability has long been fixed on Android handsets around the world, I've yet to write up any technical details about it, and it's unlikely you've heard of it unless you were present at our ShmooCon presentation earlier this year. So without further ado, let's dive into "When Angry Birds attack: Android edition."
Deciphering the Android Market
If you've followed my Android security research, you'll know that I'm a big fan of poking around in the Android Market routines. After all, if you're looking to gain code execution on a mobile device, it certainly makes a lot of sense to target the mechanism used to deliver, install, and execute new code.
In my SummerCon presentation last year, I first delved into the GTalkService mechanism that the Android platform uses to install and remove apps from a user's mobile device. Ironically (or maybe appropriately), that presentation also covered a simple proof-of-concept app called RootStrap which subsequently became the first app to be remote-killed/wiped by Google from users' devices using the very GTalkService mechanism I was presenting on.
If you haven't yet read up on the GTalkService, I recommend giving the background material a quick read:
In short, the GTalkService is a persistent connection maintained from your Android phone to Googles servers at all time. It allows Google to push down messages to your phone in order to perform particular actions. For example, when you click to install an app through the Android Market, Google pushes down an INSTALL_ASSET to your phone which causes it to fetch and install that application. When Google wants toremote kill an application from your phone, it pushes down a REMOVE_ASSET message to your phone which causes it to remove the particular application.
So, from a user's perspective, installing a new application seems like a pretty simple process:
- Launch the Android Market app
- Browse/search for an application to install
- Click to install the application
- Approve any necessary permissions
- App is downloaded and installed
The user would usually see a sequence of screens in the Market application followed by a notification in the status bar that the application is being downloaded and installed:
However, under the covers, there is a lot more going on. Steps 1-4 above all happen within the context of the Market application, but the 5th step is handled by a completely different component of the Android platform: the GTalkService.
So what exactly happens between the 4th and 5th step? A bit of magic that looks like the following:
As the above diagram shows, the actual app install process breaks down further behind the scenes:
- The user clicks the final install/approve button the Market app
- The Market app POSTs an install request to the Android Market servers
- The Market servers signal the C2DM service about the install request
- The C2DM servers send an INSTALL_ASSET message through the GTalkService connection
- The GTalkService component on the Android device receives the INSTALL_ASSET and invokes Vending
- The Vending component fetches the APK and installs the application
So while the user gives their approval within the Market app to install a new application, the actual download and install of the target APK occurs within the context of the Vending code.
Chatting with the Market Servers
So whatever, the Android app installation process is a bit round-about, but how is bad from a security perspective?
One of the things that raised a red flag in my mind was this disconnect in responsibility between the Android Market app and the GTalkService/Vending routines. The users give their approval to install the app from within the Market app, but everything else is handled automatically by the Vending code via the INSTALL_ASSET mechanism. Therefore, if we could spoof an INSTALL_ASSET message, we could potentially install applications with arbitrary permissions without the user's approval.
While we might not be able to spoof an INSTALL_ASSET message down the GTalkService pipe without owning Google's infrastructure, maybe we can trick Google's servers into initiating an INSTALL_ASSET on our behalf! After all, if the legitimate Market app is able to POST an install request to the Market servers and trigger an INSTALL_ASSET, why can't an illegitimate app make the same request? :-)
Let's take a look at what exactly the Market app POSTs to the Market servers during an install request:
POST /market/api/ApiRequest HTTP/1.1
Content-Length: 524
Content-Type: application/x-www-form-urlencoded
Host: android.clients.google.com
Connection: Keep-Alive
User-Agent: Android-Market/2 (dream DRC83); gzip
version=2&request=CuACCvYBRFFBQUFLOEFBQUJvZWVEVGo4eGV4OVRJaW9 . . .
Bleh, that request parameter just contains a big ugly blob of a payload. Google sure loves to use protobuf and it turns out that the payload blob is a base64-encoded protobuf structure. The raw protobuf for that request payload looks like:
1 {
1: "DQAAAK8AAABoeeDTj8xex9TIio . . ."
2: 0
3: 1668
4: "f2f15ccd17fb305"
5: "dream:4"
6: "en"
7: "US"
8: "Android"
9: "Android"
10: "310260"
11: "310260"
12 {
12: 0x656c676f6f672d6d
}
13: "-606db3000d480d63"
}
2 {
10 {
1: "353999319718585473"
}
}
The downside of protobuf is that if you don't have the original schema, it makes it quite difficult to infer what the various fields are for. In this dump, there appears to be some device identifiers, locale information, platform/version fields, etc. If you've played around with Google services before, you'll also recognize the first field as an auth token from the ClientLogin service. Fortunately, some of these protobuf fields have been reverse engineered already and are available in the android-market-api project. Unfortunately, this install request protobuf spec had not yet been reverse engineered.
<< cue montage of several hours of exciting reverse engineering of vending.apk >>
After some RE of the market/vending code, I was able to infer what the unknown fields were used for. The annonated protobuf spec looks something like the following:
message UnknownThing {
optional fixed64 mgoogle = 12;
}
message InstallRequest {
optional string appId = 1;
}
message RequestContext {
required string authToken = 1;
required int32 unknown1 = 2;
required int32 version = 3;
required string androidId = 4;
optional string deviceAndSdkVersion = 5;
optional string userLanguage = 6;
optional string userCountry = 7;
optional string operatorAlpha = 8;
optional string simOperatorAlpha = 9;
optional string operatorNumeric = 10;
optional string simOperatorNumeric = 11;
optional UnknownThing unknown12 = 12;
optional string unknown13 = 13;
}
message Request {
optional RequestContext context = 1;
repeated group RequestGroup = 2 {
optional InstallRequest installRequest = 10;
}
}
The values of most of these fields in the install request message are known or can be queried from the Android device itself, but the important fields worth noting are "appId" and "authToken":
-
appId is the unique id used to identify an application when it is posted to the Android Market. So in the above example, the user is requesting to download the application with an appId of "353999319718585473". If you post an app to the Market, you can easily find out its assigned appId by sniffing market traffic and extracting the id from the protobuf requests.
-
authToken is the ClientLogin token (with the service name "android") that enables Google's Market servers to authenticate your install request. If it wasn't for this authToken, we'd be in a lot of trouble since anyone would be able to spoof install requests and trick Google into pushing apps onto your device.
So all is fine and dandy...if the authToken is kept secret, we have nothing to worry about, right? Enter stage left: the Android AccountManager!
AccountManager is a legitimate component of the Android platform that provides some credential management. For example, if you have an app that wants to post a tweet on your behalf, the app doesn't need to access your actual Twitter username and password, but can instead request a token from AccountManager that will allow it to post a tweet. As it turns out, the authToken used to communicate with the Market servers is also stored in the AccountManager and can be requested by any app on your phone with just a few lines of code:
AccountManager accountManager = AccountManager.get(getApplicationContext());
Account acct = getAccount(accountManager);
accountManager.getAuthToken(acct, "android", false, new GetAuthTokenCallback(), null);
Success, we now have access to the authToken! If we fill in the rest of the fields in that protobuf spec, we could potentially construct a valid install request and POST it to the Market servers, triggering Google to push down a INSTALL_ASSET message to the device, and install any application we desire. And, since the permissions approval happens before this request is sent to the Market servers, we could install an application with any and all permissions without any prompt being presented to the user.
When Angry Birds Attack
So how do Angry Birds tie in to this? To demonstrate this vulnerability, I posted a proof-of-concept app to the Android Market that was able to construct these valid install requests to the Market servers. Essentially, my PoC app was able to impersonate the official Market app and make the same requests it would, but not actually ever prompt the user to install the app or approve the apps permissions.
To spice things up, the PoC app was titled "Angry Birds Bonus Levels", disguising itself as an add-on to the popular Angry Birds game. Completely coincidentally, the official Angry Birds developers released a legitimate new set of levels the same exact day I posted my fake app to the Android Market...I'm sure that helped the social engineering angle a bit.
So the Angry Birds application acted as the delivery mechanism for three additional applications that I posted to the Android Market: "Fake Location Tracker", "Fake Toll Fraud", and "Fake Contact Stealer". Upon running the fake Angry Birds app, it would forge install requests for those three additional applications to the Market servers. Each of the three apps had the capabilities to perform their stated malicious action, but didn't actually do anything harmful. For example, the "Fake Toll Fraud" app had permission to make calls and send SMS to premium toll numbers without the user's knowledge.
As you can see in the screenshot here, the Angry Birds Bonus Levels app triggered the download and installation of the three fake malicious apps.
It's worth noting that while my PoC attack used a fake app posted to the market, the attack could launched by compromising an existing application (eg. a browser client-side exploit).
Wrap-up
While this bug was patched months ago, I hope these technical details will provide some insight into how exactly this round-about installation process works on the Android platform and prompt others to take a deeper look at the Vending.apk which I guarantee has another solid bug or two in its depths.
The fix that Google rolled out is pretty simple: upon installing an app through the Market, the phone will keep track of that installation request, and verify that any incoming INSTALL_ASSET messages are associated with an app that was requested by the user. If the GTalkService/Vending receives an INSTALL_ASSET message for an application that it's not expecting, it will simply discard it.
However, there is one exception, which is the whole reason Google designed this round-about install/removal mechanism in the first place: the new Android web market (which has some serious issues of its own). If you initiate an install request from the web market, the INSTALL_ASSET message will contain a special "server-initiated" flag which will make the Vending routines skip their local check to see if the user had requested the app installation.
Until next time...beware of those Angry Birds! :-P