Try   HackMD

Frida Lab: Challenge 0xA and 0xB Writeup

Challenge 0xA

For this challenge, I'll be using Genymotion as my Android emulator. Before we start reversing, let's install the app and check out what it looks like.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

It looks like there isn't much to interact with in the application; it just has a text view with "Hello Hackers" in the center of the screen.

Let's dig deeper. I'm going to use jadx to explore the inner workings of the app.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

After opening the app in jadx, I immediately focused on the native functions at the top.

public final native String stringFromJNI();

This indicates that the application is using JNI, which stands for Java Native Interface. JNI is a framework that enables Java code running in a Java Virtual Machine (JVM) to interact with native applications and libraries written in other languages like C or C++.

At the bottom, you can see that the native library is being loaded.

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

As you can see from the above snippet, the library frida0xa is being loaded. Let's dig even deeper. Using apktool, let's decompile the application and put frida0xa.so under Ghidra to see what's going on inside.

You can use the following command to decompile the application.

apktool d <name>.apk

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

After decompiling the app, you can find the library under app-name/lib/.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Now, let's open libfrida0xa.so in Ghidra.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

After opening the library, we can see two interesting functions:

  1. Java_com_ad2001_frida0xa_MainActivity_stringFromJNI: This function is called on MainActivity. On line 12, you can see the string "Hello Hackers" being set.
  2. get_flag: This function is another interesting one.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

In the function decompile window, you can see that the flag is encrypted with some sort of operation. We could take the code, reverse the operation, and get the flag. However, this is not the intended solution for the challenge. Additionally, the function takes two parameters, and the flag is only decoded if a certain if instruction evaluates to true.

if (param_1 + param_2 == 3)

We can simply pass

get_flag(1, 2);

If you look closely at the function, you'll notice that an Android log function is being called after decrypting the flag.

__android_log_print

Now that we know what we need to do, since the get_flag function is not being called, we need to call the function somehow to print the flag in the Android log. To do that, we can use Frida.

What is frida?

Frida is a dynamic instrumentation toolkit that allows developers, reverse engineers, and security researchers to inject JavaScript into running processes on Windows, macOS, Linux, iOS, and Android, enabling them to analyze and manipulate the behavior of applications and the operating system.

Frida is a powerful tool, and for this challenge, we are going to call the get_flag function using Frida.

Before solving the challenge using Frida, we need to set it up. You can check out their documentation on how to do that.

Let's see if Frida is working properly. By running the following command, we should see our target app starting and the Frida CLI.

frida -U -f "com.ad2001.frida0xa"

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Good! Let's now run the following command to get the get_flag function name.

Module.enumerateExports("libfrida0xa.so")

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Great! We got the function name, which is _Z8get_flagii. Now we can get the address of the function and use the NativeFunction function to call it.

if (Java.available) {

	Java.perform(function() {
	
	let get_flag_address = Module.findExportByName("libfrida0xa.so", "_Z8get_flagii");
	var get_flag = new NativeFunction(ptr(get_flag_address), "void", ["int", "int"]);
	
	send("calling get_flag()...");
	get_flag(1,2);

	});
}

The final code looks something like this. Let's see what the code is doing:

let get_flag_address = Module.findExportByName("libfrida0xa.so", "_Z8get_flagii");

It gets the address of the get_flag function using Module.findExportByName.

var get_flag = new NativeFunction(ptr(get_flag_address), "void", ["int", "int"]);

In this line, it's preparing a NativeFunction based on the address of get_flag. The second parameter is the return type of the function, which is void, and the last parameter is the type of the parameters we need to pass, which are both int as we've seen in Ghidra.

get_flag(1,2);

Lastly, we call the function.

Let's open logcat and see if calling the function spits out the flag for us.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Boom! As you can see, we get our flag.

Challenge 0xB

For the final challenge, let's open the app on an emulator and see what it looks like.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

The app looks simple enough, with a CLICK ME button in the center of the screen that does nothing when clicked. Let's use jadx to see what's going on.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

In this challenge, the app is also using a native library. When the button is clicked, the getFlag function is being called, but nothing seems to be happening. Let's check the log to see if the flag is being output there.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

No flag yet. Let's dig deeper. Like the previous challenge, let's decompile the app using apktool and load the library using ghidra.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Decompiling the getFlag function shows nothing. It looks like it's doing nothing. Is it not? If you check out the comments left by ghidra, you can see there is some unreachable code. Let's check out the assembly code.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Okay, good. The assembly code tells us what the function is doing, and it explains why ghidra marked the function as unreachable.

After the function prologue

MOV        dword ptr [RBP + local_2c],0xdeadbeef
CMP        dword ptr [RBP + local_2c],0x539
JNZ        LAB_001171a6

You can see the following instruction: it's comparing 0xdeadbeef == 0x539, which is always false. The rest of the code will not be executed. To understand the code more, let's enable the option in ghidra to show the unreachable code.

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

Unset Eliminate unreachable code

Pasted image 20240505170153

Now we can see the decompiled code a little clearer. If 0xdeadbeef == 0x539 equals true, we can see that the flag will be decrypted. Like the previous challenge, the flag will be logged.

What we need to do

  1. We need a way to patch the instruction so that it evaluates to true.
  2. Call the getFlag function.

Once again, we are going to use frida with radare2 or r2 for short. We are going to install a special plugin for radare2 called r2frida.

What is r2frida

r2frida is a plugin for the radare2 reverse engineering framework that enables interaction with Frida, a dynamic instrumentation toolkit.

To install the plugin, we can use the following command:

r2pm -ci r2frida

Pasted image 20240505171419

To verify if the plugin is installed, use the following command:

r2pm -l

Pasted image 20240505171509

To spawn the app, use the following command:

r2 'frida://launch/usb//com.ad2001.frida0xb'

Pasted image 20240505194443

To list all the native libraries and filter for the challenge library, you can use the following command:

:il~+libfri

Pasted image 20240505194619

To seek to the base address of the library and search for the getFlag function address, you can use the following commands in r2:

Pasted image 20240505194827

To seek to the address of getFlag and view the instructions at that address, you can use the following command in r2:

pd 25

Pasted image 20240505195043

From the above screenshot, we can see that we're at the right address.

Ways to Solve This Challenge

  1. We can patch the instruction using r2frida and trigger the function.
  2. Or, using just frida to patch in the correct address.

Patching the Instruction Using r2frida

The first thing we need to do is seek to the function we're patching, then press v.

Pasted image 20240505195636

Set the instruction we're patching to the top of the line.

Pasted image 20240505195726

Then A, type the instruction that we want to patch.

mov dword [ebp - 0x10], 0x539 ; 1337

Pasted image 20240505195823

Now, save the changes and to verify the instruction is patched, let's use the pd command to view the instruction.

Pasted image 20240505200023

Good, it's patched. Let's now open logcat and click on the button.

Pasted image 20240505200114

Awesome! As you can see, our patch worked, and when we clicked on the button, it logged the flag.

Using Frida

Let's use the following code to get the base address of the native lib. Since we've solved the challenge using r2frida, we can start our app using just Frida, attach to the process using r2frida, and check if the base address of the native lib is the same.

Module.getBaseAddress("libfrida0xb.so")

Pasted image 20240505200538

Now, let's attach to the process using r2frida and verify that we have the same address.

Pasted image 20240505201257

As we can see from the above screenshot, both are showing the same base address.

let getFlagOffset = 0x10e00;
let getFlagAddress = Module.getBaseAddress("libfrida0xb.so").add(getFlagOffset);
let patchOffset = ptr(getFlagAddress).add(0x1c);

In the following code, we get the base address of libfrida0xb.so, add the offset of the getFlag function, and finally, we add the exact offset, which is 0x1c, where the 0xdeadbeef value is being moved to the stack.

mov dword [ebp - 0x10], 0xdeadbeef

then in this location we're going to patch the instruction, changing the value from 0xdeadbeef to 0x539

Memory.protect(patchOffset, 0x100, "rwx");
let writer = new X86Writer(patchOffset);

try {

writer.putMovRegOffsetPtrU32('ebp', -0x10, 0x539);
writer.flush();

} finally {
writer.dispose();
}

Let's run our code and verify using r2frida to check if the instruction is patched or not.

Pasted image 20240505202404

The code ran without any errors. Let's verify the patch.

img4

The instruction has been patched. Let's click on the button and see what happens.

Pasted image 20240505202743 (copy)

Awesome, it looks like the patch worked!

Challenge link: https://github.com/DERE-ad2001/Frida-Labs

Reference

  1. https://frida.re/docs/home/
  2. https://www.radare.org/doc/frida/modules.html
  3. https://github.com/enovella/r2frida-wiki