cyshield 2025 - Android Part |
Analysis of the AndroidManifest
The first thing you must do when looking for anything about the app is to check the AndroidManifest.xml of the APK because it contains everything about the app. This will be our starting point. We will not find many things except MainActivity which is the really important one.
<activity
android:name="com.ctf.gtm.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>Let us look at the code of the MainActivity class.
Analysis of the MainActivity Java Code
At the beginning we notice a variable named encrypted of type String that is encoded with Base64.

We also find a method named sendFlag
This is a native method and it is not written in Java. It is written in a language like C or C++ in a library called libgtm.so. This means when the app starts, it loads the libgtm.so library which contains a function:
This will be responsible for generating or sending the flag after a certain check is satisfied.
onCreate method
The onCreate() runs when you open the screen for the first time.

There is nothing very important for our CTF in it. Just normal functions so when the user writes their name and presses the button, the app can read the value and show a result.
The check that allows sending the Flag
If we go down the code a bit we find the main check that, if satisfied, will send the FLAG in a certain way we do not yet know.

At the start of the code we find that when the user presses the button:
A log is recorded to logcat with the name
"CTF".It reads the text the user wrote from
nameInputand stores it in a variable calledusername.
We find that it uses the getDecryptionKey method to get the decryption key and saves it in the variable secretKey.
And this is the method:
If we try to get the key to decrypt, the key will be:
This key has length 16 bytes, suitable for AES-128 encryption.
We also have a decrypt method:
It decrypts a text encrypted with AES (using ECB mode and PKCS5 padding) and returns it as readable plain text.
The inputs (what the method receives):
The
base64Cipher→ the encrypted text but encoded in Base64. This is what we found at the start of the code.The
key→ the secret key which we already obtained.
What the method will do:
It decodes Base64 to get the real encrypted data.
It sets up the algorithm AES/ECB/PKCS5Padding.
It uses the given key to decrypt.
In the end it returns the original text (plaintext) after decrypting the Base64 encoded ciphertext.
So in short we have two important functions: getDecryptionKey which is responsible for getting the key, and decrypt which we use to decrypt the encrypted text using the key we obtained. From this we can get the username which is one of the conditions that the code checks before calling getFlag to get the Flag in a certain way we will learn later.
Now we can get the username using a simple Python code like this:
And the result after decryption will be:
Let us open the Android app and try to write the username we found.

We will find that it shows the message Flag Sent but!! To know how and where it sends the Flag, we need to analyze the sendFlag method.
From the code, we need to analyze the libgtm.so library. Using:
We use the apktool tool. This tool is used to decompile APK files of Android apps.
We will find this library at:
To analyze it, we need to use a tool like Ghidra which lets us analyze this library and see how the Flag is sent.

We will indeed find a function named:
Analysis of the important parts of the code
There is a fixed part of the flag shown clearly as plain text:
And it also adds random characters from the array local_74.
Here it adds 8 random letters from a set stored in
local_74.Then it closes the flag with
}.
Sending the flag via Intent
It creates a new Intent with the action
"com.ctf.FLAG_ACTION".It adds an Extra named
"flag"and puts the generated flag inside it.It calls Broadcast, and
any app or tool like Frida can capture it.
In short
The flag is partially fixed:
"cyctf{aX9tG4LkZp72MvBQeC...}".The remaining 8 random characters are generated using
rand()andsrand(time(NULL)).
The sendFlag function in native code aims to:
3. Generate the flag as a string.
4. Send it via an Android Broadcast Intent with the action "com.ctf.FLAG_ACTION".
So the CTF here depends on knowing the flag that the code generates, and we can "grab" or "read" it using Android debugging or tools like Frida.
Now we will use a Frida script (in JavaScript) whose job is to intercept (hook) the calls to sendBroadcast(Intent) on Android to print the content of the Intent and show any data named "flag" or any other extras.
In this JavaScript code:
We hook
ContextWrapper.sendBroadcast: every time the app callssendBroadcast(intent), our code runs first.It gets the Intent
Actionand prints it.It searches for any
Extrasinside the Intent and prints everything inside.If there are no Extras, it prints
no extras.After printing the information, it lets the original
sendBroadcastrun so the app sends the Intent as normal.
This code will run and we can get the Flag because the JNI code creates an Intent with the action com.ctf.FLAG_ACTION.
In short: the script above lists all extra data sent with the broadcast so we can see all extras easily.
Now we can run Frida with our script:

And we indeed obtained the Flag:
Last updated