TISC 2022 Challenge 9 Walkthrough — PalindromeOS

CSIT tech blog
Published in
5 min readOct 26, 2022


Challenge 9 of TISC 2022 was PalindromeOS, an Android Exploitation challenge. The motivation behind this challenge was to give participants a peek into the mobile security work done in CSIT. It was adapted from our training materials on the “Bad Binder” vulnerability (CVE-2019-2215).

In this writeup, I will provide a detailed walkthrough of the challenge, and give my thoughts as the challenge creator.

The Challenge

Like traditional exploitation (pwn) challenges, we hosted the target kernel remotely and provided participants with a bridge binary to upload and run their payloads on the target device (Thanks to the infrastructure team for their help!). On top of that, we provided two binaries for participants to develop and test their exploits: Image and vmlinux.

Initial recon

We can run strings on the provided files, and try to grep for keywords like “palindrome” or “TISC{“ (flag format).

grep-ping for “palindrome”
grep-ping for the flag format

We see that there is a test flag (“TISC{THIS_IS_A_TEST_FLAG}”) and a string (“palindromes_secret”). If we throw vmlinux into a disassembler, we see that palindromes_secret is a symbol for the test flag.

Booting the kernel

We can boot up the kernel using the emulator included in Android Studio. To do so, we have to create an Android Virtual Device (AVD) using the AVD Manager. The configuration below is what we used during the development of the challenge.

AVD configuration

We can then boot the kernel on the virtual device with the command:

emulator -avd badbinder -kernel ./Image -no-snapshot -show-kernel

To access the shell of the device, we will use the Android Debug Bridge (ADB).

Android Debug Bridge

Next steps

In the device shell, if we run the command “uname -a”, we have:

uname -a

This is meant to hint at the fact that this particular kernel contains this vulnerability. Doing some quick research, we can find the Google Project Zero article, and we see that the vulnerability can be exploited for arbitrary kernel read/write privileges.

from Google Project Zero blog post.

We should also be able to find many PoC exploits for this vulnerability since it is quite old. Here, I will adapt the exploit from qu1ckr00t. We can compile this using ndk-build from the Android NDK (here I am using NDK r15c) and then use “adb push” to copy the binary into the device.

adb push

We then run the binary, asking it to return us an elevated shell to make sure it works. However, the exploit just hangs at some point and does not work.

Exploit hangs here

Fixing the PoC

This happens because the offsets of variables in structures critical to the exploit have been modified. The exploit makes use of two main structures: binder_thread and task_struct. Below is the challenge kernel’s definition of binder_thread.

Kernel source code

As you can see, I added some extra (unused) variables into the structure and also swapped the position of the wait_queue_head_t member. We can determine the offset using pahole.

pahole output for binder_thread

Here, we see that there are two columns of numbers after each member of the structure. The first represents the offset while the second is the size of the member. We should now be able to populate the exploit with the correct offsets:

Populated offsets

The exploit also requires the addresses of some kernel symbols in order to defeat kernel ASLR (KASLR). We can obtain these addresses from the kernel itself by reading /proc/kallsyms. For example:

/proc/kallsyms masked

However, the addresses in kallsyms are masked by default for security reasons. To overcome this, we have to set the kptr_restrict parameter to 0. We have to do this as root and fortunately we can simply run “su” on the device to get root. As root, we run:

echo 0 > /proc/sys/kernel/kptr_restrict

Now, if we read kallsyms, we get the addresses. This will allow us to populate the appropriate addresses in the exploit.

/proc/kallsyms unmasked
Populated addresses

The PoC should now work as intended, giving us a shell with elevated privileges.

Working exploit

Now, all that is left is to read the flag. Recall that the flag is associated to the symbol palindromes_secret. We can find the address of the symbol like before.

Address of palindromes_secret

We can then read the flag at the address as we perform the exploitation.

Code in exploit to read the flag
Flag obtained!

At this point, all that is left to do is to tidy up and patch the exploit so that it does not need any command line arguments to run. We can then send the exploit binary to the server and get the real flag.


At the end of TISC 2022, 3 participants managed to solve the challenge. Congratulations and well done! As the challenge creator, I hope this challenge has piqued your interest in mobile security!