CTF Challenge Description:

Hi there,

thanks for testing my new notes app. I know it’s not done yet, but the military-grade encryption is implemented, so let’s see if you can read my note.


If you install the app on your phone, the login screen looks just like below

Cyber Security Study Notes

OSCP Study Notes

Since this is an Android reverse engineering challenge, we use JEB decompiler to reverse and analyze the app. Below is full source code:

package com.example.firstjavaapplication;

/* ... */

public class MainActivity extends AppCompatActivity {
    private Button buttonLogin;
    private EditText editTextPassword;
    private EditText editTextUsername;

    @Override  // androidx.fragment.app.FragmentActivity
    protected void onCreate(Bundle bundle0) {
        this.editTextUsername = (EditText)this.findViewById(id.editTextUsername);
        this.editTextPassword = (EditText)this.findViewById(id.editTextPassword);
        Button button0 = (Button)this.findViewById(id.buttonLogin);
        this.buttonLogin = button0;
        button0.setOnClickListener(new View.OnClickListener() {
            private boolean isValidCredentials(String s, String s1) {
                return (s.equals("admin")) && (s1.equals("fbG6pXd0wm1cz"));

            @Override  // android.view.View$OnClickListener
            public void onClick(View view0) {
                String s = MainActivity.this.editTextUsername.getText().toString().trim();
                String s1 = MainActivity.this.editTextPassword.getText().toString().trim();
                if(!s.isEmpty() && !s1.isEmpty()) {
                    if(this.isValidCredentials(s, s1)) {
                        Intent intent0 = new Intent(MainActivity.this, Part1.class);
                        intent0.putExtra("password", s1);

                    Toast.makeText(MainActivity.this, "Wrong Username or Password", 0).show();

                Toast.makeText(MainActivity.this, "Please enter username and password", 0).show();

The initial section of the wooden container contained a keypad and a wooden trapdoor that was closed. The provided code below outlines the authentication mechanism employed by the keypad in order to validate the user’s input by comparing it to the anticipated outcome.


uint64_t validate_code()

      int64_t var_38
      __builtin_strcpy(dest: &var_38, src: ":1N50QU357")
      int64_t var_8 = 0x19
      void var_140
      void* var_10 = &var_140
      strcpy(var_10, &code, 0x33555130354e313a)
      strcat(var_10, &var_38)
      size_t var_18 = 0x10
      int64_t var_20 = 0xf
      md5String(var_10, &var_140 - 0x10)
      int128_t var_a0
      __builtin_strncpy(dest: &var_a0, src: "33c12ea236cc81d7deb97e432aedc9d4", n: 0x21)
      void* x0_6
      int64_t x1_2
      x0_6, x1_2 = hexstr_to_char(&var_a0)
      void var_78
      fastpbkdf2_hmac_sha512(&var_140 - 0x10, var_18, x0_6, x1_2, 0x64, &var_78, 0x40)
      void* x0_9
      int64_t x1_5
      x0_9, x1_5 = hexstr_to_char("6d47dd806bdf0677abfdca674206650a…")
      if (x1_5 == 0x40)
          int32_t x0_12 = memcmp(&var_78, x0_9, 0x40)
          int64_t var_c0_1 = 0
          int64_t var_b0_1 = 0
          return zx.q(x0_12)
      __assert_fail(assertion: "expected.length == PKBDF2_OUT_SI…", file: "firmware.c", line: 0x8d, function: "validate_code")

If you notice com.example.firstjavaapplication.MainActivity you will find that the correct credentials as admin and fbG6pXd0wm1cz which can be used to login as admin.

In the code,  MainActivity sends the password fbG6pXd0wm1cz that is never being used besides methods such as generateFlag and MD5 which are also not used at all.

if we display the content of storage.json we find the below:

└──> cat contents/assets/storage.json 
  "AES-Type": "256",
  "note": "uS0D11dq3RM9QimRWfXcewwQdoxYwrZRNUGT205pDfQ="

This makes us re-examine this part of the code below:

@Override  // android.view.View$OnClickListener
public void onClick(View view0) {
	String s = Part1.this.editTextPassphrase.getText().toString().trim();
    if(!s.isEmpty()) {
        try {
            ((TextView)Part1.this.findViewById(id.textViewFLAG2)).setText(this.decrypt(s1, s));
        catch(Exception exception0) {
            Toast.makeText(Part1.this, "Wrong passphrase", 0).show();


    Toast.makeText(Part1.this, "Please enter a password", 0).show();

IIt calls decrypt using the note uS0D11dq3RM9QimRWfXcewwQdoxYwrZRNUGT205pDfQ= and the passphrase we enter. If we prevent exceptions from being raised in the code, we can assume that the note is successfully decrypted

Note gets decoded using Base64 and then it gets decrypted 313370+1 times using AES-256. NoPadding option is also used to avoid throwing exceptions during the multiple decryptions.

At the end, we use PKCS7Padding to make sure decryption is correct.

To find the correct passphrase, we conduct a password dictionary attack using rockyou.txt.

The passphrase that we try must be of the “correct size” to avoid any error such as  Unsupported key size: 4 bytes exception.

Since we know that the encryption algorithm is AES-256 , we can safely deduce that the correct size is 32 characters long.

The next step is to filter out all passwords from the rockyou.txt wordlist which are not 32 bytes long.

So we proceed with this approach and get the correct passphrase (and the flag):


CTF Walkthrough Playlist

About the Author

I create cybersecurity notes, digital marketing notes and online courses. I also provide digital marketing consulting including but not limited to SEO, Google & Meta ads and CRM administration.

View Articles