For Insomni’hack Teaser 2024, I designed CryptoNotes, a challenging Android pwn challenge that combined native memory corruption with Android’s Intent-based IPC mechanisms. The goal was to create a realistic mobile exploitation scenario that required players to chain multiple exploitation techniques across the Android application framework and native code layers.
Challenge Concept
The concept was basic:
A security researcher stored a sensitive note in a new note-taking application.
As an attacker, you can convince the researcher to install your malicious application and start its main activity. The task was to find a way to leak the notes and capture the flag.
The challenge ran on Android API 30
system-images;android-30;google_apis_playstore;x86_64)
which provided a realistic modern Android environment with its associated security mitigations: stack canaries, ASLR, and the Android application sandbox.
Technical Design Decisions
When designing this challenge, I wanted to create something that went beyond typical Android IPC vulnerabilities. Most Android CTF challenges focus solely on logic bugs in Java/Kotlin code, misconfigurations in permissions, path traversals in Content Providers, or Intent injection vulnerabilities.
While these are valuable learning experiences, I wanted to push participants into something new which was based on native code exploitation through Android’s IPC layer.
The Note Application
The target application was a simple note-keeping app with three modes:
- Store plaintext notes
- Encrypt notes with algorithm A1 (ALG1)
- Encrypt notes with algorithm A2 (ALG2)
The flag was stored as an encrypted note using a custom native encryption algorithm implemented in C++. Notes were persisted in SharedPreferences, making them accessible only within the application’s sandbox.
The Vulnerability: Native Buffer Overflow
The core vulnerability resided in the native library (libins24.so) within the get_algo() function. This function parsed a JSON configuration string containing an encryption algorithm specification and copied it to a fixed-size stack buffer without proper bounds checking:
char algo[32]; // Fixed size buffer
char *json_algo = (char *)json_object_get_string(algo_obj);
strcpy(algo, json_algo); // Unsafe copy - buffer overflow!
The function was protected by stack canaries (-fstack-protector), meaning any overflow would need to either leak or bypass the canary. Additionally, with ASLR enabled, attackers would need to defeat address space randomization to build a working ROP chain.
The Attack Surface: Exported Activity
The application exposed an exported Activity (NoteAPIActivity) that accepted serialized Intent extras. This Activity would deserialize a CryptoConfig object from the Intent and pass it directly to the native encryption function:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getIntent().hasExtra("config")) {
String config = getIntent().getStringExtra("config");
CryptoConfig cryptoconf = gson.fromJson(config, CryptoConfig.class);
byte[] algo = cryptoconf.getAlgo();
// Pass to native code...
}
}
This design choice was deliberate: it created a controlled entry point from another application’s context into vulnerable native code. The attacker could craft malicious JSON payloads in their own application and deliver them via Intents to trigger the buffer overflow.
Exploitation Chain
The complete exploitation required players to:
- Trigger the vulnerability - Craft a malicious Intent with oversized ALGO field to overflow the stack buffer
- Leak the stack canary - Read the memory of your own “malicious application” to obtain the canary value (which is the same as victim app due to the Android fork and shared zygote initialization mechanism)
- Defeat ASLR - Read
/proc/self/mapsto leak libc base address (which is again the same as victim app due to the Android fork and shared zygote initialization mechanism) - Build a ROP chain - Construct a ROP payload to execute arbitrary commands
- Exfiltrate data - Use the
android.permission.INTERNETpermission to send SharedPreferences over the network
What made this particularly interesting was that all information needed for exploitation (canary, libc addresses) was available through /proc/self/* files, which are readable from any application context. This mirrors real-world scenarios where attackers leverage information disclosure primitives available on the platform.
Data Exfiltration via Intents
The final piece of the puzzle was data exfiltration. Since the victim application crashed after exploitation, traditional return-oriented programming couldn’t pass data back through the Intent mechanism. Instead, players had to craft a ROP chain that:
- Read the SharedPreferences file from
/data/user/0/com.inso.ins24/shared_prefs/com.inso.ins24.mynotes.xml - Used the victim’s
android.permission.INTERNETto send it over the network vianetcat
This demonstrated a realistic privilege escalation scenario: a malicious application without network permissions exploiting a privileged application to perform actions it couldn’t do directly.
Intended Learning Outcomes
CryptoNotes was designed to teach several important concepts:
- Cross-layer exploitation: Moving from high-level Java/Kotlin to low-level native code exploitation
- Android’s security model: Understanding how application sandboxing, permissions, and process isolation work (and their limitations)
- Modern exploitation techniques: Bypassing stack canaries and ASLR in a constrained environment
- Creative information disclosure: Using
/procfilesystem as an information leak oracle - Intent-based attack vectors: Weaponizing Android’s IPC mechanisms for privilege escalation
Challenge Reception
The challenge proved to be quite difficult, with only a handful of teams solving it during the CTF. The combination of Android-specific knowledge and traditional binary exploitation skills created a high barrier to entry, which was exactly the goal.
You can read Nikolaos Chalkiadakis’s detailed writeup here, which provides a comprehensive walkthrough of the exploitation process, including full exploit code.
And for sure, you can also give a try to the challenge.