As a bug hunter, are your bug bounty reports getting rejected because you don't use a "malicious" Proof of Concept (PoC) app to exploit the vulnerabilities?
As a security engineer, do you struggle with validating bug bounty reports, performing regression testing, and conduct penetration testing?
I've got you covered!
Rooting your device is not required.
For more tips and tricks check my Android Penetration Testing Cheat Sheet.
Built with Android Studio v2024.3.2 (64-bit) (JDK 17) and tested on Samsung Galaxy Note S20 Ultra with Android OS v13.0 (Tiramisu).
Made for educational purposes. I hope it will help!
Future plans:
- add an option to bind to a service,
- add a content encoding and decoding section,
- hide soft keyboard when user taps outside a text input.
Version: 4.2
APK name: Malware APK
Package name: com.kira.malware
Min. SDK: 28
Target SDK: 35
Exported activities:
com.kira.malware.activities.MainActivitycom.kira.malware.activities.HiddenActivity
Permissions required:
android.permission.READ_EXTERNAL_STORAGEandroid.permission.WRITE_EXTERNAL_STORAGEandroid.permission.QUERY_ALL_PACKAGESandroid.permission.INTERNETandroid.permission.SYSTEM_ALERT_WINDOWandroid.permission.BIND_ACCESSIBILITY_SERVICEandroid.permission.BIND_NOTIFICATION_LISTENER_SERVICEandroid.permission.POST_NOTIFICATIONS
URIs for internal QA testing:
kira://hiddencontent://com.kira.malware.TestFileProvider/files/test.txtcontent://com.kira.malware.TestSQLiteProvider
#1: Read and modify files of another app.
#2: Read world-readable shared preferences of another app.
Figure 1 - File System
#1: Not all devices or root tools store the su (switch user) binary in the same location.
#2: Run CLI tools like /system/bin/logcat or start a reverse shell with user /bin/sh or root /bin/su privileges.
Figure 2 - Running CLI Tools
#1: Read the manifest file of another app.
#2: List protected or exported components of another app.
#3: Request a custom permission defined by another app by declaring it in this app's AndroidManifest.xml, then rebuild the APK - this works only if the permission's protection level is not signature.
<permission
android:name="com.someapp.dev.CUSTOM_PERMISSION"
android:protectionLevel="normal" />
<uses-permission android:name="com.someapp.dev.CUSTOM_PERMISSION" />#4: List user installed or system installed packages.
Figure 3 - Enumeration
#1: Test an intent filter of another app.
#2: Send an intent to another app to bypass its biometrics / security.
#3: Send an intent to another app to bypass its biometrics / security by triggering its push notification manager, then manually opening the received push notification.
#4: Send an intent to another app to poison its widget.
#5: Send a [pending] intent to another app multiple times to cause Denial of Service (DoS).
#6: Send a mutable pending intent to another app to extract subsequently added intent extras.
#7: Access a protected component, such as a file or SQLite content provider of another app, by exploiting its exported (proxy) component.
#8: Test a deep link of another app.
#9: Hijack a deep link of another app by specifying it in this app's AndroidManifest.xml under HiddenActivity, then rebuild the APK.
<data
android:host="hidden"
android:scheme="kira" />#10: Perform a battering ram attack on a deep link or content provider URI of another app by adding </payload> placeholder into the intent's URI.
#11: Send your intent to HiddenActivity to inspect it and its extras before dispatching.
The following applies only to the proxy intent extras:
- If the value is a string equal to
</target-pending-intent>:- the entire value will be replaced with an
PendingIntentobject oftarget intent, - and
Intent.putParcelable()will be used.
- the entire value will be replaced with an
- If the value is a string equal to
</target-intent>:- the entire value will be replaced with an
Intentobject oftarget intent, - and
Intent.putParcelable()will be used.
- the entire value will be replaced with an
- If the value is a string containing
</target-intent-uri>:- all matching parts will be replaced with
Intent.toUri(Intent.URI_INTENT_SCHEME)oftarget intent.
- all matching parts will be replaced with
- If the value is a string containing
</target-intent-uri-unsafe>:- all matching parts will be replaced with
Intent.toUri(Intent.URI_ALLOW_UNSAFE)oftarget intent.
- all matching parts will be replaced with
The following applies on both the proxy intent and target intent extras:
- To use the file content provider callback:
- add an intent extra with the type
string, - key
HiddenActivity, - and value
</file-provider>.
- add an intent extra with the type
- To use the SQLite content provider callback:
- add an intent extra with the type
string, - key
HiddenActivity, - and value
</sqlite-provider>.
- add an intent extra with the type
- To auto-close the callback activity on success:
- add an intent extra with the type
string, - key
HiddenActivityClose, - and value
</close-on-success>.
- add an intent extra with the type
- To auto-close the callback activity on error:
- add an intent extra with the type
string, - key
HiddenActivityClose, - and value
</close-on-error>.
- add an intent extra with the type
When testing intent injections, you will often need to specify com.kira.malware.activities.HiddenActivity as the target intent class name, and scope the file or SQLite content provider intent extra to the same target intent, as shown in the images below.
YouTube: Malware APK v4.0 - Proxy Intent Injection PoC
Figure 4 - Intent Fuzzing
Figure 5 - Pending Intent Injection P1
Figure 6 - Pending Intent Injection P2
Figure 7 - Intent Injection P1
Figure 8 - Intent Injection P2
#1: Listen for a broadcast intent from another app and extract sensitive information from its extras.
Figure 9 - Broadcast Monitor
#1: Verify whether misconfigured asset links allow app link hijacking — this applies only on intent filters with autoVerify set to true.
#2: Initiate a deep link callback from a website to hijack the flow of another app.
#3: Leverage existing web browser sessions to hijack the authenticated flow of another app.
#4: Hijack the OAuth flow and complete it by automating the remaining steps.
#5: All values extracted from a deep link or response body are URL-decoded and only URL-encoded when inserted into the URL query string (after ?) of another request.
Figure 10 - Web
Each time you run the app, make sure to open the Web section to activate the deep link callback flow.
Figure 11 - Deep Link Callback Flow
#1: Changing the task affinity at runtime is not possible.
#2: To hijack a task of another app, modify the task affinity in this app's AndroidManifest.xml under MainActivity, then rebuild the APK.
Read more about the taskjacking here.
Figure 12 - Taskjacking
#1: Test if another app can detect an overlay.
#2: Detect an overlay by checking MotionEvent.FLAG_WINDOW_IS_OBSCURED and MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED flags - this solution works only on older Android versions.
Read more about tapjacking here.
Figure 13 - Tapjacking
#1: Extract sensitive information from another app's UI by abusing the accessibility service.
Read more about the solutions here;
Figure 14 - Accessibility Monitor
#1: Extract sensitive information from another app's notification by abusing the notification service.
Figure 15 - Notification Monitor
#1: Dump the clipboard and look for sensitive information.
Figure 16 - Clipboard
#1: Save and load the UI state at any time.
#2: Download and share UI state files with others, and upload files shared by others at any time.
Figure 17 - State Manager
#1: Additional system controls and UI customizations, minimizing the need to rebuild the APK.
Figure 18 - Settings