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
calledc
(fromi.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 ofobj3
andsb
(reverse ofobj3
)
When we open the app and we noticed that the input (obj3
) is simply four digits PIN code which can be bruteforced easily:
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 use 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.