HTB | Angler

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

File - Angler.apk

Skill Learned

  • Learn how to trigger BATTERY_LOW events.

  • Become more experienced in hooking function arguments with Frida.

Enumeration

Running the application

application

From the application, it looks like it is a one-page app, and 100% maybe indicating battery (?)

Triggering LOW_Battery Intent

on analysing the code via jadax we can see registered event "android.intent.action.BATTERY_LOW"

Batter low intent

But to invoke this intent, we have broadcast the data along with it.

Android has intent action for broadcast receiver. BroadCast receiver will be trigger when it listen any action which registered within it.

For calling this Intent, we will use adb

PS D:\htb\Angler> adb shell am broadcast -a "android.intent.action.BATTERY_LOW" --es "Is_on" "yes"

where,

  • am: for activity manager

  • broadcast: for sending a broadcast message

  • a: for intent action

  • es: for an extra of type string.

This will trigger the intent and display the screen below

We can see a toast Look me inside (logs ?)

Let’s go to logcat and see the logs for com.example.angler and we can see I am not here, I am there (there means code ?)

Decompiling the library

On checking the code, we found the native library being loaded

    static {
        System.loadLibrary("angler");
    }
  • This is a static block, executed when the class is loaded.

  • System.loadLibrary("angler") loads a native shared library (e.g., libangler.so on Linux, angler.dll on Windows, or libangler.dylib on macOS).

  • The name passed does not include the lib prefix or file extension; the JVM handles that based on the OS.

We found the libangler.so in Resources/lib/x86_64/

We can disassemble this library file using a tool called Ghidra. We have to mind that about the architecture of the device, the app is installed when we disassemble it. Then we discover where that method is implemented.

We can see that getInfo() is calling two functions illusion and ne

illusion

In this, we can see the initial value of the flag (which is obvious since it is called first)

ne

In the below code, there is a string that was discovered before in the logcat, and there is another string which says You found the flag. These two strings are returned after strcmp. We can try to hook this up with Frida and see the copied string

Exploitation using Frida

Our next step is to figure out what the strings are being compared in the above code.

In order to achieve this, we need to hook on strcmp function, but there are two challenges here:

  • One, we need to make sure the strcmp is the actual name of the function being imported.

  • Two which is the main problem here is how we can hook into a native method that is not being declared in the Java Code.

To solve the first challenge, we can simply list the exports and the imports of the shared object.

[Android Emulator 5554::Angler ]-> Module.enumerateExports("libangler.so")

[Android Emulator 5554::Angler ]-> Module.enumerateImports("libangler.so")

but the above output is so big that I was unable to get the full content. We can use below JS for copying the content into a text file

//for Export

// Enumerate the exports of libangler.so module and print the JSON
Module.enumerateExports("libangler.so", {
  onMatch: function (exp) {
    // Directly print the export details in JSON format
    console.log(
      JSON.stringify({
        name: exp.name,
        address: exp.address.toString(),
        type: exp.type,
      })
    );
  },
  onComplete: function () {
    console.log("Enumeration Complete");
  },
});


//for Imports

// Enumerate the Import of libangler.so module and print the JSON
Module.enumerateImports("libangler.so", {
  onMatch: function (exp) {
    // Directly print the export details in JSON format
    console.log(
      JSON.stringify({
        name: exp.name,
        address: exp.address.toString(),
        type: exp.type,
      })
    );
  },
  onComplete: function () {
    console.log("Enumeration Complete");
  },
});
PS D:\htb\Angler> frida -U Angler -l .\Module_exports.js | tee output_export.json
PS D:\htb\Angler> frida -U Angler -l .\Module_imports.js | tee output_import.json

We found strcmp in Imports

{"name":"strcmp","address":"0x793e9a463820","type":"function"}

Now all that is left is to figure out a way to hook and monitor the native library methods.

Since we found the imported strcmp address in libangler.so, and the flag is being passed as the second argument (args[1]) to strcmp

Let’s write a Frida script that hooks that specific import, logs only when strcmp is called from libangler.so, and prints the second argument, which should be the flag.

// Replace this with the exact import address of strcmp in your lib
var strcmpAddr = ptr("0x793e9a463820");

Interceptor.attach(strcmpAddr, {
    onEnter: function (args) {
        try {
            var str1 = Memory.readUtf8String(args[0]);
            var str2 = Memory.readUtf8String(args[1]);

            console.log("[*] strcmp called");
            console.log("    Arg1: " + str1);
            console.log("    Arg2: " + str2);  // ← Likely the flag
        } catch (err) {
            console.log("Error reading strcmp args:", err);
        }
    }
});

Now we need to run the Frida, and then we need to trigger the broadcast receiver that will trigger getInfo(), that will trigger ne(), that will trigger strcmp.

//run Frida
PS D:\htb\Angler> frida -U Angler -l .\flag_script.js

//trigger the broadcast
PS D:\Andriod_Studio\testapp> adb shell am broadcast -a "android.intent.action.BATTERY_LOW" --es "Is_on" "yes"
Broadcasting: Intent { act=android.intent.action.BATTERY_LOW flg=0x400000 (has extras) }
Broadcast completed: result=0
PS D:\Andriod_Studio\testapp> 

And we got the flag (need to convert the hexadecimal to text)

Last updated