ØxOPOSɆC Baby Xmas Challenge 2019 | Android Rev

ØxOPOSɆC Baby Xmas Challenge 2019 | Android Rev

The APK for the original challenge can be downloaded here.
The author's write-up can be downloaded here.

We're given an Android Package (xmas_ctfzadas.apk) along with the following instructions:

Baby pwn tu-tu-tu-tu!
- Get the first two flags ({oposec}XPTO) locally;
- For the brave ones, find a way to get the third flag
- Yup "This_is_the_flag" is your friend but not the final solution.

Unzipping the APK

An APK is actually just a ZIP file, by unzipping it we get to see the compressed files:

~ unzip app.apk

~ ls -la
...
-rw-r--r--@  1 inesmartins  staff     2444 Nov 22  2019 AndroidManifest.xml
drwxr-xr-x@  5 inesmartins  staff      160 Jun 13 20:59 META-INF
-rw-r--r--@  1 inesmartins  staff   178784 Nov 22  2019 classes.dex
-rw-r--r--@  1 inesmartins  staff  2062612 Nov 22  2019 classes2.dex
drwxr-xr-x@  3 inesmartins  staff       96 Jun 13 20:59 lib
drwxr-xr-x@ 16 inesmartins  staff      512 Jun 13 20:59 res
-rw-r--r--@  1 inesmartins  staff   225072 Nov 22  2019 resources.arsc

So, as expected we see AndroidManifest.xml, some .dex files (the source code), the resources, and also one Java Native Interface (JNI) library that contains some interesting strings:

~ strings lib/armeabi-v7a/libopoctf-jni.so

...
Java_pt_oposec_ctfzadas_CTFObject_firstFlag
Java_pt_oposec_ctfzadas_CTFObject_secondFlag
Java_pt_oposec_ctfzadas_CTFObject_thirdFlag
Java_pt_oposec_ctfzadas_MainActivity_stringFromJNI
envzadas
jump_here
jump_not_here
...
tres_out
...
This_is_not_the_flag
This_is_the_flag
...

Static analysis with Jadx

To be able to use jadx we need to transform the .dex files into a single .jar file, which we can achieve using the dex2jar script:

~ sh dex2jar-2.0/d2j-dex2jar.sh -f xmas_ctfzadas.apk

The pt.oposec.ctfzadas package contains 4 classes:

  • BuildConfig.class and R.class are automatically generated files: the first lists the project configs and the second identifies all the project assets;
  • this leaves us with only 2 interesting files: MainActivity.class and CTFObject.class.

MainActivity.class

As we can see below, on app start the MainActivity loads the opoctf-jni library, then creates a Webview that has access to an instance of the CTFObject class called ctfObj. The webview's URL depends on the Intent's open_sesame query string.

CTFObject.class

This class is quite interesting:

  1. There is a function called um that returns the output of the firstFlag JNI function:
public class CTFObject {
...
	
    public native String firstFlag();
    
    @JavascriptInterface
    public String um() {
        return firstFlag();
    }

...
}

2. Similarly, there's a function called dois that calls the secondFlag JNI function with a string param. Note that there's a 5-number PIN mentioned on this function.

public class CTFObject {
...

    public native String secondFlag(String paramString);

    @JavascriptInterface
    public String dois(String paramString) {
        Log.d("OPOSEC", "Pin have 5 numbers (String)\nString starts with \"{oposec}\"");
        Log.d(secondFlag(paramString), secondFlag(paramString));
        return secondFlag(paramString);
    }

...
}

3. Finally, there's a tres function that calls the thirdFlag function from the JNI library with a byte array that's parsed from a hex string:

public class CTFObject {
...

    public static byte[] hexStringToByteArray(String paramString) {
        int j = paramString.length();
        byte[] arrayOfByte = new byte[j / 2 + 1];
        for (int i = 0; i < j; i += 2)
            arrayOfByte[i / 2] = (byte)((Character.digit(paramString.charAt(i), 16) << 4) + Character.digit(paramString.charAt(i + 1), 16));
            arrayOfByte[arrayOfByte.length - 1] = Integer.valueOf(0).byteValue();
        return arrayOfByte;
    }
  
    public native String thirdFlag(byte[] paramArrayOfbyte);
    
    @JavascriptInterface
    public String tres(String paramString) {
        Log.d("aaa-", paramString);
        byte[] arrayOfByte = hexStringToByteArray(paramString);
        Log.d("aaa:", String.valueOf(arrayOfByte));
        return thirdFlag(arrayOfByte);
    }

...
}

Note that functions um, dois and tres are accessible on the MainActivity's webview since they're marked with the JavascriptInterface notation.

Re-Creating the Android project

After installing Android Studio we create a new Android project and then:

  • add the CTFObject class to src/main/java/pt.oposec.ctfzadas (I ended up changing the Java code to Kotlin, since I'm more familiar with it);
  • add the libopoctf-jni.so library under /src/main/jniLibs/armeabi-v7a (this path is extremely important).

Getting the first flag

The first flag is very simple, all we need is an instance of CTFObject and then we can simply print the result of calling either um or the firstFlag function directly, as shown below.

package pt.oposec.ctfzadas

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    private var ctfObject = CTFObject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        print(ctfObject.firstFlag())
    }
}

Getting the second flag

From the CTFObject class we know we need to find a 5-digit PIN code, which we can easily brute force. Knowing that the flag has the word oposec we simply run through all the options until we get the one that prints out the flag:

package pt.oposec.ctfzadas

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    
    private var ctfObject = CTFObject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        for (i in 0..9) {
            for (j in 0..9) {
                for (k in 0..9) {
                    for (l in 0..9) {
                        for (m in 0..9) {
                            val pin = "$i$j$k$l$m"
                            val res = ctfObject.secondFlag(pin)
                            if (res.contains("oposec")) {
                                print("Pin is: " + pin)
                                print("Flag is: " + res)
                            }
                        }
                    }
                }
            }
        }
        print("Could not get flag")   
    }
    ```

Useful Resources

How to include *.so library in Android Studio?
I read many threads how to add a *.so library to Android Studio, but none of them works, especially when it comes to the point of text: This does not work with the newer xxx (Android Studio, gradle...