Allsafe Android Walkthrough - Part 1
Introduction
Allsafe is just another intentionally vulnerable Android application. The app is built with kotlin and contains many vulnerabilities with a nice difficulty curve. The main reason I’m writing a blog about this one is that it contains some nice Android challenges and there are no public writeups for the app till now.
Prerequisites
Before starting, I’m assuming that the reader to have:
- Basic understanding of different components that Android apps use.
- Rooted Android device/Emulator with frida installed.
- Android studio installed.
1. Insecure Logging
Developers normally use logging
to trace their code, and troubleshoot errors but, sometimes developers write sensitive data in the logs such as login credentials or authentication tokens.
To start testing for Insecure Logging
in the app you first need to make sure that you are connected to the device via adb
then use logcat to monitor the device logs.
The challenge is straightforward and we don’t need to dig deep into the code. After you open the challenge and before typing anything in the textview you first need to use logcat
to monitor the logs. Doing that is as easy as adb logcat
but, if you do it this way you’ll receive a massive amount of logs from every service and application running on your device. Instead, you want to limit the logs you see to Allsafe
only. To do that you can use the --pid
to monitor logs generated from the Process id of your target only
adb shell --pid=$(adb shell pidof -s infosecadventures.allsafe)
Once you’ve done that you can type any string in the textview and once you press Done
you’ll notice that the supposedly secret/sensitive data is logged by the application.
2. Hardcoded Credentials
The task here is to find 2 sets of hardcoded usernames and passwords in the source code of the challenge.
The Challenge’s code is located in infosecadventures.allsafe.challenges.HardcodedCredentials
class. The first set of creds can be found so easily defined in the string constant BODY
.
If you scroll down a bit and inside the onClick
method, you’ll notice another string that is being used as a part of a request that is being constructed.
To find that dev_env
string navigate to /Resources/resources.arsc/res/values/strings.xml
and search for it
3. Firebase Database
Firebase is a Realtime cloud-based NoSQL database that can be integrated with both Android and iOS apps. To start our testing we first need to find the firebase URL. In jadx navigate to /Resources/resources.arsc/res/values/strings.xml
and search for firebase
to locate it
Next, from your browser try to access the /.json
endpoint. If you get a Permission Denied
response that means that the database is properly configured but if get a json data or even null
as a response that means you at least have read permission.
4. Insecure Shared Preferences
Data stored in a SharedPreferences
object is written to an XML file within the application’s data directory. If you look at the code of this challenge you’ll see that getSharedPreferences("user", 0)
creates a shared preference object with the default creation mode MODE_PRIVATE
and is named user.xml
. After that, the plaintext username and password of a registered user are stored in the xml file.
This is a very bad practice because while on non-rooted devices, other apps normally can’t read that xml file, it’s not the same case on a rooted android device. You can confirm the existence of an Insecure Data Storage
vulnerability by navigating to /data/data/infosecadventures.allsafe/shared_prefs/
and reading user.xml
5. SQL Injection
This one is just another basic and lame SQL Injection challenge. The challenge is to exploit a SQL Injection vulnerability in the login function which can be done using the good old ' or 1=1 --
payload and a dummy password.
If you want to investigate it more, you’ll notice that the username and password are both parts of a raw SQL query that lacks proper sanitization
6. PIN Bypass
In this challenge, there is a hardcoded base64 encoded PIN in the app that we need to enter in the textview to solve the challenge. The goal of this challenge is to use frida to override the check function and force it to return true
for any PIN but since the PIN is juat a 4-digit number, why not write a frida script to burteforce the correct PIN as well?
If you look at the source code, you’ll see that the function responsible for validating the PIN is called checkPin
and it returns true if the PIN we entered is the same as the hardcoded one.
The approach here is to write a simple for loop that calls the checkPin
method with possible PIN values (0000 - 9999)
as a string parameter, and once the correct PIN is passed to the function it’ll return true signaling that we found the correct value.
Java.perform(function(){
var pinClass = Java.use("infosecadventures.allsafe.challenges.PinBypass");
pinClass.checkPin.implementation = function(pin){
for(var i = 0; i <= 9999; i++){
var isValidPin = this.checkPin(String(i).padStart(4,0));
if (isValidPin){
console.log("[+] Valid PIN found: " + i);
break;
}
}
return true;
}
});
Now, spawn the app using frida and give it the script to be injected as follows:
frida -U -l pinbypass.js -f infosecadventures.allsafe --no-pause
Finally, open the PIN Bypass
challenge once more, enter a dummy pin to trigger the checkPin
function and you’ll find that the correct pin is logged in the frida REPL shell.
7. Root Detection Bypass
Allsafe is using RootBear to check whether or not the app is running on a rooted device. While true it performs a lot of checks but they are all boolean function that can be hooked easily and forced to return false, but the checks are implemented in a naive way because all the checks are being called from a function named isRooted
so we can just hook isRooted
and force it to return false every time using the below frida script:
Java.perform(function(){
var rootbeerClass = Java.use("com.scottyab.rootbeer.RootBeer");
rootbeerClass.isRooted.implementation = function(){
return true;
}
});
Finally, use frida to inject the script into the app and you’ll easily bypass the checks.
7. Secure Flag Bypass
This one and I quote: Window flag: treat the content of the window as secure, preventing it from appearing in screenshots or from being viewed on non-secure displays.
You’ll find it commonly used in banking applications. FLAG_SECURE
is technically not a vulnerability and I’m not interested in going into details about it but, you can use this frida script to bypass it.
8. Deep Link Exploitation
In case you are not familiar with the term, Deep links are basically hyperlinks that allow users to directly open specific views inside an android application.
Before we start exploiting deep links we first need to understand how the application is processing the deep link data. I’ll start by examining the AndroidManifest.xml
file and search for android:scheme
attributes inside <data>
tags to find the deep link defined with below attributes:
- scheme:
allsafe
- host:
infosecadventures
- pathPrefix:
/congrats
You’ll also notice that the code responsible for handling the deep link in infosecadventures.allsafe.challenges.DeepLinkTask
What’s happening here is that the code first gets the data this intent is operating on, checks for a parameter named key
and compares its value to something from strings.xml
that’s also named key. The goal is to make this if
condition evaluates to true
so before we start exploiting we need to get the key which is very obvious inside strings.xml
Finally, use adb to send an intent with the proper action
and data
to trigger the code inside DeepLinkTask
adb shell am start -a "android.intent.action.VIEW" -d "allsafe://infosecadventures/congrats?key=ebfb7ff0-b2f6-41c8-bef3-4fba17be410c"
9. Insecure Broadcast Receiver
BroadcastReceiver
is an android component that listens to system-wide broadcast events or intents. Examples of these broadcasts are when your phone’s battery is running low then a broadcast indicating the low battery condition is sent. Some apps could be configured to listen for this broadcast and lower its power consumption and maybe lower the brightness on your screen, etc…
Now the challenge speaks of a Permission Re-delegation
issue that lets hackers capture notes sent via Allsafe’s broadcast receiver.
Permission Re-delegation
and I quote, Permission re-delegation occurs when an application with permissions performs a privileged task for an application without permissions.
To start examining the broadcast receiver in the app, I’ll go to AndroidManifest.xml
and look for <receiver>
tags.
You’ll notice 2 things:
- It defines an
intent-filter
withinfosecadventures.allsafe.action.PROCESS_NOTE
as the action - The code responsible for handling a broadcast when received is in
infosecadventures.allsafe.challenges.NoteReceiver
Now, whenever you want to analyze a broadcast receiver you want to start from the onReceive
function and see how it handles broadcasts it receives.
From the code above, you’ll see that it looks for 3 string extras named server
, note
and notification_message
. The first 2 extras are passed to the HttpUrl
object to build a certain url. Finally, notification_message
is being used as the content of a push notification after the note is sent.
Now, the NoteReceiver
is exported, we can directly call it using adb and that gives us control over the values of server
, note
and notification_message
so we can control what’s being displayed in the push notification and we also control the server that notes will be sent to.
To call the receiver we’ll use adb as follows
adb shell am broadcast -a "infosecadventures.allsafe.action.PROCESS_NOTE" --es server
'192.168.1.10' --es note 'Hello,World' --es notification_message 'Compromised'
-n infosecadventures.allsafe/.challenges.NoteReceiver
run that command and you’ll notice the push notification with the content we specified indicating that we indeed control the values of the 3 string extras.
10. Vulnerable WebView
The challenge first requires showing an alert dialog and to access /etc/hosts
from the filesystem.
The second part looks simple enough as it hints that the file
scheme is supported in this web view so I tried file:///etc/hosts
and it worked.
As for the first task, it was also simple. I just tried a couple of payloads until <svg onload=alert(1337) >
worked
11. Certificate Pinning
This is a basic SSL Pinning Bypass challenge. It is obvious from the code that it sends a HTTP request to https://httpbin.org/json
and uses OkHttpClient.Builder
for the pinning process which under the hood uses the TrustManager
method to implement the SSL Pinning control. The application also uses a NetworkSecurityConfig.xml
file which we can modify to make it trust user-installed certificates.
There is no need to get into too much details on this one so, i’ll just use universal-android-ssl-pinning-bypass-with-frida from codeshare to bypass ssl pinning the intercept the HTTPS traffic in Burp.
frida -U --codeshare pcipolloni/universal-android-ssl-pinning-bypass-with-frida -f infosecadventures.allsafe --no-pause
12. Weak Cryptography
The goal of this challenge is to use frida to hook some encryption methods and obtain sensitive information like Encryption key
, IV
Encryption Algorithm
, etc…
It is truly a good frida practice if you want to do it manually but to not make this blog longer than it already is, I’ll just use Intercept Android APK Crypto Operations frida script from codeshare to monitor encryption operations. I’ll spawn the app with the script, enter any text and press ENCRYPT
and watch as the script logs all the info about the encryption process that just happened. It gives you information about the Algorithm, the Secret Key, and also the plaintext.
You can also use this script if you want to hook hashing operations as well.
13. Insecure Service
In this challenge, the app has the RECOED_AUDIO
permission. If you inspect the code you’ll notice that once you click START AUDIO RECORDER SERVICE
the app starts the service from infosecadventures.allsafe.challenges.RecorderService
.
Inside RecorderService
You’ll see that it is configuring the android media recorder to record sounds for only a couple of seconds and then saves the recored in mp3
format inside the Download
directory in the sdcard
(/sdcard/Download
)
Finally, from AndroidManifest.xml
you’ll find that infosecadventures.allsafe.challenges.RecorderService
is exported so, we can directly call it to record audio without the need to open the application.
To do that just use:
adb shell am startservice infosecadventures.allsafe/.challenges.RecorderService
And you’ll see that the phone indeed started recording voice and saved it to its expected location
14. Object Serialization
For challenge No. 14, there seems to be an insecure data storage issue combined with insecure deserialization that must be exploited to give us privileges to use the LOAD USER DATA
function.
Inside the code, there is a User
object defined that takes a username, a password, and a default role of ROLE_AUTHOR
.
Once you type a user and a password in the app and click SAVE USER DATA
it uses ObjectOutputStream.writeObject
to write a serialized User
object to the App’s External Files Directory which is /sdcard/Android/data/<PackageName>/files
Finally, when you click LOAD USER DATA
it deserializes the User
object and checks if the role is set to ROLE_EDITOR
or not.
The app doesn’t check for anything else and doesn’t check the integrity of the serialized object so we can just modify the stored serialized object and replace it with the original one.
Normally I’d use SerialTweaker from redtimmy but for this small object, I can just use a basic Hex editor to do the job.
Next, I’ll replace the new file with the old one and I’ll be able to use the LOAD USER DATA
function successfully this time.