Description
Can you handle APKs? Download the android apk here.
Setup
Download mobpsycho.apk and unzip it in a separate directory (APK unzip dumps lots of files).
wget https://artifacts.picoctf.net/c_titan/53/mobpsycho.apk && \
mkdir mobpsycho_dir && cd mobpsycho_dir && \
unzip ../mobpsycho.apkSolution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
xxd -r -p reverse-hex pattern at the heart of this solve, plus the recognition cues for hex-encoded strings hidden inside other formats.Step 1
Find the flag fileObservationI noticed the challenge provided an APK file and Android apps store raw resources (text, config, and asset files) inside the res/ directory of their ZIP structure, which suggested scanning that directory with find and strings to locate any file referencing the flag.Recursively scan extracted files with find and strings, scoped to res/ for speed. The flag lives at res/color/flag.txt.bashfind res -type f -exec strings {} + | grep flagbashfind . -type f -name 'flag*'What didn't work first
Tried: Run strings on the APK file directly before unzipping it
strings on the raw .apk will emit a flood of ZIP metadata and DEX bytecode fragments; the flag string is buried or absent because ZIP compression means the file bytes are not plain ASCII. Unzipping first lets strings operate on the uncompressed resource files where the flag is stored as a plain text hex string.
Tried: Search only the top-level directory with grep -r . instead of scoping to res/
grep -r . over the entire extracted tree also processes classes.dex, which is large binary Dalvik bytecode. The grep will still find the flag eventually, but it can take far longer and may output thousands of false-positive binary matches. Scoping to res/ (or using find with -exec strings) targets the resource layer where Android apps store raw text files.
Learn more
An APK (Android Package Kit) is simply a ZIP archive containing everything an Android app needs: compiled Dalvik bytecode (
classes.dex), resources, assets, a manifest, and native libraries. Because it is a standard ZIP, any tool that can unzip an archive can explore its contents without needing a real Android device.find ... -exec strings {} +appliesstringsto every regular file under the path and pipes the union intogrep. It's the safer alternative to a top-levelstrings *: that glob skips dotfiles, expands at most one level, and chokes on directories. Scoping tores/avoids dumpingclasses.dexthroughstringsjust to look for a filename you already suspect lives in resources.- The
res/directory holds Android XML resources, drawables, and raw files, a common hiding spot for CTF secrets. - The
assets/directory is another frequent location for embedded files that are not compiled into the DEX. - For deeper analysis, tools like jadx or apktool decompile DEX bytecode back to readable Java/Smali.
- The
Step 2
Decode the hexObservationI noticed that flag.txt contained a continuous string of [0-9a-f] character pairs with no address offsets or spaces, which are the hallmarks of a plain hex dump and suggested using xxd -r -p to convert the hex digits back to readable ASCII.flag.txt contains hex; pipe it through xxd -r -p (or CyberChef's From Hex) to recover the ASCII flag.bashxxd -r -p res/color/flag.txtExpected output
picoCTF{ax8mC0RU6ve_NX85l4ax8mCl_a3e...}What didn't work first
Tried: Run xxd -r res/color/flag.txt without the -p flag
xxd -r without -p expects the standard xxd address-offset format (e.g. '00000000: 7069 636f ...'). flag.txt contains a continuous hex string with no offsets, so xxd -r produces garbled or empty output. The -p flag tells xxd the input is plain hex with no column structure.
Tried: Open flag.txt in a text editor and read it as the flag
The raw contents of flag.txt look like a long string of hex digits (e.g. 7069636f435446...) which is not a valid picoCTF flag. The actual flag is the ASCII result of decoding those hex pairs - each pair encodes one character. Submitting the raw hex string will be rejected.
Learn more
Hiding data as hexadecimal is a simple obfuscation technique: each byte of the original string is represented as two hex digits (e.g., the letter 'p' becomes
70). The value is human-unreadable at a glance but trivially reversible.xxd -r -preverses a plain hex dump back to binary. The-rflag means "reverse" (hex to binary) and-pmeans the input is in plain/continuous hex format without address offsets. This combination is the standard Linux one-liner for hex decoding.CyberChef's From Hex recipe performs the same operation visually, making it useful when you want to see intermediate steps or chain multiple decodings (e.g., hex then base64 then ROT13). Real malware samples often layer encodings precisely to slow down analysts.
The decoded output is pure ASCII printable text from
pthrough}: that's the entire flag, copy it as-is. If you see\0or other null bytes inside the output, that's a sign the input hex had odd-length bytes, stray spaces, or trailing whitespace thatxxd -r -pmishandled, not part of the flag itself.
Interactive tools
- File Magic IdentifierIdentify file types from magic numbers. Paste hex bytes or drop a file to detect PNG, JPEG, ZIP, PDF, ELF, PCAP, SQLite, and dozens of other formats.
- Strings ExtractorPull printable text from any binary, library, or image. ASCII and UTF-16 detection, configurable minimum length, flag-like highlight, no command line needed.
Flag
Reveal flag
picoCTF{ax8mC0RU6ve_NX85l4ax8mCl_a3e...}
The decoded hex string inside res/color/flag.txt is the flag.