Android Challenge in ASCWG Finals

Hello guys, my name is Adham Makroum, I started the Android PenTest app a short time ago, so I saw this challenge as training for me and it was also because it has a variety of tasks

Let’s Start

First, After installing the app using and try to lunch it, I noticed that the app doesn’t run, so I checked the source code using jadx_GUI

open I noticed that there are a Broadcast Receiver that has an and but it’s

In the MainActivity there’s Root Detection

if is true it will make toast and exit from APK, then I checked this function

in Class, most of these methods check for a specific technique to root detection, and in if there’s any function of them is the will be return

it’s time for Frida

Java.perform(function x() {
let RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
RootBeer.isRooted.implementation = function () {
return false;
};

this script will hook function and return to root detection

the root detection was bypassed and the APK opened

All the Frida scripts that i will use in this walkthrough must include root detection script like this

Java.perform(function x() {
// Root Detection Bypass
let RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");
RootBeer.isRooted.implementation = function () {
return false;
};


// Another script
let MainActivity = Java.use("com.ctf.ascwg.MainActivity");
MainActivity.decrypt.implementation = function (algorithm, cipherText, key, iv) {
console.log('The Script Loaded');
let dec = this.decrypt(algorithm, cipherText, key, iv);
console.log('decrypted Value: ' + dec);
return dec;
};
});

there are another 2 functions in class

public static final void m28onCreate$lambda0(MainActivity this$0, View view) {
Intrinsics.checkNotNullParameter(this$0, "this$0");
byte[] bytes = "1561615050650065".getBytes(Charsets.UTF_8);
Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)");
String decrypt = this$0.decrypt("AES/CBC/PKCS5Padding", "kcjiK3pT/4QdvfqmnPel/A==", new SecretKeySpec(bytes, "AES"), new IvParameterSpec(new byte[16]));
if (((EditText) this$0._$_findCachedViewById(R.id.passEt)).getText().toString().equals(decrypt) & ((EditText) this$0._$_findCachedViewById(R.id.nameEt)).getText().toString().equals("badawy")) {
MainActivity mainActivity = this$0;
this$0.startActivity(new Intent(mainActivity, AnotherActivity.class));
this$0.finish();
Toast.makeText(mainActivity, "Welcome Badawy!", 1).show();
return;
}
Toast.makeText(this$0, "Credentials are wrong!", 1).show();
}
}

in this function, it compares the decrypted cipherText with a password that the user will enter, and compare the username with

this function is Used to decrypt the cipherText

 public final String decrypt(String algorithm, String cipherText, SecretKeySpec key, IvParameterSpec iv) {
Intrinsics.checkNotNullParameter(algorithm, "algorithm");
Intrinsics.checkNotNullParameter(cipherText, "cipherText");
Intrinsics.checkNotNullParameter(key, "key");
Intrinsics.checkNotNullParameter(iv, "iv");
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(2, key, iv);
byte[] plainText = cipher.doFinal(Base64.decode(cipherText, 0));
Intrinsics.checkNotNullExpressionValue(plainText, "plainText");
return new String(plainText, Charsets.UTF_8);
}

let’s hook this function to get the password

when clicking on the login button the decrypt function will execute

let MainActivity = Java.use("com.ctf.ascwg.MainActivity");
MainActivity.decrypt.implementation = function (algorithm, cipherText, key, iv) {
console.log('The Script Loaded');
let dec = this.decrypt(algorithm, cipherText, key, iv);
console.log('decrypted Value: ' + dec);
return dec;
};

now we have login creds

username:

password:

Clicking on the BROADCAST button

let’s check the class

public final class MyBroadcastReceiver extends BroadcastReceiver {
public final native String getKeys();

public MyBroadcastReceiver() {
System.loadLibrary("api-keys");
}

@Override // android.content.BroadcastReceiver
public void onReceive(Context context, Intent intent) {
Intrinsics.checkNotNullParameter(context, "context");
Intrinsics.checkNotNullParameter(intent, "intent");
byte[] bytes = "2561651651561651".getBytes(Charsets.UTF_8);
Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)");
if (StringsKt.equals$default(intent.getStringExtra("data"), new MainActivity().decrypt("AES/CBC/PKCS5Padding", "3KO5tETzqPHQGYdRQXJZE1x4ePh44iLL08vQJGjvXb4=", new SecretKeySpec(bytes, "AES"), new IvParameterSpec(new byte[16])), false, 2, null)) {
Toast.makeText(context, Intrinsics.stringPlus("Flag: ", getKeys()), 1).show();
return;
}
Log.i("FLAG", "Hehehehe try again!");
Toast.makeText(context, "Hehehehe try again!", 1).show();
}
}

this will check if the broadcast contains a string extra named “data” which is equal to the result of decrypting a hardcoded ciphertext using the method

if the condition is true the flag will toast a message from the getKeys() method if not will toast

now we need to run the previous Frida script to hook again like we did before

if your Frida script still running, when clicking on the broadcast button the cipher will decrypt this is that we will send it within the broadcast

now let’s check How this broadcast receiver work, in AnotherActivity Class there is an intent

// AnotherActivity    
public static final void m26onCreate$lambda0(AnotherActivity this$0, View view) {
Intrinsics.checkNotNullParameter(this$0, "this$0");
Intent intent = new Intent();
intent.setAction("com.ctf.MyBroadcastReceiver");
intent.setFlags(32);
intent.putExtra("data", "Nothing to see here, move along.");
this$0.sendBroadcast(intent);
}

it takes as an action, so let’s try to send a broadcast using adb

This is an incomplete flag

Another Solution Using frida: Hook getKeys() function

using to get PID to the APK

this is our script

Java.choose("com.ctf.ascwg.MyBroadcastReceiver", {    
"onMatch":function(instance){
console.log("[*] Matched !!")
console.log(instance.getKeys());
},
"onComplete":function() {
console.log("[*] Finished")
}
});

Now we will try to figure out how to get the rest of the flag

in the Class, there are some functions let’s check it

this will retrieve the object with the key , so the Flag may be in shared pref

Another Solution Using frida: Hook getKeys() function

let AnotherActivity = Java.use("com.ctf.ascwg.AnotherActivity");  
AnotherActivity.getKeys.implementation = function () {
console.log('The Script Loaded');
let ret = this.getKeys();
console.log('Flag: ' + ret);
return ret;
};

you can use also like I did before

and finally, there’s the rest of the flag

Flag:

It’s great APKto training, I recommended it for beginners like me

In the end, If there’s any step wrong Feel free to ping me, Don’t forget to follow me on medium and Twitter

Thank you for reading.

--

--

infosec guy who seeks knowledge.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store