0xL4ugh CTF 2024 - MyVault
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.encfrom app assets (stores it in the cache dir) - it uses a custom
onClickListenercalledc(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.encis 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 ofobj3andsb(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.
