Post

0xL4ugh CTF 2024 - MyVault

Description

Category: Mobile

Welcome to our secure vault !

Author : 0xtarek

Attachment:

1. Overview

We decompile the apk to get the source code:

1
$ jadx MyVault.apk

We find the main activity (./MyVault/sources/com/tarek/myvault/MainActivity.java) and analyze it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.tarek.myvault;

import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import d.m;
import i.c;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
/* loaded from: classes.dex */
public class MainActivity extends m {
    @Override // androidx.fragment.app.u, androidx.activity.k, v.g, android.app.Activity
    public final void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.activity_main);
        File file = new File(getCacheDir() + "/vault.enc");
        if (file.exists()) {
            return;
        }
        try {
            InputStream open = getAssets().open("vault.enc");
            byte[] bArr = new byte[open.available()];
            open.read(bArr);
            open.close();
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            fileOutputStream.write(bArr);
            fileOutputStream.close();
            ((Button) findViewById(R.id.btnSubmit)).setOnClickListener(new c(this, (EditText) findViewById(R.id.editTextOTP), 2));
        } catch (Exception e3) {
            throw new RuntimeException(e3);
        }
    }
}

  • it uses the file vault.enc from app assets (stores it in the cache dir)
  • it uses a custom onClickListener called c (from i.c)

Same but with ./MyVault/sources/i/c.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package i;

import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.widget.EditText;
import android.widget.Toast;
import com.tarek.myvault.MainActivity;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
/* loaded from: classes.dex */
public final class c implements View.OnClickListener {

    /* renamed from: a  reason: collision with root package name */
    public final /* synthetic */ int f2015a;

    /* renamed from: b  reason: collision with root package name */
    public final Object f2016b;

    /* renamed from: c  reason: collision with root package name */
    public final /* synthetic */ Object f2017c;

    public /* synthetic */ c(KeyEvent.Callback callback, Object obj, int i3) {
        this.f2015a = i3;
        this.f2017c = callback;
        this.f2016b = obj;
    }

    @Override // android.view.View.OnClickListener
    public final void onClick(View view) {
        String str;
        int i3 = this.f2015a;
        Object obj = this.f2017c;
        Object obj2 = this.f2016b;
        switch (i3) {
            case 0:
                ((g.c) obj2).a();
                return;
            case 1:
                c4 c4Var = (c4) obj;
                Window.Callback callback = c4Var.f2031k;
                if (callback != null && c4Var.f2032l) {
                    callback.onMenuItemSelected(0, (h.a) obj2);
                    return;
                }
                return;
            default:
                String obj3 = ((EditText) obj2).getText().toString();
                MainActivity mainActivity = (MainActivity) obj;
                mainActivity.getClass();
                try {
                    String sb = new StringBuilder(obj3).reverse().toString();
                    File file = new File(mainActivity.getCacheDir(), "vault.txt");
                    File file2 = new File(mainActivity.getCacheDir(), "vault.enc");
                    SecretKeySpec secretKeySpec = new SecretKeySpec((obj3 + sb + obj3 + sb).getBytes(), "AES");
                    Cipher cipher = Cipher.getInstance("AES");
                    cipher.init(2, secretKeySpec);
                    FileInputStream fileInputStream = new FileInputStream(file2);
                    byte[] bArr = new byte[(int) file2.length()];
                    fileInputStream.read(bArr);
                    byte[] doFinal = cipher.doFinal(bArr);
                    FileOutputStream fileOutputStream = new FileOutputStream(file);
                    fileOutputStream.write(doFinal);
                    fileInputStream.close();
                    fileOutputStream.close();
                    str = "Congrats!";
                } catch (Exception unused) {
                    str = "Incorrect OTP";
                }
                Toast.makeText(mainActivity, str, 0).show();
                return;
        }
    }

    public c(c4 c4Var) {
        this.f2015a = 1;
        this.f2017c = c4Var;
        this.f2016b = new h.a(c4Var.f2021a.getContext(), c4Var.f2028h);
    }
}
  • the file vault.enc is encrypted using AES (unknown mode) and our goal seems to decrypt it;
  • SecretKeySpec secretKeySpec = new SecretKeySpec((obj3 + sb + obj3 + sb).getBytes(), "AES"); the key is composed of obj3 and sb (reverse of obj3)

When we open the app and we noticed that the input (obj3) is simply four digits PIN code which can be bruteforced easily:

PIN Code

2. Exploit

The key space is only $10^4$, we can bruteforce it until the decryption of vault.enc succeed (i.e no errors during the decryption).

The script will be in java in order to keep the same object SecretKeySpec.

We get the vault.enc file from the app assets directory (./MyVault/resources/assets/vault.enc).

Bruteforce script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.io.File;
import java.io.FileInputStream;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class Main {
    public static void main(String[] args) throws Exception {
        File inputFile = new File("vault.enc");
        FileInputStream fileInputStream = new FileInputStream(inputFile);
        byte[] encryptedBytes = new byte[(int) inputFile.length()];
        fileInputStream.read(encryptedBytes);
        fileInputStream.close();

        for (int i = 0; i <= 9999; i++) {
            String obj3 = String.format("%04d", i);

            String sb = new StringBuilder(obj3).reverse().toString();
            String keyStr = obj3 + sb + obj3 + sb;

            byte[] keyBytes = keyStr.getBytes();

            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");

            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);

            try {
                byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
                String flag = new String(decryptedBytes, "UTF-8");
                System.out.println(flag);
            } catch (Exception ignored){}
        }
    }
}

Result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "vault": [
    {
      "id": 0,
      "name": "Mohamed Tarek",
      "content": "I hope You enjoyed with me!"
    },
    {
      "id": 1337,
      "name": "flag",
      "content": "0xL4ugh{Y0u_Ar3_FoRceR_Like_A_H0uRc3}"
    }
  ]
}

Flag: 0xL4ugh{Y0u_Ar3_FoRceR_Like_A_H0uRc3}

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