Midnight Flag CTF 2025 - NeoPasswd2 (RE)
Challenge that makes us bypass conditions in an Android app.
Information gathering
This challenge is an Android-based one, where the objective is to gain administrator access in order to read a notification
Let’s start by launching the application on an emulator.
Let’s create an account and explore the application.
We notice that only administrators can read the messages. Our first goal is to bypass this restriction. We’re using Jadx to understand the application’s logic.
Several elements catch our attention in Main Activity :
1
2
3
4
5
6
7
Cursor cursor = getContentResolver().query(UserContract.CONTENT_URI, null, "username=?", new String[]{currentUsername}, null);
if (cursor != null && cursor.moveToFirst()) {
int adminValue = cursor.getInt(cursor.getColumnIndexOrThrow(UserContract.UserEntry.COLUMN_ADMIN));
this.isAdmin = adminValue == 1;
cursor.close();
}
To bypass the restriction, you just need to modify this value in the SQLite database.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void onClick(View view) {
if (!MainActivity.this.isAdmin) {
Snackbar.make(view, "Sorry, only an admin can read messages. :/", 0).setAnchorView(R.id.fab).show();
return;
}
if (!this.firstClick) {
String decrypted = MainActivity.this.tryDecrypt("Mszhl+UnftsTwm7Ule0V28WQMptqd8uoc4AbDSBKavw=");
if (decrypted != null) {
Snackbar.make(view, "★" + decrypted, 0).setAnchorView(R.id.fab).show();
return;
} else {
Snackbar.make(view, "Sry bro, you don't have permission to read the notification :(", 0).setAnchorView(R.id.fab).show();
return;
}
}
Snackbar.make(view, "A secret and important notification is about to arrive..", 0).setAnchorView(R.id.fab).show();
this.firstClick = false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public String tryDecrypt(String base64Data) {
try {
byte[] encrypted = Base64.decode(base64Data, 0);
if (encrypted.length > getMaxAllowedLength()) {
return null;
}
byte[] key = getObfuscatedString().getBytes("UTF-8");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(2, new SecretKeySpec(key, "AES"));
byte[] decrypted = cipher.doFinal(encrypted);
String result = new String(decrypted, "UTF-8").trim();
return result;
} catch (Exception e) {
return null;
}
}
The function getObfuscatedString is imported from the native library.
The condition encrypted.length > getMaxAllowedLength()
catches our attention. We just need to increase the size (which is currently 3) to decrypt all the messages.
Let’s apply these two solutions.
Exploitation
Let’s modify the database to grant ourselves admin privileges.
We are using Frida to modify the return value of the getMaxAllowedLength
function at runtime.
1
2
3
4
5
6
7
8
Java.perform(function () {
let MainActivity = Java.use("com.example.neopasswd2.MainActivity");
MainActivity.getMaxAllowedLength.implementation = function () {
return 1000;
};
});
Let’s try.
Solved by Raza