← Back to list

HTB “Saw” Challenge: THE SAW APK

Welcome to my first detailed write-up on solving an Android application challenge from Hack The Box (HTB). The APK in question is named…

Manish Adithya (5h3nron) · 2024-09-09 09:15 · 6 claps · 6.9 min read
Open on Medium ↗

HTB “Saw” Challenge: THE SAW APK

Welcome to my first detailed write-up on solving an Android application challenge from Hack The Box (HTB). The APK in question is named SAW, and throughout this journey, we’ll delve into reverse engineering techniques to uncover the hidden flag within the app. If you’re interested in Android app security, reverse engineering, or just love a good puzzle, this post is for you.

Prerequisites

Before we dive in, make sure you’re familiar with the following tools and concepts:

  • Basic Java and C Programming: Understanding the syntax and structure will help in analyzing the code.
  • IDA : For disassembling native libraries.
  • Genymotion: An Android emulator to run the APK.
  • JADX: A decompiler to view the APK’s Java code.
  • ADB (Android Debug Bridge): To interact with the emulator and app.

Installing the app in the Genymotion Emulator using ADB commands

Go to the directory with your apk and use the command:

adb install SAW.apk

Initial Reconnaissance with JADX

Our first step is to decompile the APK using JADX, which allows us to view the app’s source code in Java. After opening the APK in JADX, navigate to the AndroidManifest.xml file. Here, you'll find the MainActivity defined, which is the entry point of the application.

<activity android:name="com.stego.saw.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

Understanding the MainActivity

In the MainActivity, the onCreate method is crucial as it initializes the app's components when launched. Here's a high-level overview of what happens in onCreate:

  1. Extra Verification: The app checks for an extra parameter named open with the value sesame. If this parameter is missing or incorrect, the app closes immediately.
  2. Dynamic Button Creation: If the extra verification passes, the app programmatically creates a button labeled “Click me…” and adds it to the window.
  3. Button Click Listener: Clicking this button triggers a method that attempts to display an overlay button.

The App Doesn’t Launch Properly

Attempting to launch the app normally results in an immediate crash or closure. This behavior suggests that the app expects certain conditions to be met upon startup.

Solution: Passing the Correct Extra Parameter

By examining the code in onCreate, we find that the app looks for an extra parameter. To provide this parameter, use the following ADB command:

adb shell am start -n com.stego.saw/.MainActivity -e open "sesame"

This command tells the Android system to start MainActivity of the com.stego.saw package and pass the extra parameter open with the value sesame.

Overcoming the First Crash: SYSTEM_ALERT_WINDOW Permission

Now that the app launches, clicking the “Click me…” button causes the app to crash again. Checking the Logcat output reveals a permission issue:

Granting the Required Permission

The app attempts to display an overlay using the SYSTEM_ALERT_WINDOW permission, which is not granted by default. To grant this permission, execute the following command:

adb shell appops set com.stego.saw SYSTEM_ALERT_WINDOW allow

This command modifies the app’s operations (app ops) to allow it to create system overlays.

The Overlay Button and the XOR Challenge

With the necessary permission granted, clicking the “Click me…” button now displays a grey overlay button on the screen. Interacting with this overlay button leads us to the next stage: a dialog titled “XOR XOR XOR” with a text input field.

What’s Happening Under the Hood?

At this point, the app prompts us to “XOR ME!” and awaits our input. This is a clue that some XOR-based verification is happening. Let’s dive back into the code to understand what’s expected.

Analyzing the Native Method

In MainActivity, there's a native method declaration:

public native String a(String str, String str2);

This method is defined in a native library loaded at runtime:

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

Dissecting the Native Library with IDA

First we need to decompile the APK using a widely-used tool APKTool.

To analyze the native method, we’ll use IDA to disassemble the libdefault.so library found within the APK's lib directory.

High-Level Overview of the Native Function

The native function performs the following operations:

  1. Input Validation: It checks if the user-provided input, when XORed with certain values, matches a predefined array.
  2. Flag Generation: If the validation passes, it constructs a file path and writes data to a file in the app’s data directory.

Reconstructing the XOR Logic

Let’s reconstruct the XOR validation logic in a simplified manner:

// Predefined byte arrays
unsigned char l[8] = {0x0A, 0x0B, 0x18, 0x0F, 0x5E, 0x31, 0x0C, 0x0F};
unsigned char m[8] = {0x6C, 0x67, 0x28, 0x6E, 0x2A, 0x58, 0x62, 0x68};

What Does This Mean?

The app expects an 8-character input where each character, when XORed with the corresponding value in array l, results in the corresponding value in array m.

Solving the XOR Puzzle

To find the correct input, we’ll reverse the XOR operation:

// To find input[i]:
input[i] = l[i] ^ m[i];

Let’s compute each character step by step.

Calculating the Correct Input

  1. First Character:
  • l[0] = 0x0A
  • m[0] = 0x6C
  • input[0] = l[0] ^ m[0] = 0x0A ^ 0x6C = 0x6
  1. Second Character:
  • l[1] = 0x0B
  • m[1] = 0x67
  • input[1] = l[1] ^ m[1] = 0x0B ^ 0x67 = 0x6C
  1. Third Character:
  • l[2] = 0x18
  • m[2] = 0x28
  • input[2] = l[2] ^ m[2] = 0x18 ^ 0x28 = 0x30
  1. Fourth Character:
  • l[3] = 0x0F
  • m[3] = 0x6E
  • input[3] = l[3] ^ m[3] = 0x0F ^ 0x6E = 0x61
  1. Fifth Character:
  • l[4] = 0x5E
  • m[4] = 0x2A
  • input[4] = l[4] ^ m[4] = 0x5E ^ 0x2A = 0x74
  1. Sixth Character:
  • l[5] = 0x31
  • m[5] = 0x58
  • input[5] = l[5] ^ m[5] = 0x31 ^ 0x58 = 0x69
  1. Seventh Character:
  • l[6] = 0x0C
  • m[6] = 0x62
  • input[6] = l[6] ^ m[6] = 0x0C ^ 0x62 = 0x6E
  1. Eighth Character:
  • l[7] = 0x0F
  • m[7] = 0x68
  • input[7] = l[7] ^ m[7] = 0x0F ^ 0x68 = 0x67

Converting Hex Values to Characters

Now, let’s convert the hexadecimal values to ASCII characters:

  1. 0x66'f'
  2. 0x6C'l'
  3. 0x30'0'
  4. 0x61'a'
  5. 0x74't'
  6. 0x69'i'
  7. 0x6E'n'
  8. 0x67'g'

Combining these characters, we get the input string:

"fl0ating"

Retrieving the Flag

With the correct input "fl0ating", enter it into the app when prompted. The native method now validates the input successfully and proceeds to write data to a file.

Understanding the File Writing Process

The app constructs a file path using its internal data directory:

this.FILE_PATH_PREFIX = getApplicationContext().getApplicationInfo().dataDir + File.separatorChar;

It appends a specific filename and writes binary data to this file.

Locating the Flag File

The file is created within the app’s private data directory, which is not directly accessible without root permissions. However, since we’re using an emulator like Genymotion, we can access this directory.

Steps to Retrieve the Flag File

We see that the file named “h” which is a .dex file. To get the flag, we can either cat the file or use jadx to open it.

Method-1

Method-2

THE FLAG IS — — — — → “HTB{SawS0DCLing}”

Conclusion

Through careful analysis and methodical problem-solving, we’ve successfully navigated the SAW APK challenge. This journey took us through:

  • Bypassing Initial Launch Checks: Using ADB to pass the required extra parameter.
  • Granting Permissions: Understanding and resolving permission issues to allow system overlays.
  • Reverse Engineering: Disassembling native code to uncover hidden logic.
  • Solving the XOR Puzzle: Applying bitwise operations to derive the correct input.
  • Accessing Protected Files: Retrieving the flag from the app’s private directory.

Key Takeaways

  • Understanding App Behavior: Always start by examining the app’s code to understand its expected behavior.
  • Leveraging ADB: ADB is a powerful tool for interacting with Android devices and can help bypass certain app restrictions.
  • Reverse Engineering Skills: Disassembling and analyzing native libraries is crucial for uncovering hidden logic.
  • Permission Management: Knowing how Android permissions work can help troubleshoot and resolve issues during app analysis.

Final Thoughts

This challenge was an excellent opportunity to apply reverse engineering techniques in an Android environment. It reinforced the importance of a systematic approach to problem-solving and the value of understanding both high-level app logic and low-level native code.

If you’re interested in exploring more challenges like this, I encourage you to dive into the world of Android reverse engineering. There’s a wealth of knowledge to be gained, and each new challenge offers unique lessons.

Thank you for joining me on this journey. Feel free to share your thoughts, questions, or alternative solutions in the comments below!


메타데이터
post_id
d4be714b0a0e
slug
htb-saw-challenge-the-saw-apk-d4be714b0a0e
url
https://medium.com/@5h3nron/htb-saw-challenge-the-saw-apk-d4be714b0a0e
canonical_url
https://medium.com/@5h3nron/htb-saw-challenge-the-saw-apk-d4be714b0a0e
author_url
https://medium.com/@5h3nron
status
ok
fetched_at
2026-07-22 21:40:45