Post

Midnight Flag CTF 2025 - NeoPasswd2 (RE)

Challenge that makes us bypass conditions in an Android app.

Midnight Flag CTF 2025 - NeoPasswd2 (RE)

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.

loginpage

Let’s create an account and explore the application.

alt text

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.

alt text

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.

alt text

Solved by Raza

This post is licensed under CC BY 4.0 by the author.

Trending Tags