HTB | Supermarket

Machine - https://app.hackthebox.com/challenges/Supermarket

File - Supermarket.apk

Description - My supermarket list is too big and I only have $50. Can you help me get the Discount code?

Skill Learned

  • Android Reverse Engineering.

  • Reading decompiled Java code.

Enumeration

Running the application

Analyzing the Source code

We have found a class textWatcher which have an onTextChanged the function which is responsible for decrypting and checking the discount code:

  • String obj = MainActivity.this.f2075q.getText().toString(); - Retrieves the text input from a UI element (likely an EditText).

  • stringFromJNI() seems to return a Base64-encoded encrypted string.

  • A secret key is constructed from stringFromJNI2() and cipher type from stringFromJNI3().

  • The cipher is initialized in decrypt mode (mode 2).

  • if (!obj.equals(new String(cipher.doFinal(Base64.decode(stringFromJNI, 0)), "utf-8")))

    • Decrypts the encrypted string and compares it to the user input.

    • If it doesn't match, a list is filled from this.f2085c, and a variable f2076r is set to 5.0d.

    • If it matches, the list is filled from this.f2084b, and f2076r is set to 2.5d.

  • So if we get the encrypted string, we can get the discount code (maybe ?)

There is another onCreate which is responsible for all the calculations we see on the UI/ front end

Decompiling and analyzing a native library

We can see that the libsupermarket.so is being loaded

    static {
        System.loadLibrary("supermarket");
    }

We can also see the methods stringFromJNI(), stringFromJNI2(), and stringFromJNI3(), which are native method calls defined in Java, typically using JNI (Java Native Interface), and implemented in native code — usually C or C++

String stringFromJNI = mainActivity.stringFromJNI(); // Encrypted base64 string
SecretKeySpec secretKeySpec = new SecretKeySpec(mainActivity.stringFromJNI2().getBytes(), mainActivity.stringFromJNI3());
Cipher cipher = Cipher.getInstance(mainActivity.stringFromJNI3());
Method
Purpose

stringFromJNI()

Returns a Base64-encoded string — probably encrypted data (maybe the correct discount code).

stringFromJNI2()

Returns the encryption key (used to create SecretKeySpec).

stringFromJNI3()

Returns the cipher algorithm string, e.g., "AES" or "AES/CBC/PKCS5Padding" — used in Cipher.getInstance(...).

Now we need to reverse the libsupermarket.so and find all three functions.

For this purpose, we will load the libsupermarket.so in the Ghidra tool

Analyzing the libsupermarket.so

We can see the three functions

stringFromJNI()

We can see that it is doing some logic and returning the value (probably encrypted discount code)

stringFromJNI2()

Same as stringFromJNI() it is also doing some logic and math and returning the variable (probably encryption key)

stringFromJNI3()

Same as stringFromJNI() it is also doing some logic and math and returning the variable (probably Cipher encryption algorithm)

Exploiting the APK

Frida

We can use Frida to hook the functions and get what is being returned

Java.perform(function () {
  var myActivity = Java.use("com.example.supermarket.MainActivity");

  Java.choose("com.example.supermarket.MainActivity", {
    onMatch: function (instance) {
      let JNI = instance.stringFromJNI();
      let JNI2 = instance.stringFromJNI2();
      let JNI3 = instance.stringFromJNI3();

      console.log("JNI: " + JNI);
      console.log("JNI2: " + JNI2);
      console.log("JNI3: " + JNI3);
    },
    onComplete: function () {
      console.log("Search complete.");
    },
  });
});

We found encrypted text(discount code ?), key and cipher algorithm

Now, to crack this, we will use online AES decryption, and we got the flag

Last updated