Android Nougat’s certificate pinning security mechanism

If you are a pentester like me, chances are you are doing mobile application reviews on Android. One of the most important things to check is the server API. On the other hand we might want to see what possibilities a server has to influence the Android app with its responses. For both the easiest and most straight forward method is to do a Man-In-The-Middle attack in the lab and look at the network traffic. How do we do this if the mobile app uses TLS? Easy, just install a user CA certificate.

Before Android 7 that was straight forward. After you installed the CA certificate, there was a little annoying screen showing a warning in the notifications every time you start up your phone, but it worked fine for everyone. However, starting with Android 7 installing a CA certificate is not affecting mobile apps, I tested that and the official announcement about this user-added certificate security is here. User installed CA certificates won’t be trusted by mobile apps and Android claims there is some security gain from this. So let’s look at this new “security” feature of Google’s Android.

First of all who is affected by this security feature? I think only the defender side has to jump through this hoop. Every real-world attack vector I can think of is not very realistic. First of all, a user would need to fully cooperate to let an attacker exploit this. As Android is not opening the security settings automatically when you download a certificate (like iOS), an attacker would have to convince the user to go to the settings dialogue, go to the security settings, scroll down, tap on “install certificate” and choose the correct file from the file system. Let’s say an attacker will setup a Wi-Fi access point and forces the user to do this or otherwise the user won’t get Internet access. This is the only scenario I can think of where a user might at all consider installing such a certificate. You might say that can happen with non-technical users, but then why don’t we just add a big red warning that websites trying to convince you to install a CA certificate are evil? That would suffice in my opinion. If a user would be so ignorant and install an unknown CA despite the warnings, we are in much bigger trouble. That user will probably also type all his usernames and passwords into any website forms that look remotely like a known login form, has an invalid TLS certificate or doesn’t use TLS at all. So for example the attacker could do easy phishing. For users of personal Android phones this is probably the biggest issue.

But let’s also consider corporate Android phones. I understand that administrators don’t want their users to decide on such a security critical topic. But why doesn’t Android just implement an Administrator API rule that would disabling installation of user CA certificates and delete all already installed ones on managed phones? There is already an Administration API that does various such things.

Secondly, why does Android think that a user installed certificate is less trusted than the hundreds of preinstalled, nation-state-attacker-owned CAs? Your Android already comes with various preinstalled CAs, which are not very thrustworthy in my opinion.

It seems Android is raising the bar for defenders, not for attackers. I don’t believe Android is defending against any real attack vector. It boarders to pretending to do security (snakeoil).

On the other hand I know how to disassemble an app and reassemble it to circumvent this new security feature. There are also Android apps that will allow you to install CA certificates in the root CA store on rooted phones, which is by far the best solution on rooted phones. Use Magisk and you have your perfect phone for security analysis.

I thought I’ve seen many strange Android security decisions, this is exceptional. Or is it just me?

iOS TLS session resumption race condition (CVE-2016-10511)

Roughly three months ago when iOS 9 was still the newest version available for the iPhone, we encountered a bug in the Twitter iOS app. When doing a transparent proxy setup for one of our iOS app security tests, a Twitter HTTPS request turned up in the Burp proxy log. This should never happen, as the proxy’s HTTPS certificate is not trusted on iOS and therefore connections should be rejected. Being shocked, we checked that certainly we did not install the CA certificate of the proxy on the iPhone and verified with a second non-jailbroken iPhone. The bug was repoducible on iOS 9.3.3 and 9.3.5.

After opening a Hackerone bug report with Twitter I took some time to further investigate the issue. Changing the seemingly unrelated location of the DHCP server in our test setup from the interception device to the WiFi access point made the bug non-reproducible. Moving the DHCP server back to the interception device the issue was reproducible again. This could only mean this was a bug that needed exact timing of certain network related packets. After a lot of back and forth, I was certain that this has to be a race condition/thread safety problem.

Dissecting the network packets with Wireshark, I was able to spot the bug. It seems that if the server certificate in the server hello packet is invalid, the TLS session is not removed fast enough/in a thread safe manner from the TLS connection pool. If the race condition is triggered, this TLS session will be reused for another TLS connection (TLS session resumption). During the TLS session resumption the server hello packet will not include a server certificate. The TLS session is already trusted and the client has no second opportunity to check the server certificate. If an attacker is able to conduct such an attack, the authentication mechanism of TLS is broken, allowing extraction of sensitive OAuth tokens, redirecting the Twitter app via HTTP redirect messages and other traffic manipulations.

I was not able to reproduce the issue on iOS 10. Twitter additionally fixed the issue on their side in Twitter iOS version 6.44, but noted that this was probably related to an Apple bug. We did not further investigate the issue, but the assumption seems plausible.

The issue was rated high severity by Twitter. The entire details are published on Hackerone.

Update: CVE-2016-10511 was assigned to this security issue.

Android app configuration manipulation

So you got an Android application and you would like to temper with its configuration files? Nothing easier than that as long as you have a rooted Android phone, a sqlite editor and a text editor.

I only wanted to temper with the databases of the app. I used this script (pull-databases.sh) to get the databases:

APP=com.example.theNameInYourAndroidManifest
TMP=/data/local/tmp
APP_UID=`adb shell dumpsys package $APP|grep userId=|cut -d " " -f 5|cut -d "=" -f 2`
#after first run, maybe hardcode, so you can also push files when Android is still starting up and before the app started:
#APP_UID=10000
echo "[+] Removing local folder"
rm -r ./$APP-databases
echo "[+] The applications UID and GID is:"
echo $APP_UID
echo "[+] Copying database to tmp dir"
adb shell "su -c cp -r /data/data/$APP/databases $TMP/$APP-databases"
echo "[+] chmoding tmp dir to 777"
adb shell "su -c chmod -R 777 $TMP/$APP-databases"
echo "[+] Pulling database"
adb pull $TMP/$APP-databases $APP-databases
echo "[+] Removing database in tmp"
adb shell "su -c rm -r $TMP/$APP-databases"

You might need to change the cut commands, as they might not work in every case. Then, to upload the databases back to the phone, use this script (push-databases.sh):

APP=com.example.theNameInYourAndroidManifest
TMP=/data/local/tmp
APP_UID=`adb shell dumpsys package $APP|grep userId=|cut -d " " -f 5|cut -d "=" -f 2`
#after first run, maybe hardcode, so you can also push files when Android is still starting up and before the app started:
#APP_UID=10000
echo "[+] The applications UID and GID is:"
echo $APP_UID
echo "[+] Pushing to tmp dir"
adb push $APP-databases $TMP/$APP-databases
echo "[+] Copying from tmp to app dir"
adb shell "su -c cp -pr $TMP/$APP-databases/* /data/data/$APP/databases/"
#cp -p  doesn't seem to preserver mode, but sets it to 666
echo "[+] chmoding app dir"
#attention: 777, easy way out, but databases might have different flags...
adb shell "su -c chmod -R 777 /data/data/$APP/databases"
adb shell "su -c chmod 771 /data/data/$APP/databases"
echo "[+] removing tmp database"
adb shell "su -c rm -r $TMP/$APP-databases"
#cp -p doesn't seem to preserve owner, but sets it to shell
echo "[+] chowning app dir"
adb shell "su -c chown $APP_UID.$APP_UID /data/data/$APP/databases"
adb shell "su -c chown $APP_UID.$APP_UID /data/data/$APP/databases/*"

If you want to get the entire configuration of the app, you can use this script (pull-all.sh):

APP=com.example.theNameInYourAndroidManifest
TMP=/data/local/tmp
APP_UID=`adb shell dumpsys package $APP|grep userId=|cut -d " " -f 5|cut -d "=" -f 2`
#after first run, maybe hardcode, so you can also push files when Android is still starting up and before the app started:
#APP_UID=10000
echo "[+] Removing local folder"
rm -r ./$APP
echo "[+] The applications UID and GID is:"
echo $APP_UID
echo "[+] Copying app dir to tmp dir"
adb shell "su -c cp -r /data/data/$APP $TMP/$APP"
echo "[+] chmoding tmp dir to 777"
adb shell "su -c chmod -R 777 $TMP/$APP"
echo "[+] Pulling app dir from tmp"
adb pull $TMP/$APP $APP
echo "[+] Removing app dir in tmp"
adb shell "su -c rm -r $TMP/$APP"

As I didn’t need to push the entire app configuration, I didn’t write a push-all.sh script. That could get messy with the permissions and I didn’t want to do a chmod 777. But of course you can do that if you like.

These simple scripts got me some really nice results during pentests. Activate apps that I only had in the free version. Reset the app’s PIN lock count. Disable ads showing in the application.

Android app disassembling, modification and reassembling

This is actually nothing very new, but what probably a lot of people do for a long time already. You can use this technique to do security reviews, to crack license mechanisms of apps, check how easy it is to modify your own app or do malware research. I’m not saying you should or shouldn’t do any of these. As usually tested on Mac OSX only but should work on Linux or other Unix, too.

You need the following folder structure (or simply download the Android-app-disassembling-reassembling.zip):

  • Folder called “apks-to-process”
  • Folder called “external-tools”
  • File “disassemble.sh” (see below)
  • File “reassemble.sh” (see below)
  • In the “external-tools” put the apktool.jar
  • In the “apks-to-process” folder put your Android app apk file

After you run the disassemble.sh file you find the smali code for your app in the “outputs/smali-output” directory. Now you can change the app as you like. Here are three suggestions:

  • I recommend to add the android:debuggable=”true” attribute in the AndroidManifest.xml to your application tag. Afterwards you will be able to see the log messages of the application in logcat (“adb logcat” command when your phone is connected via USB).
  • Replace one of the png files in the ressources folder
  • If your application is making a new instance of a SecreKeySpec for encryption (something like “new-instance v1, Ljavax/crypto/spec/SecretKeySpec” in smali, grep for it), try to dump the contents of the secret key. That’s pretty easy with IGLogger. Download the IGLogger files and put the iglogger.smali file in the folder “outputs/smali-output//smali/”. Then open the file where you found the SecreKeySpec intialisation. Add a new instruction after the invoke-direct line which will initialize the SecretKeySpec (e.g. “invoke-direct {v4, v5, v6}, Ljavax/crypto/spec/SecretKeySpec;->([BLjava/lang/String;)V”). This is the place where the secret key is passed to the SecretKeySpec constructor. As we know that the first argument is the secret key, we have to log the Dalvik VM’s register v4. Add “invoke-static {v4}, Liglogger;->d([B)I” after the initialisation statement.

After you have done all your modifications, run reassemble.sh. There will be an apk file you can install on your device (see the last message that reassemble.sh will print). If you have added IGLogger, you will see a line in logcat that prints the secret key (for example run “adb logcat|grep -i IGLogger”).

Happy hacking
floyd

Here’s the disassemble.sh that will disassemble your apk file to smali code:

#!/bin/bash
ORGWD=`pwd`

#Configurable Parameters
APKLOCATION=$ORGWD/apks-to-process #where the APK files are stored that should be processed

#Disassembling
SMALI_TARGET=$ORGWD/outputs/smali-output #Where to save the results
APKTOOLSTART="java -jar $ORGWD/external-tools/apktool.jar" #The apktool

########
#Normally you should not need to change anything below here
########

#Look for the files to dissassemble
cd $APKLOCATION
FILES=`ls *.apk`

if [ -e $SMALI_TARGET ]
then
    echo "[ERROR] Please delete/rename $SMALI_TARGET folder first!"
    exit
else
    mkdir $SMALI_TARGET
fi

for f in $FILES
do
  echo "[INFO] Disassembling $f"  
  $APKTOOLSTART d $f $SMALI_TARGET/$f
done

cd $ORGWD

Here’s the reassemble.sh code that will reassemble your app to a signed and ready to be installed Android app apk file:

#!/bin/bash
ORGWD=`pwd`

#Configurable Parameters
APKLOCATION="$ORGWD/outputs/faked-apks" #where the APK files will be stored that should be produced

#Reassembling
SMALI_TARGET="$ORGWD/outputs/smali-output" #Where to get the apps to reassemble
APKTOOLSTART="java -jar $ORGWD/external-tools/apktool.jar" #The apktool

########
#Normally you should not need to change anything below here
########

#Look for the files to dissassemble
cd "$SMALI_TARGET"
FILES=`ls`

if [ -e "$APKLOCATION" ]
then
    echo "[ERROR] Please delete/rename $APKLOCATION folder first!"
    exit
else
    mkdir "$APKLOCATION"
fi

for f in $FILES
do
  echo "[INFO] Reassembling $f"  
  $APKTOOLSTART b "$SMALI_TARGET/$f" "$APKLOCATION/$f"
  if [ ! -f "$APKLOCATION/someone.keystore" ]
  then
    keytool -genkey -noprompt -dname "CN=example.ch, OU=floydsReassembling, O=example, L=example, S=example, C=CH" -storepass password -keypass password -alias someone -validity 100000 -keystore "$APKLOCATION/someone.keystore" -keyalg RSA -keysize 2048
  fi
  jarsigner -verbose -storepass password -keypass password -sigalg SHA1withRSA -digestalg SHA1 -keystore "$APKLOCATION/someone.keystore" "$APKLOCATION/$f" someone
  mv "$APKLOCATION/$f" "$APKLOCATION/$f.unaligned"
  zipalign -v 4 "$APKLOCATION/$f.unaligned" "$APKLOCATION/$f"
done

echo "TODO:"
echo "adb install \"$APKLOCATION/$f\""

cd "$ORGWD"

Samsung Galaxy S3 / Android device encryption is broken and the factory reset feature joined the party

I’m a pretty happy owner of a Samsung Galaxy S3, Android Version 4.1.2. I don’t know if these things apply to the Galaxy S3 only, or if it’s the Android system that’s broken, but I’m pretty sure a lot of these things apply to all Android 4.1.2 phones.

As you might have noticed I’m a little paranoid, so of course I want all security features enabled. Especially an encrypted internal memory and encrypted SD card is important. So while I wanted to set up the encryption for the internal memory, the device asked me to set up a lock screen password first, that contains at least one number.

First mistake. Why a number? There are enough reasons not to force such complexity rules. Maybe I would put a longer password in there with only lower and upper case. Now I have to press and hold one of the number buttons, which takes about 2 seconds. An alternative is to press 3 buttons to get a number. When you’re a fast typer you can type in 3 lowercase letters in that time, which is a lot stronger than having only a single number in it (if the attacker can still not rule out a number). Ok, whatever, let’s go on. But wait.

Second mistake. Why do I have to set up a lock screen password for device encryption? Answer: The disc encryption password is the lock screen password. Device encryption and lock screen are two different things. One scenario is a cold boot attack attacking the disc encryption password. The other one is when an attacker finds a device that is still running (let’s call it running device attack). If we want a shorter password for the lock screen for convenience reasons, then of course the security will decrease and running device attacks will get easier. But security does not decrease for cold boot attack scenarios if you’re able to use two different passwords. Of course you can argue that the running device attack is much more likely for mobile phones. But with some crypto you can easily make two different attack scenarios with different risks, depending on each password. So why do they have to be the same? The only conclusion I drawed at this point was that the disc encryption key will be properly wiped when the lock screen is enabled. So to unlock it you would need the disc encryption password (I was so wrong).

After setting the password I encrypted the SD card first, then the device. From that point on, I had to type in that long password every time I unlocked the screen. Annoying, but at least it’s secure as long as nobody is looking over my shoulder when I’m unlocking my phone (again, he would get the password for the cold boot AND the running device attack).

I took a month to think about the next security measure: Setting up SamsungDive. It’s the Samsung service where you can locate/wipe/ring your phone in case it got stolen or you lost it. I wasn’t sure if I wanted to do that. What if there is a security breach at samsungdive.com and somebody wipes my phone? And do I really want to give all that private information to Samsung? In the end I decided I wanted the feature anyway, so I set up a SamsungDive account. When I logged in, I realised there is another feature: Unlocking the screen remotely. Of course this would work for non-encrypted devices, but it won’t work for encrypted ones, because the encryption key was wiped when the screen was locked, right? Wrong.

Third mistake. The encryption key is not wiped when you lock your screen. It’s merely just to annoy the user that the lock screen password has to be the encryption password. After encrypting the device you can not change the lock screen type any more, everything is grayed out, except the password:

select-screen-lock

But let’s unlock the screen from the SamsungDive web site:

SamsungDive-Screen-Unlock

Now there is no screen lock anymore. Disabled. Gone. You don’t have to type in your password anymore. Look, swipe means no lock screen:

screen-lock-reset-to-wipe

And when we want to change the type of lock screen mode again (clicking on Screen lock – Swipe), we get the same picture again:

select-screen-lock

The lock screen does not wipe the encryption key after all.

After reverting to the password lock, I wanted to show a picture to one of my friends… but the SD card pictures were not recognised anymore.

Fourth mistake: The SD card encryption is disabled, the data not accessible. I restarted my phone, but the usual message that the SD card is encrypted didn’t pop up. And there is no option in the Android preferences to tell the system that the SD card is encrypted. The only option is to encrypt the SD card again. And that would double encrypt the already encrypted files. Fuuuu! So far I didn’t have enough time to check how I could restore the data, I might write a blog post about it, but for a start, check the Android source for the encryption. So far I copied down the encrypted files, but probably they’re unrecoverable. I guess the master key on the device was overwritten when I enabled encryption for the SD card again.

And I hate so say it, because there are too many Apple fan boys out there (I don’t need individuality, iPhone, iPhone, iPhone!), but: Yes, regarding device encryption, the newest iPhones are at least a little bit better. We are not talking about a device PIN of 4 digits, everybody knows that’s crap, but at least the disc is partially locked with this PIN when you lock your screen. It will protect your mail index, the keychain and files specially tagged by developers when your screen is locked. It’s a thought through concept of different encryption keys, although in the end that might not help that much (PIN bruteforcing, exploits, forensics…). Funny part is, that your mails won’t be stored encrypted on the iCloud (but that’s another story too). Well, let’s just say Apple is at least trying.

Unrelated fifth mistake: Factory Data Reset is totally broken for my phone. It was even better on my old Android 2.2, where it was at least deleting some stuff on the internal memory. That’s not the case for the Galaxy S3. I factory reset the device three times, it didn’t reset anything, just deleted the apps from the screens. All pictures, files, music, etc. is still on the internal and external memory. I wonder what it’s actually doing at all except from removing the icons from the different screens. Maybe this problem is because the phone was rooted once, but I properly unrooted it, so I doubt it.

Additionally, I found the login bypass for the Samsung Galaxy S3 here. Having some troubles, Galaxy S3?

Update: Also check this link to the details of the Android crypto implementation supplied by steentje in the comments.

Update2: Updated the iPhone part, as Jacob pointed out in the comments, Apple isn’t doing that well either.

Source Code Review Grep Script

Edit: This evolved over years, see the CRASS project.

As a pentester you sometimes get access to the source code of the application you are reviewing. Sometimes you can look manually through the files, but sometimes you get million lines of code and you don’t have time to spend sitting there, reading line after line.

The first approach that came to my mind was to use static code analysis tools. There are a lot of them out there, you can find lists of tools on the flawfinder page or you can have a look at Wikipedia’s list of tools for static code analysis. It definitely makes sense to use these tools, but it needs time to download/compile them and most of them are written for one particular language.

You will be reading a lot of code. If you want to do yourself a favour, use an IDE like eclipse to look at the code. Especially when looking at Java code you will find yourself quite often changing from one class to another and changing between files. With eclipse you only need one click for that. But still, you need different IDEs for different programming languages. So this is still not a universal approach.

As a penetration tester you often want to find the interesting parts of the code. To name some interesting things: everything related to cryptography, encryption, SQL queries, file read and writes, URLs and sockets, obfuscation, passwords and so on. And there is one really universal tool that let us find these parts of the code: grep.

The script I wrote here is pretty focused on Java/Android and Objective-C/iOS. But I also got some JSP and spring java framework specific code. So here we go, no rocket science, but I hope it’s helpful for someone in the future.

#!/bin/bash
#
# A simple code greper...
#
# ----------------------------------------------------------------------------
# "THE BEER-WARE LICENSE" (Revision 42):
# <floyd at floyd dot ch> wrote this file. As long as you retain this notice you
# can do whatever you want with this stuff. If we meet some day, and you think
# this stuff is worth it, you can buy me a beer in return
# floyd http://floyd.ch @floyd_ch <floyd at floyd dot ch>
# August 2012
# ----------------------------------------------------------------------------
#
# Tested under MAC OSX ONLY!
#
# This script isn't very advanced - exactly what's needed if you don't know where to start.
# It is not a real static analysis tool and it's not in any way a replacement for all the cool
# tools out there (checkstyle, jlint, etc.)
#
 
 
if [ $# -ne 1 ]
then
  echo "Usage: `basename $0` directory-to-grep-through"
  exit 0
fi
###
#OPTIONS
###
#Open the colored outputs with "less -R" or cat, otherwise remove --color=always
ADDITIONAL_GREP_ARGUMENTS="-A 1 -B 3 --color=always"
TARGET="./grep-output"
#In my opinion I would always leave all the options below here on true,
#because I did find strange android code in iphone apps and vice versa. I would only
#change it if the greping needs very long, you are greping a couple of hundret apps
#or if you have any other performance issues with this script.
DO_JAVA=true
DO_SPRING=true
DO_JSP=true
DO_ANDROID=true
DO_IOS=true
DO_PHP=true
DO_GENERAL=true
###
#END OPTIONS
#Normally you don't have to change anything below here...
###
 
GREP_ARGUMENTS="-nrP"
STANDARD_GREP_ARGUMENTS=$ADDITIONAL_GREP_ARGUMENTS" "$GREP_ARGUMENTS
SEARCH_FOLDER=$1
mkdir $TARGET
 
echo "Your standard grep arguments: $STANDARD_GREP_ARGUMENTS"
echo "Output will be put into this folder: $TARGET"
echo "You are currently greping through folder: $SEARCH_FOLDER"
sleep 2
 
#The Java stuff
if [ $DO_JAVA ]; then
    SEARCH_STRING='javax.crypto|bouncy.*?castle|new\sSecretKeySpec\(|messagedigest'
    OUTFILE="java_general_crypto.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
     
    SEARCH_STRING='toString\(\) *==|== *toString\(\)|" *==|== *"'
    OUTFILE="java_general_wrong_string_comparison.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #String comparison has to be done with .equals() in Java, not with ==
     
    SEARCH_STRING='\.exec\('
    OUTFILE="java_general_exec.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
     
    SEARCH_STRING='java\.net\.|java\.io\.|javax\.servlet|org\.apache\.http'
    OUTFILE="java_general_io.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
     
    SEARCH_STRING='@Entity|@ManyToOne|@OneToMany|@OneToOne|@Table|@Column'
    OUTFILE="java_persistent_beans.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #Special case, we only want to know matching files to know which beans get persisted, therefore -l to output matching files
    grep -l $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #Find out which Java Beans get persisted with javax.persistence
     
    SEARCH_STRING='@Table\(|@Column\('
    OUTFILE="java_persistent_tables_and_columns_in_database.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #Case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #The source code shows the database table/column names... e.g. if you find a sql injection later on
     
    SEARCH_STRING='string .{0,10}password|string .{0,10}secret|string .{0,10}key|string .{0,10}cvv|string .{0,10}user|string .{0,10}hash(?!(table|map|set|code))|string .{0,10}passcode|string .{0,10}passphrase|string .{0,10}user|string .{0,10}pin|string .{0,10}credit'
    OUTFILE="java_confidential_data_in_strings.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #http://docs.oracle.com/javase/1.5.0/docs/guide/security/jce/JCERefGuide.html#PBEEx
fi
 
#The Java Spring specific stuff
if [ $DO_SPRING ]; then
    SEARCH_STRING="DataBinder\.setAllowedFields"
    OUTFILE="java_spring_mass_assignment.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #see e.g. http://blog.fortify.com/blog/2012/03/23/Mass-Assignment-Its-Not-Just-For-Rails-Anymore
fi
 
#The JSP specific stuff
if [ $DO_JSP ]; then
    SEARCH_STRING="escape\s*=\s*\"?\s*false|escape\s*=\s*\'?\s*false"
    OUTFILE="java_jsp_xss.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #can introduce XSS when using escape=false
     
    SEARCH_STRING="<s:file "
    OUTFILE="java_jsp_file_upload.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
fi
 
#The Android specific stuff
if [ $DO_ANDROID ]; then
 
    SEARCH_STRING='\.printStackTrace\(|Log\.(e|w|i|d|v)\('
    OUTFILE="android_logging.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #printStackTrace Logs to Android log, information leakage, etc.
     
    SEARCH_STRING='MODE_|\.openFile\(|\.openOrCreate|\.getDatabase\(|\.openDatabase\(|\.getShared|\.getCache|\.getExternalCache|query\(|rawQuery\(|compileStatement\('
    OUTFILE="android_access.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #Android file io and access things
     
    SEARCH_STRING='<intent-filter>|\.getIntent\(\)\.getData\(\)|RunningAppProcessInfo'
    OUTFILE="android_intents.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
     
fi
 
#The iOS specific stuff
 
if [ $DO_IOS ]; then
    SEARCH_STRING='NSFileProtection|NSFileManager|NSPersistantStoreCoordinator|NSData' #sqlite, see sql.txt
    OUTFILE="ios_file_access.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #File protection APIs
 
    SEARCH_STRING='kSecAttrAccessible|SecItemAdd|KeychainItemWrapper|Security\.h'
    OUTFILE="ios_keychain.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #Keychain stuff
 
    SEARCH_STRING='CFBundleURLSchemes|kCFStream|CFFTPStream|CFHTTP|CFNetServices|FTPURL|IOBluetooth'
    OUTFILE="ios_network.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #Network stuff
 
    SEARCH_STRING='NSLog\('
    OUTFILE="ios_logging.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
 
    SEARCH_STRING='initWithFormat:|informativeTextWithFormat:|format:|stringWithFormat:|appendFormat:|predicateWithFormat:|NSRunAlertPanel'
    OUTFILE="ios_string_format_functions.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #just check if the first argument to these functions are user controlled, that could be a format string vulnerability
 
    SEARCH_STRING='handleOpenURL:|openURL:'
    OUTFILE="ios_url_handler.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
fi

#The PHP stuff

if [ $DO_PHP ]; then
    SEARCH_STRING='\$_GET|\$_POST'
    OUTFILE="php_get_post.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    
    SEARCH_STRING='crypt\('
    OUTFILE="php_crypt_call.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
fi

#The general stuff
 
if [ $DO_GENERAL ]; then
    SEARCH_STRING='\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,4}\b'
    OUTFILE="email.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #Email addresses
    
    SEARCH_STRING='todo|workaround'
    OUTFILE="todo_workaround.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
 
    SEARCH_STRING='hack|crack|exploit|bypass|backdoor|backd00r'
    OUTFILE="exploit.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" | grep -vE 'Ack|setCdrBackdoor' | grep -viE 'imageshack' > $TARGET/$OUTFILE
 
    SEARCH_STRING='https?://'
    OUTFILE="https_and_http_urls.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #All URIs
 
    SEARCH_STRING='http://|ftp://|imap://|file://'
    OUTFILE="no_ssl_uris.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #Non-SSL URIs
 
    SEARCH_STRING='malloc\(|realloc\('
    OUTFILE="initialisation.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #rather rare bug, but see issues CVE-2010-0041 and CVE-2010-0042... could also happen in java/android native code...
 
    SEARCH_STRING='memcpy\(|memset\(|strcat\(|strcpy\(|strncat\(|strncpy\(|sprintf\(|gets\('
    OUTFILE="insecure_c_functions.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #memcpy
    #memset
    #strcat --> strlcat
    #strcpy --> strlcpy
    #strncat --> strlcat
    #strncpy --> strlcpy
    #sprintf --> snprintf
    #vsprintf --> vsnprintf
    #gets --> fgets
 
    SEARCH_STRING='default.?password|passwo?r?d|passcode|hash.?(?!(table|map|set|code))|pass.?phrase|salt|encryption.?key|encrypt.?key|BEGIN CERTIFICATE---|PRIVATE KEY---|Proxy-Authorization|pin'
    OUTFILE="keys.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
 
    SEARCH_STRING='root.*?detection|rooted.*?Device|is.*?rooted|detect.*?root|jail.*?break'
    OUTFILE="root.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
 
    SEARCH_STRING='sql.{0,10}injection|xss|click.{0,10}jacking|xsrf|directory.{0,10}listing|buffer.{0,10}overflow|obfuscate'
    OUTFILE="hacking_techniques.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #e.g. find prevention techniques...
 
    SEARCH_STRING='`.{2,100}`'
    OUTFILE="backticks.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #-I for binaries=without-match
    grep -I $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #anything between backticks is suspicious, thinking about command execution in perl scripts in cgi-bin directories...
    SEARCH_STRING='location\.hash|location\.href|location\.pathname|location\.search|eval\(|\.appendChild\(|document\.write\(|document\.writeln\(|\.innerHTML\s*?=|\.outerHTML\s*?='
    OUTFILE="dom_xss.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    #case sensitive...
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
 
    SEARCH_STRING='SELECT.*?FROM|INSERT.*?INTO|DELETE.*?WHERE|sqlite'
    OUTFILE="sql.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
 
    SEARCH_STRING='^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$'
    OUTFILE="base64.txt"
    #case sensitive, the regex is insensitive anyway
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #Sometimes developers try to hide stuff in base64...
 
    SEARCH_STRING='GNU\sGPL|GPLv2|GPLv3|GPL\sVersion|General\sPublic\sLicense'
    OUTFILE="gpl.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
    #GPL violation, not security related, but your customer might be happy to know such stuff...
 
    SEARCH_STRING='stupid|fuck|shit|crap'
    OUTFILE="swear.txt"
    echo "Searching for $SEARCH_STRING --> writing to $OUTFILE"
    grep -i $STANDARD_GREP_ARGUMENTS "$SEARCH_STRING" "$SEARCH_FOLDER" > $TARGET/$OUTFILE
fi
 
echo "Done grep. Results in $TARGET. Have a grepy day."

Of course the script produces a lot of false positives, but it should be a tool that supports you in your manual analysis. I’m sure there are a million more interesting strings we can add to the script. If you think something is missing, leave it in the comments and I’ll add it.

Oh and if you already looked at one version of a source code and you get a new version, you better use the “diff” command line tool and first have a look at the parts that changed.

A Weird Idea – Java, Strings and Memory

Disclaimer: I’m not a Java internals specialist. Let me know if I got a point wrong here.

Wiping sensitive information when you don’t need it anymore is a very basic security rule. It makes sense to everyone when we talk about hard discs, but the same rule should be applied to memory (RAM). While memory management is quite straight forward in C derived languages (eg. allocate some memory, override it with random bytes when the sensitive information is not needed anymore), it’s quite hard to do it in Java. Is it even possible? Java Strings are immutable, that means you can not change the memory allocated for a String. The Java VM will make a copy of the String and change the copy. So from this perspective, Strings are not a good way to store sensitive information. So let’s do it in char arrays I thought and wrote the following Java class:

/**
* ----------------------------------------------------------------------------
* &quot;THE BEER-WARE LICENSE&quot; (Revision 42):
* &lt;floyd at floyd dot ch&gt; wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return
* floyd http://floyd.ch @floyd_ch &lt;floyd at floyd dot ch&gt;
* August 2012
* ----------------------------------------------------------------------------
**/

import java.util.Arrays;
import java.io.IOException;
import java.io.Serializable;
import java.lang.Exception;

/**
 * ATTENTION: This method is only ASCII safe, not Unicode.
 * It should be used to store sensitive information which are ASCII
 * 
 * This class keeps track of the passed in char[] and wipes all the old arrays
 * whenever they are changed. This means:
 * 
 * char[] r1 = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41};
 * SecureString k = new SecureString(r1);
 * k.destroy();
 * System.out.println(r1); //Attention! r1 will be {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
 * 
 * char[] r2 = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41};
 * SecureString k = new SecureString(r2);
 * k.append('C');
 * System.out.println(r2); //Attention! r2 will be {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
 * System.out.println(k.getByteArray()); //correct
 * 
 * General rule for this class:
 * - Never call toString() of this class. It will just return null:
 * char[] r3 = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41};
 * SecureString k = new SecureString(r3);
 * System.out.println(k); //will print null, because it calls toString()
 * 
 * - Choose the place to do the destroy() wisely. It should be done when an operation
 * is finished and the string is not needed anymore.
 */

public class SecureString implements Serializable{
	private static final long serialVersionUID = 1L;
	private char[] charRepresentation;
	private char[] charRepresentationLower;
	private byte[] byteRepresentation;
	private boolean isDestroyed = false;
	private int length=0;
	private final static int OBFUSCATOR_BYTE = 0x55;
	
	/**
	 * After calling the constructor you should NOT wipe the char[]
	 * you just passed in (the stringAsChars reference)
	 * @param stringAsChars
	 */
	public SecureString(char[] stringAsChars){
		//Pointing to the same address as original char[]
		this.length = stringAsChars.length;
		this.initialise(stringAsChars);
	}
	
	/**
	 * After calling the constructor you should NOT wipe the byte[]
	 * you just passed in (the stringAsBytes reference)
	 * @param stringAsBytes
	 */
	public SecureString(byte[] stringAsBytes){
		//Pointing to the same address as original byte[]
		this.length = stringAsBytes.length;
		this.initialise(stringAsBytes);
	}
	
	/**
	 * @return length of the string
	 */
	public int length(){
		return this.length;
	}
	
	/**
	 * Initialising entire object based on a char array
	 * @param stringAsChars
	 */
	private void initialise(char[] stringAsChars){
		this.length = stringAsChars.length;
		charRepresentation = stringAsChars; 
		charRepresentationLower = new char[stringAsChars.length];
		byteRepresentation = new byte[stringAsChars.length];
		for(int i=0; i&lt;stringAsChars.length; i++){
			charRepresentationLower[i] = Character.toLowerCase(charRepresentation[i]);
			byteRepresentation[i] = (byte)charRepresentation[i];
		}
	}
	
	/**
	 * Initializing entire object based on a byte array
	 * @param stringAsBytes
	 */
	private void initialise(byte[] stringAsBytes){
		this.length = stringAsBytes.length;
		byteRepresentation = stringAsBytes;
		charRepresentation = new char[stringAsBytes.length];
		charRepresentationLower = new char[stringAsBytes.length];
		for(int i=0; i&lt;stringAsBytes.length; i++){
			charRepresentation[i] = (char) byteRepresentation[i];
			charRepresentationLower[i] = Character.toLowerCase(charRepresentation[i]);
		}
	}
	
	/**
	 * We first obfuscate the string by XORing with OBFUSCATOR_BYTE
	 * So it doesn't get serialized as plain text. THIS METHOD
	 * WILL DESTROY THIS OBJECT AT THE END.
	 * @param out
	 * @throws IOException
	 */
	private void writeObject(java.io.ObjectOutputStream out) throws IOException{		
		out.writeInt(byteRepresentation.length);
		for(int i = 0; i &lt; byteRepresentation.length; i++ ){
			out.write((byte)(byteRepresentation[i]) ^ (byte)(OBFUSCATOR_BYTE));
		}
		//TODO: Test if the Android Intent is really only calling writeObject once,
		//otherwise this could be a problem. Then I suggest that the SecureString
		//is not passed in an intent (and never serialized)
		destroy();
	}
	
	/**
	 * We first have to deobfuscate the string by XORing with OBFUSCATOR_BYTE
	 * @param out
	 * @throws IOException
	 */
	private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
		//we first have to deobfuscate
		int newLength = in.readInt();
		byteRepresentation = new byte[newLength];
		for(int i = 0; i &lt; newLength; i++ ){
			byteRepresentation[i] = (byte) ((byte)(in.read()) ^ (byte)(OBFUSCATOR_BYTE));
		}
		initialise(byteRepresentation);
	}
	
	/**
	 * Use this method wisely
	 * 
	 * When the destroy() method of this SecureString is called, the byte[] 
	 * this method had returned will also be wiped!
	 * 
	 * Never WRITE directly to this byte[], but only READ
	 * 
	 * @return reference to byte[] of this SecureString
	 */
	public byte[] getByteArray(){
		if(this.isDestroyed)
			return null;
		return byteRepresentation;
	}
	
	/**
	 * This method should never be called.
	 */
	@Deprecated
	@Override
	public String toString(){
		return null;
	}
	
	/**
	 * This method should never be called, because it means you already stored the
	 * sensitive information in a String.
	 * @param string
	 * @return
	 * @throws Exception
	 */
	
	@Deprecated
	public boolean equals(String string) throws Exception{
		throw new Exception(&quot;YOU ARE NOT SUPPOSED TO CALL equals(String string) of SecureString, &quot;+
				&quot;because your are not supposed to have the other value in a string in the first place!&quot;);
	}
	/**
	 * This method should never be called, because it means you already stored the
	 * sensitive information in a String.
	 * @param string
	 * @return
	 * @throws Exception
	 */
	
	@Deprecated
	public boolean equalsIgnoreCase(String string) throws Exception{
		throw new Exception(&quot;YOU ARE NOT SUPPOSED TO CALL equalsIgnoreCase(String string) of SecureString, &quot;+
				&quot;because your are not supposed to have the other value in a string in the first place!&quot;);
	}
	/**
	 * Comparing if the two strings stored in the SecureString objects are equal
	 * @param secureString2
	 * @return true if strings are equal
	 */
	public boolean equals(SecureString secureString2){
		if(this.isDestroyed)
			return false;
		return Arrays.equals(this.charRepresentation, secureString2.charRepresentation);
	}
	
	/**
	 * Comparing if the two strings stored in the SecureString objects are equalsIgnoreCase
	 * @param secureString2
	 * @return true if strings are equal ignoring case
	 */
	public boolean equalsIgnoreCase(SecureString secureString2){
		if(this.isDestroyed)
			return false;
		return Arrays.equals(this.charRepresentationLower, secureString2.charRepresentationLower);
	}
	
	/**
	 * Delete a char at the given position
	 * @param index
	 */
	public void deleteCharAt(int index){
		if(this.isDestroyed)
			return;
		if(this.length == 0 || index &gt;= length)
			return;
		wipe(charRepresentationLower);
		wipe(byteRepresentation);
		char[] newCharRepresentation = new char[length-1];
		for(int i=0; i&lt;index; i++)
			newCharRepresentation[i] = charRepresentation[i];
		for(int i=index+1; i&lt;this.length-1; i++)
			newCharRepresentation[i-1] = charRepresentation[i];
		wipe(charRepresentation);
		initialise(newCharRepresentation);
	}
	
	/**
	 * Append a char at the end of the string
	 * @param c
	 */
	public void append(char c){
		wipe(charRepresentationLower);
		wipe(byteRepresentation);
		char[] newCharRepresentation = new char[length+1];
		for(int i=0; i&lt;this.length; i++)
			newCharRepresentation[i] = charRepresentation[i];
		newCharRepresentation[length-1] = c;
		wipe(charRepresentation);
		initialise(newCharRepresentation);
	}
	
	/**
	 * Internal method to wipe a char[]
	 * @param chars
	 */
	private void wipe(char[] chars){
		for(int i=0; i&lt;chars.length; i++){
			//set to hex AA
			int val = 0xAA;
			chars[i] = (char) val; 
			//set to hex 55
			val = 0x55;
			chars[i] = (char) val; 
			//set to hex 00
			val = 0x00;
			chars[i] = (char) val; 
		}
	}
	
	/**
	 * Internal method to wipe a byte[]
	 * @param chars
	 */
	private void wipe(byte[] bytes){
		for(int i=0; i&lt;bytes.length; i++){
			//set to hex AA
			int val = 0xAA;
			bytes[i] = (byte) val; 
			//set to hex 55
			val = 0x55;
			bytes[i] = (byte) val; 
			//set to hex 00
			val = 0x00;
			bytes[i] = (byte) val;
		}
	}
	
	/**
	 * Safely wipes all the data
	 */
	public void destroy(){
		if(this.isDestroyed)
			return;
		wipe(charRepresentation);
		wipe(charRepresentationLower);
		wipe(byteRepresentation);
		
		//loose references
		charRepresentation = null; 
		charRepresentationLower = null; 
		byteRepresentation = null; 
		
		this.length = 0;
		
		//TODO: call garbage collector?
		//Runtime.getRuntime().gc();
		
		this.isDestroyed = true;		
	}
	
	/**
	 * This method is only to check if non-sensitive data
	 * is part of the SecureString
	 * @return true if SecureString contains the given String
	 */
	public boolean contains(String nonSensitiveData){
		if(this.isDestroyed)
			return false;
		if(nonSensitiveData.length() &gt; this.length)
			return false;
		char[] nonSensitiveDataChars = nonSensitiveData.toCharArray();
		int positionInNonSensitiveData = 0;
		for(int i=0; i &lt; this.length; i++){
			if(positionInNonSensitiveData == nonSensitiveDataChars.length)
				return true;
			if(this.charRepresentation[i] == nonSensitiveDataChars[positionInNonSensitiveData])
				positionInNonSensitiveData++;
			else
				positionInNonSensitiveData = 0;
		}
		return false;
	}
}

Does that make any sense? How does the Java VM handle this? And how does the Android Dalvik Java VM handle this? Does the Android system cache the UI inputs anyway (so there would be no point in wiping our copies)? Is the data really going to be wiped when I use this class? Where are the real Java internals heroes out there?

Update 1, 11th September 2013: Added Beerware license. Note: This was just some idea I had. Use at your own risk.

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…