Android Penetration Testing
Contents
1. Setup and Decompile
2. Verify Signing
3. Check for Hardcoded and URL endpoints
4. Digging into AndroidManifest.xml File
5. Network
6. Storage
7. Analyze the source code thoroughly specially on
8. Reverse Engineering
9. Cryptography
10. Exploiting backup
11. Exploiting Android Debuggable
12. Exploiting Exported Components with drozer
13. Exported Components with APKAnalyzer
14. DeepLink URIs
15. Intercepting HTTPS request on Java Based Application
16. Intercepting HTTPS request on Flutter Based Application
17. Intercepting HTTPS request on Webview Based Application
18. Testing APIs
19. References
20. More Resources
Setup and Decompile
- Install the application on emulator or physical device
- View the package name of application with ADB
adb shell pm list packages | grep "app-name"
- View the path of the application.
adb shell pm path "package-name"
- Decompile the application with APKTOOL
apktool d app.apk
- Decompile the application with JADX
jadx /path/to/apk -d /path/to/output
Verify Signing
- Check if the application has been properly signed.
1 2
apksigner verify --verbose app.apk jarsigner -verify -verbose -certs app.apk
Check for Hardcoded and URL endpoints
- Hardcoded Emails
` grep -HnroE ‘\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,6}\b’ awk -F: ‘{print $3}’`
- Hardcoded AWS Keys
- ` gf aws-keys`
- Hardcoded IPs
- ` gf ip`
- Hardcoded Base64 data
- ` gf base64`
- Hardcoded usernames and passwords on the source code
- Hardcoded data on strings.xml ➡️ /res/values/strings.xml
- Hardcode encryption keys and secrets
- Hardcoded data on raw.xml ➡️ /res/values/raw.xml
- Hardcoded URL endpoints
- ` gf urls`
Digging into AndroidManifest File
- Check if backup is enabled ➡️
android:allowBackup="true"
- Check if debug mode is enabled ➡️
android:debuggable="true"
- DeepLink ➡️ Check for deeplink schemas
- Permission ➡️ Check if application is asking unnecessary permission.
- Permission ➡️ Check if application is having permission like External Storage, SMS
- Check the exported activites, services, providers and receivers
- Intent Filter also makes components exported by default
- Task Hijacking ➡️ Check if
launchMode=singleTask
defined - Check the Implicit and Explicit Intents
- Note down the interesting activites on the manifest file
- Check if HTTP is enabled ➡️
cleartextTrafficPermitted="true"
- Checked for exported components
apkanalyzer manifest print appname.apk
Network
- Check the Network Security config file ➡️
/res/xml/network_security_config.xml
- URL with HTTP protocol on the source code
- Find the URLs with
gf url
and list the HTTP only - Or, with GREP ➡️
grep -iRn "http://"
- Find the URLs with
- Check if SSL pinning is enabled on the application or not
Storage
- Shared Preferences
- Check if the shared preferences contains sensitive data unecrypted ➡️
/data/data/package-name/sharedprefs
- World Writable - Check if the shared preferences is world writable
- World Readable ➡️ Check if the shared preferences is world readable
- MODE_WORLD_READABLE allows all applications to access and read the content of the file
1
SharedPreferences sharedPref = getSharedPreferences("key", MODE_WORLD_READABLE);
- Check if the shared preferences contains sensitive data unecrypted ➡️
- SQLite Database
- Check if database contains sensitive information unencrypted ➡️
/data/data/package-name/files/
- Check if database contains sensitive information unencrypted ➡️
- Firebase
- Check for firebase URL endpoint with
gf url
or on/res/values/strings.xml
- Add /.json on the end of the URL
curl -X GET https://subdomain.firebaseio.com/.json
- The endpoint will be vulnerable if it does not replies with
"error" : "Permission denied"
- The endpoint will be vulnerable if it does not replies with
- Check for firebase URL endpoint with
- Internal Storage
- World Writable - Check if the internal storage is world writable
- World Readable - Check if the external storage is world readable
1
fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
- Analyse the data in internal storage
- Pull the internal storage without using (just after installation)
adb pull /data/data/package-name
- Pull the internal storage after using (full feature usage)
adb pull /data/data/package-name
- Analyze the difference
- Pull the internal storage without using (just after installation)
- External Storage
- Application can store data externally on SD card or on internal (non-removal).
- Pull the external storage of the application and look for any sensitive information. ```bash
locate the external storage of the app
adb shell ls /sdcard/Android/data
Pull the external directory of the app
adb pull /sdcard/Android/data/”package-name”
1 2 3 4
- File saved to external storage is World Readable which can be accessed by other apps. ```java File file = new File (Environment.getExternalFilesDir(), "password.txt");
- Search for Classes and Functions such as
MODE_WORLD_READABLE
orMODE_WORLD_WRITABLE
- the
SharedPreferences
class (stores key-valu pairs) - the
FileOutPutStream
class (uses internal or external storage) - the
getExternal*
functions(use external storage) - the
getWritableDatabase
function (returns a SQLiteDatabase for writing) - the
getReadableDatabase
function (returns a SQLiteDataabase for reading) - the
getCacheDir
andgetExtrernalCacheDirs
function (use cached files)
- Check for HTTP caching (sensitive information) which is stored in Cache.db in internal storage
- Keyboard Cache
- Login and Registration
- Password Forgot and Reset
- Activity which has implemented the intent defined on AndroidManifest.xml
- Encryption and Decryption
- Web API Intregation
- Transactions Related
- OTP Related
- Activity which has implemented webviews
- Database, Shared Preferences, Internal and External Directory Integration
- Build Configs
Reverse Engineering
- Decompile
- Basic Decompilation
apktool d base.apk
- Decompile only resource
apktool d -s base.apk
- Decompile only source
apktool d -r base.apk
- If above give errors
apktool --use-aapt2 d base.apk
- Basic Decompilation
- Recompile
apktool b --use-aapt2 -o out.apk base
- Generate a key
keytool -genkey -v -keystore keys/test.keystore -alias Test -keyalg RSA -keysize 1024 -sigalg SHA1withRSA -validity 10000
- Sign the application
jarsigner -keystore keys/test.keystore dist/test.apk -sigalg SHA1withRSA -digestalg SHA1 Test
- Zipalign the application
zipalign -v 4 <YOUR_APP_NAME> app-release-final.apk
- Automate the signing and zipalign with uber-apk-signer
java -jar uber-apk-signer-1.2.1.jar -a out.apk
Cryptography
- Check if application is communicating with server encrypted or unencrypted
- Check if the private key secrets are being stored hardcode on the source code
- Check if the application is using Base64 for encrypting data for transmitting or storing ➡️
gf base64
- Check for depreciated algorithms like RC1, MD4, MD5 and SHA1 which causes collision issues
- Analyze the source code and workflow of the encryption technique
- Sometimes there might not be issues on hardcoding secrets, depreciated algorithm, but the implementation of the techniques might be done insecurely
- Check if the application has implemented their own custom cryptographic algorithm and try to reverse it.
Exploiting backup
- Check if application has backup enabled. If enabled, use following commands to extract internal storage even if the device is non-rooted.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# Look for backup on Manifest file android:allowBackup="true" # List the package name adb shell pm list packages | grep "app name" # Backup the data contained in the package. adb backup "package-name" # The file name will be backup.ab # Convert .ab extension with android backup extractor. Download link: https://sourceforge.net/projects/adbextractor/ # Convert .ab into tar java -jar abe.jar unpack backup.ab backup.tar # Extract the tar file tar -xvf backup.tar # Analyze the files in the extracted folder and look for directory like databases, shared preferences, files
Exploiting Android Debuggable
- Check if application has debuggable mode enabled. If enabled, use following commands to access the android shell as the vulnerable application even if the device is not rooted.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
# Look for debuggable mdoe on Manifest file android:debuggable="true" # Before app start run, adb jdwp # It will list all the process ID of the running applications. # After app start run, adb jdwp # The currently started application process ID can be identified. adb shell ps | grep "ID-number" # It will give the package name of the ID. adb shell run-as package-name # Or, adb shell pm list packages | grep "app-name" adb shell run-as package-name
- For further exploitation, follow Link1 and Link2.
Exploiting Exported Components with drozer
Setup
- Download drozer app for android and PC from here.
- Install the applications on android and PC.
- Forward the port from PC
1
adb forward tcp:31415 tcp:31415
- Open the drozer application on android device and click on ON button
- Launch the application
1
drozer console connect
Retrieving Package information
- View the package name of the application
1
run app.package.list -f "app-name"
- View the basic information of the package
1
run app.package.info -a "package-name"
- View the Manifest information of the package
1
run app.package.manifest "package-name"
- Identify the attack surface
1 2
run app.package.attacksurface "package-name" # It will list all the exported activities, services, providers, broadcasts.
Exported Activities
- Verify if unnecessary activities are exported and exploit it
1 2 3 4 5 6 7 8
run app.activity.info -a "package-name" # It will list the exported activities run app.activity.info -a "package-name" -u # It will list all the activities # Start the exported activity from drozer and see the result on android device run app.activity.start --component "package-name" "component-name"
Exported Content Providers
- Verify if any content providers are exported and exploit it
1 2 3 4 5 6 7 8 9 10 11
run app.provider.info -a "package-name" # It will list the exported providers run scanner.provider.finduris -a "package-name" # It will list the URIs of the exported providers run app.provider.query "uri" # List the query on exported URIs run app.provider.update "uri" --selection <conditions> <selection arg> <column> <dataa> # Update the database using exported URIs
- SQLi on Content providers
1 2 3 4 5
run scanner.provider.sqltables -a "package-name" # List the tables run scanner.provider.injection -a "package-name" # Performs some basic SQLi
- Path traversal on Content providers
1
run scaner.provider.traversal -a "package-name"
Exported Broadcast Receivers
- Verify any exported broadcast receivers and exploit it
1 2 3 4 5 6 7 8
run app.broadcast.info -a "package-name" # List the exported broadcast receivers run app.broadcast.send --component "package-name" "component-name" --extra <type> <key> <value> # Send data through exported broadcast receivers run app.broadcast.sniff --action <action> # Sniff the data through exported broadcast receivers
Exported Services
- Verify any exported services and exploit it
1 2 3 4 5 6 7 8
run app.service.info -a "package-name" # List the exported services run app.service.start --action <action> --component <package-name> <component-name> # Start the exported services run app.service.send <package-name> <component-name> -msg <what> <arg1> <arg2> --extra <type> <key> <value> --bundle-as-obj # Send data through services
Exported Components with APKAnalyzer
- Drozer might not work on every device, we can use apkanalyzer for exported components in such case.
- Exported Activities
apkanalyzer manifest print app-name.apk | grep -i activity
- Start an activity
adb shell am start -n com.example.demo/com.example.test.MainActivity
- Start an activity
- Exported Services
apkanalyzer manifest print app-name.apk | grep -i service
- Start the services
adb shell am startservice com.some.package.name/.YourServiceSubClassName
- Start the services
- Exported Content Providers
apkanalyzer manifest print app-name.apk | grep -i provider
- Query the content providers
adb shell content query --uri content://com.myapp.authority/path --where column=x --arg 1 --sort column_name DESC
- More on Stack Overflow
- Query the content providers
- Exported Broadcast Receivers
apkanalyzer manifest print app-name.apk | grep -i broadcast
- Start broadcast
adb shell am broadcast -a <Action-Name> -n <package-nname/.BroadcastReceiver> --es <parameter> <value>
- Start broadcast
DeepLink URIs
- Download the below script.
- Decompile the application.
- Execute below commands
1 2 3
git clone https://github.com/teknogeek/get_schemas pip3 install -r requirements.txt python3 get_schemas.py -m ./com.twitter.android/AndroidManifest.xml -s ./com.twitter.android/res/values/strings.xml
- Execute the deeplink
adb shell am start -W -a android.intent.action.VIEW -d "example://gizmos" com.example.android
- For manual enumeration and exploitation, check out another notes of mine here.
Intercepting HTTPS request on Java Based Application
With Frida
- Download and install frida
1
pip3 install frida-tools
- Download frida servers according to your android architecture from here and rename it to frida-server
1 2
# View CPU architecture of the android device adb shell getprop ro.product.cpu.abi
- Download the certificate from burp and copy as cert-der.crt
- Push the server and certificate file to android
1 2 3 4 5
adb root # might be required adb push frida-server /data/local/tmp/ adb push cert-der.crt /data/local/tmp/ adb shell "chmod 755 /data/local/tmp/frida-server" adb shell "/data/local/tmp/frida-server &"
- Download the SSL Pinning bypass script from here and save it as sslPinningBypass.js
- Change proxy on android device
- Long press on wifi
- Click on Advance option
- Change proxy to manual
- Enter your PC ip address
- Enter a random port (like 5567)
- Save
- Open the burp and Change the proxy option from 127.0.0.1:8080 to PC-IP:5567 (port number must be same)
- Grab the package name with
adb shell pm list packages | grep "app-name"
- Run the script
1
frida -U -l sslPinningBypass.js -f "package-name" --no-pause
- The application will open automatically and now the requests will be captured on burp
With Objection (works also on non-rooted device)
- Install Objection
1
pip3 install objection
- Patch the apk with Objection
1
objection patchapk -s base.apk
- If the above patch failed, follow here for manual patch of application using objection
- Installed the patched apk
1
adb install patched.pak
- Proxy the android route into PC as described on frida section
- Setup burp as described on frida section
- Run the Application
- Run Objection explore
1
objection explore
- Disable SSL Pinning
1
android sslpinning disable
- Burp will start intercepting the packets
Intercepting HTTPS request on Flutter Based Application
The use of Frida on bypassing SSL Pinning may not work on flutter. I also performed some exploitatio in libflutter.so file for injecting the memory address of the certificate checker. However it did not work as intended. Here is the reference for bypassing SSL Pinning on flutter application through the exploitation of libflutter.so file.
This is an alternative method for bypassing SSL Pinning in Flutter application.
Note: I have tested this method on few aplications with success but this might not work as well.
- Requirements
- Genymotion: Android Emulator
- ProxyDroid: Since flutter appication ignores manual proxy configuration through wifi
- Burp Suite
- Setup and Configurations
- Install ARM Translation tool on the device since flutter application and proxydroid may not work in genymotion since it is of x86 architecture.
- Click on the File Upload widget and INSTALL OPEN GAPPS
- Click INSTALL
- Wait for the package to be installed
- When done, you will be prompted to reboot the virtual device.
- (Optional) Open the Play Store and configure you account.
- Verify ARM Translation tools installtion
1 2
adb shell getprob ro.product.cpu.abilist # This command should return "x86,armeabi-v7a,armeabi" as the output.
- Download the Burp Certificate
- Convert Burp Certificate into PEM format
- Android only accepts certificate of PEM format if needs to be installed as system certificate.
- We can convert the burp certificate into PEM format using openssl. More
1 2 3 4 5 6 7
openssl x509 -inform DER -in cacert.der -out cacert.pem openssl x509 -inform PEM -subject_hash_old -in cacert.pem | head -1 # Rename the PEM file with the output given by the second command mv cacert.pem <RandomAlphaNumeric>.0 # Example: mv cacert.pem 4a787b67.0
- Push the certificate file into android application
- We need to push the certificate into /system/etc/security/cacerts directory inorder to install it as system certificate. But although having root access on the device, permission to write on system directory is denied. For this we need to remount the device.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
adb root adb remount # Push the file adb push 4a787b67.0 /system/etc/security/cacerts/ # If the above command still throws error. Consider pushing it into the sdcard at first. adb push 4a787b67.0 /sdcard/Download/ adb shell mv /sdcard/Download/4a787b67.0 /system/etc/security/cacerts/ # Change the permission of the file (inside adb shell) chmod 644 /system/etc/security/cacerts/4a787b67.0 # Reboot the device (inside adb shell) reboot
- Verify the certicate installation by navigating to installed certificates on android device through settings. Should list Portswigger CA.
- We need to push the certificate into /system/etc/security/cacerts directory inorder to install it as system certificate. But although having root access on the device, permission to write on system directory is denied. For this we need to remount the device.
- Install and Configure the ProxyDroid aplication on the device
- Since the flutter application ignores the proxy configured through wifi options, proxydroid must be installed.
- Download Link is here
- Open the application
- Enter the IP address of your computer (which intercepts the traffic) on the host parameter and a random port number and enable the proxy.
- Remove the custom proxy from the Wifi advance option if you have configured previously.
- Burp’s Things
- Open the Burp
- Go To: Proxy ➡️ Options ➡️ Add ➡️ Request handling
- Enter the previously entered host and port from proxy droid
- Click on Support invisible proxying.
- Save the configuration, and you will be able to intercept the traffic of the flutter application
Intercepting HTTPS request on Webview Based Application
- Grab the package name.
frida-ps -Uai | grep -ia 'app-name'
- Save the below JS code. fridawebview.js
Java.perform(function() { var array_list = Java.use("java.util.ArrayList"); var ApiClient = Java.use('com.android.org.conscrypt.TrustManagerImpl'); // Cert pin bypass by https://techblog.mediaservice.net/2018/11/universal-android-ssl-pinning-bypass-2/ ApiClient.checkTrustedRecursive.implementation = function(a1,a2,a3,a4,a5,a6) { console.log('Bypassing SSL Pinning'); var k = array_list.$new(); return k; } var WebView = Java.use('android.webkit.WebView'); WebView.loadUrl.overload("java.lang.String").implementation = function (s) { console.log('Enable webview debug for URL: '+s.toString()); this.setWebContentsDebuggingEnabled(true); this.loadUrl.overload("java.lang.String").call(this, s); }; },0);
- Proxy the request from Android device to your PC.
- Run the command
frida -U -l fridawebview.js -f com.packagename
Testing APIs
- Please follow this for API Pentesting Checklist
References
- OWASP MSTG
- Hacktrick Android Application Penetration
- https://gist.github.com/incogbyte/1e0e2f38b5602e72b1380f21ba04b15e