Hello I am Arsalan. Offensive Security Engineer, I blog about Cyber security, CTF writeup, Programming, Blockchain and more about tech. born and raised in indonesia, currently living in indonesia

Posts   About

Writeup Mobile Penetration Testing challenge wreckit CTF 2021

WreckIT CTF Writeup Mobile Category

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}