Even though it is "common" knowledge that mobile apps are undertested, I often talk to people that have been hunting bugs for a while but don't include mobile apps in their tests. To me this is somewhat strange since a lot of vulnerabilities I found were discovered through inspecting the mobile app and didn't take rocket science to figure out. Thus in this blog I will try and provide a comprehensive but basic overview of how to test android apps (although most methods also apply for iOS) and use them to widen the scope of your bug bounty program.


Before moving on to decompiling the actual app it makes sense to first define the tools, hardware and initial techniques I use or ones that you can use. To run the app you basically have two options: an emulator or your own (rooted) phone. For compatibility reasons I prefer using my phone, but I have the android emulator Genymotion installed if I ever need android apps on my laptop.

The second step is to make sure you have the app (APK) file installed on your device. Here you can install the app via the Google Play Store and get the APK by browsing to /data/app/{PACKAGE_NAME}/base.apk on your mobile device or get it from an APK library like APKPure.

Once you have installed the app make sure you proxy the traffic of your phone through Burp Suite. Setting this up should be fairly easy using tutorial setup and tutorial certificate.

Using the app

Often one of the most important things to do is just proxying all the functions/actions you can find in the mobile app through Burp, much like you would do in a normal web app. The main point here is to discover as much as possible. In my experience companies almost always use different endpoints for their mobile app than they do for their web app. Often mobile apps use API like structures, which contain less XSSes and other client-side vulnerabilities, but more server-side vulnerabilities like IDORs. There is not much more to this phase than any test where you use Burp.

If for some reasons the app is not making any connection to the server or Burp isn't catching any responses you are likely dealing with either SSL pinning or non-http traffic. My first advice is to try and see if Packet Capture shows any weird data coming from the app to a port that isn't 80 or 443 or anything involving those numbers (8443). If you are just seeing empty data to a https port (443) you are likely dealing with SSL pinning. While I'm not an expert on this solutions like SSL TrustKiller and SSLUnpinning might help getting past those (bypasses standard libraries). For more information read the detailed blog on bypassing certificate pinning by Patrik Fehrenbach.

Static analysis

While I'm not a hero in static analysis I have discovered some simple tricks that make it possible for me to gain a lot out of the code of a mobile app. This all without being able to read smali code or walking through huge blocks of (obfuscated) Java code. This is also very useful if the app is performing SSL pinning, since this is a method to still find the endpoints the app uses.

First of all, I decompile the APK file both with Apktool and Dex2jar and merge these files in one folder to keep everything together. After this my rule is: use smali code for tool analysis and the Java code to read the stuff found in the smali code. I'll explain what I mean with this.

One method I like is using grep to find certain endpoints in the smali code (also use grep to look for secrets or basic auth). Here I navigate in the terminal to the folder with the smali code and use the RegEx parameter to specify my search. The point here is to find a similarity in all the endpoints the mobile app uses (discovered through Burp) and develop a RegEx for this to find more occurrences like this. This can be as simple as \.json or http(s)?:// to find endpoints with a json extension or http prefix. The command looks the following (exclude the h if you want the file names):

$ grep -hnrE "\.json" /Users/Gerben/smali/

The results that you get from this command can be easily searched in JD-GUI to find the files where they are mentioned. Here you can also find parameters that are needed to reconstruct the full URL. These parameters should be fairly obvious if you are used to at least reading a programming language.

Three parameters plus the endpoint (feedback.json) in red boxes

A second method I recently adopted is using LinkFinder to find all URLs in the smali code of an app. LinkFinder itself takes about 20-40 seconds, however copying all the smali files in one folder with random names (prevent overwriting) takes a bit longer. Thus, tomnomnom and I developed an one-line bash script that decompiles the app, copies the files to one subfolder and then runs LinkFinder over these files (usually a couple thousand). It can be useful to delete folders with smali code that contain external libraries (e.g. /com/Google/*) to hide results that do not belong to your scope.

$ apktool d app.apk; cd app;mkdir collection; find . -name \*.smali -exec sh -c "cp {} collection/\$(head /dev/urandom | md5 | cut -d' ' -f1).smali" \;; linkfinder.py -i 'collection/*.smali' -o cli

You'll probably get a bunch of results. To narrow this down use the RegEx option (-r) in LinkFinder to narrow down the endpoints and list ones that confirm to a certain pattern. Again same method applies here, look for these endpoints in the Java code using JD-GUI. Like:

$ linkfinder.py -i 'collection/*.smali' -o cli -r '\.json'

Thirdly, after decompiling check the /assets and /res/raw folders. Sometimes valuable files can be left here (source: Advanced Android Bug Bounty skills - Ben Actis, this resource is very good for android bugs, like vulnerable intents).

Finally, comparing these methods among different versions of the app might help you to gain an understanding of what was added in a new version. New endpoint are likely untested and might contain new vulnerabilities.

Real world example

Using a grep search I found that the following endpoint was present in the mobile app I was testing: order/number_verification/verify_phone.json?. Looking for this endpoint in the Java code generated by dex2jar I found that this particular piece of code wasn't converted due to an error (this happens quite often). Thus I went back to the original smali file where the endpoint was found. Here I was faced with the following code (try and make sense of it yourself):

const-string v2, "order/number_verification/verify_phone.json?"

invoke-virtual {v0, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v0

invoke-static {}, Lcom/████████/a/d/c/a;->a()Ljava/lang/String;

move-result-object v2

invoke-virtual {v0, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

move-result-object v0

invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

move-result-object v0

.line 383
new-instance v2, Lb/q$a;

invoke-direct {v2}, Lb/q$a;->()V

.line 384
const-string v3, "verification_type"

iget-object v4, p0, Lcom/library/████████/ordering/order/CheckPhoneVerificationFragment$VerifyPhone;->verificationType:Ljava/lang/String;

invoke-virtual {v2, v3, v4}, Lb/q$a;->a(Ljava/lang/String;Ljava/lang/String;)Lb/q$a;

.line 385
const-string v3, "phone"

const/4 v4, 0x0

aget-object v4, p1, v4

invoke-virtual {v2, v3, v4}, Lb/q$a;->a(Ljava/lang/String;Ljava/lang/String;)Lb/q$a;

.line 386
const-string v3, "country_id"

As you might have noticed we see the URL up top, and three const-strings which look a lot like parameters. After playing around with these variables I found that a valid request would be:

POST /v2/order/number_verification/verify_phone.json HTTP/1.1
Body: verification_type=sms&phone=612345678&country_id=1

This request would leak the verification code that was going to be send to the mobile device in the response body. Leaking this allows an attacker to bypass the phone verification completely and potentially bypass 2FA.