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/<you app’s name>/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

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:

/**
* ----------------------------------------------------------------------------
* "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
* ----------------------------------------------------------------------------
**/

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<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<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 < 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 < 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("YOU ARE NOT SUPPOSED TO CALL equals(String string) of SecureString, "+
				"because your are not supposed to have the other value in a string in the first place!");
	}
	/**
	 * 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("YOU ARE NOT SUPPOSED TO CALL equalsIgnoreCase(String string) of SecureString, "+
				"because your are not supposed to have the other value in a string in the first place!");
	}
	/**
	 * 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 >= length)
			return;
		wipe(charRepresentationLower);
		wipe(byteRepresentation);
		char[] newCharRepresentation = new char[length-1];
		for(int i=0; i<index; i++)
			newCharRepresentation[i] = charRepresentation[i];
		for(int i=index+1; i<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<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<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<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() > this.length)
			return false;
		char[] nonSensitiveDataChars = nonSensitiveData.toCharArray();
		int positionInNonSensitiveData = 0;
		for(int i=0; i < 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)
      • 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”).

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.

#!/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.