Mobile Analysis
Difficulty:
Shown in Report
Help find who has been left out of the naughty AND nice list this Christmas. Please speak with Eve Snowshoes for more information.
Objective Image
Back
Challenge

I'm Eve Snowshoes from Team Alabaster, and I need your help with our new Android app for Santa's Naughty-Nice List - I accidentally left out a child's name in both the debug and release versions of the app. Could you first check the debug version to find which name is missing from the list, before we tackle the release version?

Solution
Silver medal

Mobile Analysis Easy - Tools Hints: Try using apktool or jadx
Mobile Analysis Easy - Missing Hints: Maybe look for what names are included and work back from that?

First, we download the apk file and unzip it in the local file system, then decompile it (using jadx as hinted).

wget https://www.holidayhackchallenge.com/2024/SantaSwipe.apk

unzip SantaSwipe.apk -d SantaSwipe
Archive:  SantaSwipe.apk
  inflating: SantaSwipe/assets/awe.otf
...

cd SantaSwipe
for i in `ls *.dex` ; do jadx /home/xy/SantaSwipe/$i -d /home/xy/SantaSwipe/${i%.dex}; done
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
INFO  - loading ...
...

If we look around the file system, we see an interesting file that sends database queries. In the function that displays the NormalList, we can also immediately see which person is omitted: Ellie.

cat classes3/sources/com/northpole/santaswipe/MainActivity.java
...
        @JavascriptInterface
        public final void getNormalList() {
...
                Cursor cursor = sQLiteDatabase.rawQuery("SELECT Item FROM NormalList WHERE Item NOT LIKE '%Ellie%'", null);
...

Nice job completing the debug version—smooth as a sleigh ride on fresh snow!
But now, the real challenge lies in the obfuscated release version. Ready to dig deeper and show Alabaster’s faction your skills?

Gold medal

Mobile Analysis Hard - Format Hints: So yeah, have you heard about this new Android app format? Want to convert it to an APK file?
Mobile Analysis Hard - Encryption and Obfuscation Hints: Obfuscated and encrypted? Hmph. Shame you can't just run strings on the file.

First of all, we download the file again. Analyzing an .aab file (Android App Bundle) is slightly different from analyzing an APK because .aab files are designed for distribution via Google Play. They contain resources and binaries for multiple device configurations. To analyze a .aab, we first need to convert it to an .apk (using bundletool as hinted).

wget https://www.holidayhackchallenge.com/2024/SantaSwipeSecure.aab
wget https://github.com/google/bundletool/releases/download/1.15.0/bundletool-all-1.15.0.jar -O bundletool.jar

java -jar bundletool.jar build-apks --bundle=SantaSwipeSecure.aab --output=SantaSwipeSecure.apks --mode=universal

unzip SantaSwipeSecure.apks -d SantaSwipeSecure
unzip SantaSwipeSecure/universal.apk -d universal

apktool d SantaSwipeSecure/universal.apk -o SantaSwipeSecure_apk
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
I: Using Apktool 2.7.0-dirty on universal.apk
...

cd universal
for i in `ls *.dex` ; do jadx /home/xy/universal/$i -d /home/xy/universal/${i%.dex}; done
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
...

The interesting files now can be found in the file system (files see below). The encryption key and IV used in the DatabaseHelper class are derived from the app's resources, specifically strings stored in R.string.ek and R.string.iv. These are retrieved in the constructor using the context.getString() method and then decoded from Base64 to byte arrays.

cat classes/sources/com/northpole/santaswipe/DatabaseHelper.java
...
        Intrinsics.checkNotNullParameter(context, "context");
        String string = context.getString(R.string.ek);
        Intrinsics.checkNotNullExpressionValue(string, "getString(...)");
        String obj = StringsKt.trim(string).toString();
        String string2 = context.getString(R.string.iv);
...
    @Override // android.database.sqlite.SQLiteOpenHelper
    public void onCreate(SQLiteDatabase db) {
        Intrinsics.checkNotNullParameter(db, "db");
        db.execSQL("CREATE TABLE IF NOT EXISTS NiceList (Item TEXT);");
        db.execSQL("CREATE TABLE IF NOT EXISTS NaughtyList (Item TEXT);");
        db.execSQL("CREATE TABLE IF NOT EXISTS NormalList (Item TEXT);");
        db.execSQL(decryptData("IVrt+9Zct4oUePZeQqFwyhBix8cSCIxtsa+lJZkMNpNFBgoHeJlwp73l2oyEh1Y6AfqnfH7gcU9Yfov6u70cUA2/OwcxVt7Ubdn0UD2kImNsclEQ9M8PpnevBX3mXlW2QnH8+Q+SC7JaMUc9CIvxB2HYQG2JujQf6skpVaPAKGxfLqDj+2UyTAVLoeUlQjc18swZVtTQO7Zwe6sTCYlrw7GpFXCAuI6Ex29gfeVIeB7pK7M4kZGy3OIaFxfTdevCoTMwkoPvJuRupA6ybp36vmLLMXaAWsrDHRUbKfE6UKvGoC9d5vqmKeIO9elASuagxjBJ"));
        insertInitialData(db);
    }
    ...
    private final String decryptData(String encryptedData) {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(2, this.secretKeySpec, new GCMParameterSpec(128, this.iv));
            byte[] doFinal = cipher.doFinal(Base64.decode(encryptedData, 0));
            Intrinsics.checkNotNull(doFinal);
            return new String(doFinal, Charsets.UTF_8);
        } catch (Exception e) {
            Log.e("DatabaseHelper", "Decryption failed: " + e.getMessage());
            return null;
        }
    }
...

cat classes/sources/com/northpole/santaswipe/R.java
...
    public static final class string {
        public static int app_name = 0x7f090001;
        public static int ek = 0x7f090033;
        public static int iv = 0x7f090037;

        private string() {
        }
    }
...

cat ../SantaSwipeSecure_apk/res/values/strings.xml
...
    <string name="ek">rmDJ1wJ7ZtKy3lkLs6X9bZ2Jvpt6jL6YWiDsXtgjkXw=</string>
    <string name="expanded">Expanded</string>
    <string name="in_progress">In progress</string>
    <string name="indeterminate">Partially checked</string>
    <string name="iv">Q2hlY2tNYXRlcml4</string>
    <string name="m3c_bottom_sheet_pane_title">Bottom Sheet</str
...

With the help of AI, we are having a Java programme created that performs the same steps as the decryptData function in DatabaseHelper.java.

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class Decryptor {
    private static final String KEY = "rmDJ1wJ7ZtKy3lkLs6X9bZ2Jvpt6jL6YWiDsXtgjkXw=";
    private static final String IV = "Q2hlY2tNYXRlcml4";

    public static String decrypt(String encryptedData, String base64Key, String base64Iv) throws Exception {
        // Decode key, IV and encrypted data from Base64
        byte[] keyBytes = Base64.getDecoder().decode(base64Key);
        byte[] ivBytes = Base64.getDecoder().decode(base64Iv);
        byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);

        // Create cipher instance and initialize
        SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, ivBytes);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec);

        // Decrypt
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }

    public static void main(String[] args) {
        if (args.length < 1) {
            System.out.println("Usage: java Decryptor <encrypted-string>");
            System.out.println("Example: java Decryptor NmfFlqJV+K1mcN9+Yp81/Vku9+A2aAwUTns96j8eMWk7TUtSxw==");
            System.exit(1);
        }

        try {
            String encrypted = args[0];
            String decrypted = decrypt(encrypted, KEY, IV);
            System.out.println("Decrypted value: " + decrypted);
        } catch (IllegalArgumentException e) {
            System.out.println("Error: Invalid Base64 input");
            e.printStackTrace();
        } catch (Exception e) {
            System.out.println("Error during decryption: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

Now we can decrypt the SQL which is executed right after setting up the database:

javac Decryptor.java
java Decryptor "IVrt+9Zct4oUePZeQqFwyhBix8cSCIxtsa+lJZkMNpNFBgoHeJlwp73l2oyEh1Y6AfqnfH7gcU9Yfov6u70cUA2/OwcxVt7Ubdn0UD2kImNsclEQ9M8PpnevBX3mXlW2QnH8+Q+SC7JaMUc9CIvxB2HYQG2JujQf6skpVaPAKGxfLqDj+2UyTAVLoeUlQjc18swZVtTQO7Zwe6sTCYlrw7GpFXCAuI6Ex29gfeVIeB7pK7M4kZGy3OIaFxfTdevCoTMwkoPvJuRupA6ybp36vmLLMXaAWsrDHRUbKfE6UKvGoC9d5vqmKeIO9elASuagxjBJ" 2> /dev/null
Decrypted value: CREATE TRIGGER DeleteIfInsertedSpecificValue
    AFTER INSERT ON NormalList
    FOR EACH ROW
    BEGIN
        DELETE FROM NormalList WHERE Item = 'KGfb0vd4u/4EWMN0bp035hRjjpMiL4NQurjgHIQHNaRaDnIYbKQ9JusGaa1aAkGEVV8=';
    END;

java Decryptor "KGfb0vd4u/4EWMN0bp035hRjjpMiL4NQurjgHIQHNaRaDnIYbKQ9JusGaa1aAkGEVV8=" 2> /dev/null
Decrypted value: Joshua, Birmingham, United Kingdom

Now we have the missing person: Joshua.

I'm delighted you succeeded - your help is keeping Alabaster's plans moving forward and you're truly proving invaluable!

Platinum medal

As an extension to the previous approach with a Java program for decryption, we want to solve the whole thing in CyberChef and with just one recipe with a maximum of two operations. We can achieve this by switching from AES GCM to AES CTR. AES-GCM (Galois/Counter Mode) provides both confidentiality and integrity by generating an authentication tag that ensures data authenticity and protects against tampering. In contrast, AES-CTR (Counter Mode) only encrypts data for confidentiality but lacks built-in integrity or authentication, leaving it vulnerable to modification without detection.
AES-GCM also requires a 12-byte IV (Initialization Vector) for optimal performance and security. AES-CTR, on the other hand, typically uses a 16-byte IV for block alignment, and if the IV is shorter, it is often padded with zeros or another deterministic method to reach the required length.
So, trying to pad the IV (we start with 00000001 hex), we reduce the operations and finally succeed:

mobile_analysis.jpg