Writeup Mobile Penetration Testing challenge wreckit CTF 2021
This my first post in 2021 kinda busy in real life also, I have to finish my thesis on time. okay on 13 June my friend and I playing WreckIT CTF, WreckIT CTF is an annual CTF Cybersecurity competition event from Politeknik Siber dan Sandi Negara, I solved all of the Mobile challenge and some user flag in the penetration testing box challenge in this post, I will cover all Mobile challenges.
Otherxide
in this challenge, we are given an android application, the apps looks like this
the app has nothing fancy, it just showing us a picture and some string, with no input or anything else so I guess we have to reverse the code. after doing dynamic analysis and reading the logcat, I found something suspicious when I open the app.
seems like the app is deleting a file, and also I can’t find the string “Hello can you see me ?” inside the app but I found a write function and delete function inside the flags.java.
so I assume that the app will write an odex file on /data/user/0/com.wreckit.otherxide/files/ then after the file is written the apps load a class called com.wreckit.otherxide.flag from the odex file, after that the file is removed by del() function. in order to reverse the odex file, we have to patch the app so it won’t delete the odex file after being written. so I decided to patch the del() function from the library file libxide.so. firstly we have to pull the lib from app data using command
./adb pull /data/data/com.wreckit.otherxide/lib/libxide.so .
I use binary ninja to patch the lib since I am a cool kid lol. there’s 3 function that looks suspicious
this is the del()
function
now we have to patch the del()
function so it won’t call the remove function instead set %eax
register to 1
or 0
since the comparison will check either eax is 0 or 1. so the app doesn’t crash after del()
being called.
now we have to change call remove
and make it look like this
now let’s save the patch and push the lib file to
/data/data/com.wreckit.otherxide/lib/libxide.so
using command
./adb push ./libxide.so /data/data/com.wreckit.otherxide/lib/libxide.so
now launch the app and check the logcat
looks like we successfully patch the lib and save the odex
file from being deleted in runtime.
now we have to reverse-engineer the odex
file. the odex file is located in
/data/data/com.wreckit.otherxide/files
now we have to pull the odex file using the command
./adb pull /data/data/com.wreckit.otherxide/files/pcrebgb .
we can rename the file to pcrebgb.jar
, I use an online decompiler to read the source code of the odex file.
according to the source code, the flag is encrypted using AES ECB. I also found the key and encrypted flag, and Hello can you see me ?
string here.
key = P4t1entisk03nTji
encrytped flag = 18u+W+Mw782qr9tQDXXUgWjPPqwFpf5nZ4naKbPzmSM=
in order to decrypt the flag, I use the same java code and modify it a little bit to make it works outside the android device. here is my solver to get the flag
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class flag {
private static byte[] key;
private static SecretKeySpec secretKey;
/* renamed from: a */
public static String m0a(String x) {
String flag;
if (x == "P4t1entisk03nTji") {
flag = m1d("18u+W+Mw782qr9tQDXXUgWjPPqwFpf5nZ4naKbPzmSM=", x);
} else {
flag = "Java protector testing!!";
}
return flag.toString();
}
public static void setKey(String myKey) {
try {
key = myKey.getBytes("UTF-8");
key = MessageDigest.getInstance("SHA-1").digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e2) {
e2.printStackTrace();
}
}
/* renamed from: d */
public static String m1d(String strToDecrypt, String secret) {
try {
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(2, secretKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
} catch (Exception e) {
System.out.println("Error while decrypting: " + e.toString());
return null;
}
}
public static void main(String args[]) {
String flag;
String x = "P4t1entisk03nTji";
flag = m1d("18u+W+Mw782qr9tQDXXUgWjPPqwFpf5nZ4naKbPzmSM=", x);
System.out.println(flag);
}
}
FLAG: WRECKIT{J4r_Lar1_5aaT_rUnT1me}
Ezhax
in this challenge we are given an android application, the apps look like this
after doing static analysis, I found that there’s a suspicious string inside the MainActivity.java
as you can see there’s some sort of string looks promising as username and password. but the password “ggezhaxtrac3!” in variable s is passed to a function
public String s = "ggezhaxtrac3!";
[..]
try {
mainActivity2.t = d.c.a.a.b(mainActivity2.s);
} catch (Exception e) {
e.printStackTrace();
}
at this point we have to understand how d.c.a.a.b() works, then I found that, there’s an AES CBC encryption implementation inside d.c.a.a.b
so we have to encrypt “ggezhaxtrac3!” with AES CBC using 99, 108, 107, 105, 110, 103, 116, 102, 112, 97, 105, 114, 122, 101, 103, 103
as the key.
here is my encryption code to get the password:
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class MyClass {
public static String a(String str) {
try{
byte[] a = {99, 108, 107, 105, 110, 103, 116, 102, 112, 97, 105, 114, 122, 101, 103, 103};
int length = str.length() / 2;
byte[] bArr = new byte[length];
for (int i = 0; i < length; i++) {
int i2 = i * 2;
bArr[i] = Integer.valueOf(str.substring(i2, i2 + 2), 16).byteValue();
}
SecretKeySpec secretKeySpec = new SecretKeySpec(a, "AES");
Cipher instance = Cipher.getInstance("AES");
instance.init(2, secretKeySpec);
return new String(instance.doFinal(bArr));
}catch(Exception err){
return null;
}
}
public static String b(String str) {
byte[] a = {99, 108, 107, 105, 110, 103, 116, 102, 112, 97, 105, 114, 122, 101, 103, 103};
try{
byte[] encoded = new SecretKeySpec(a, "AES").getEncoded();
byte[] bytes = str.getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(encoded, "AES");
Cipher instance = Cipher.getInstance("AES");
instance.init(1, secretKeySpec);
byte[] doFinal = instance.doFinal(bytes);
if (doFinal == null) {
return "";
}
StringBuffer stringBuffer = new StringBuffer(doFinal.length * 2);
for (byte b2 : doFinal) {
stringBuffer.append("0123456789ABCDEF".charAt((b2 >> 4) & 15));
stringBuffer.append("0123456789ABCDEF".charAt(b2 & 15));
}
return stringBuffer.toString();
} catch(Exception err){
return null;
}
}
public static void main(String args[]) {
String s = "ggezhaxtrac3!";
String a = b(s);
System.out.println(a);
}
}
run and we got the password
DA7B33A1E6D228E0564F6E3490411085
but this is not the flag we are looking for since the flag should be formatted as WRECKIT{}. and still, there’s no flag inside the dashboard activity
but, if we back to the MainActivity.java source there’s also some suspicious byte array
public byte[] v = {49, 53, 66, 68, 53, 65, 50, 67, 48, 56, 48, 66, 66, 48, 55, 54, 49, 52, 48, 53, 68, 52, 57, 69, 57, 49, 51, 54, 67, 48, 52, 69, 48, 68, 67, 55, 50, 65, 48, 49, 69, 68, 69, 48, 50, 57, 69, 53, 51, 52, 69, 52, 50, 69, 70, 54, 68, 66, 54, 50, 65, 50, 50, 55};
and if I try to convert the array as ASCII, I found a string that looks like our password. here is the code that I use to convert these array to ASCII
b = [49, 53, 66, 68, 53, 65, 50, 67, 48, 56, 48, 66, 66, 48, 55, 54, 49, 52, 48, 53, 68, 52, 57, 69, 57, 49, 51, 54, 67, 48, 52, 69, 48, 68, 67, 55, 50, 65, 48, 49, 69, 68, 69, 48, 50, 57, 69, 53, 51, 52, 69, 52, 50, 69, 70, 54, 68, 66, 54, 50, 65, 50, 50, 55]
x = ''
for i in b:
x += chr(i)
print x
the output:
15BD5A2C080BB0761405D49E9136C04E0DC72A01EDE029E534E42EF6DB62A227
looks like an encrypted msg, here is the code to decrypt the flag using AES CBC
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
// WRECKIT{m0bil3_eZ_Reves3}
public class MyClass {
public static String a(String str) {
byte[] a = {99, 108, 107, 105, 110, 103, 116, 102, 112, 97, 105, 114, 122, 101, 103, 103};
try{
int length = str.length() / 2;
byte[] bArr = new byte[length];
for (int i = 0; i < length; i++) {
int i2 = i * 2;
bArr[i] = Integer.valueOf(str.substring(i2, i2 + 2), 16).byteValue();
}
SecretKeySpec secretKeySpec = new SecretKeySpec(a, "AES");
Cipher instance = Cipher.getInstance("AES");
instance.init(2, secretKeySpec);
return new String(instance.doFinal(bArr));
}catch (Exception err){
return null;
}
}
public static String b(String str) {
byte[] a = {99, 108, 107, 105, 110, 103, 116, 102, 112, 97, 105, 114, 122, 101, 103, 103};
try{
byte[] encoded = new SecretKeySpec(a, "AES").getEncoded();
byte[] bytes = str.getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(encoded, "AES");
Cipher instance = Cipher.getInstance("AES");
instance.init(1, secretKeySpec);
byte[] doFinal = instance.doFinal(bytes);
if (doFinal == null) {
return "";
}
StringBuffer stringBuffer = new StringBuffer(doFinal.length * 2);
for (byte b2 : doFinal) {
stringBuffer.append("0123456789ABCDEF".charAt((b2 >> 4) & 15));
stringBuffer.append("0123456789ABCDEF".charAt(b2 & 15));
}
return stringBuffer.toString();
} catch(Exception err){
return null;
}
}
public static void main(String args[]) {
String s = "15BD5A2C080BB0761405D49E9136C04E0DC72A01EDE029E534E42EF6DB62A227";
byte[] v = {};
String a = a(s);
System.out.println(a);
}
}
FLAG: WRECKIT{m0bil3_eZ_Reves3}