Description
An Android APK hides the flag in its string resources under a suspicious key. Decompile the app and decode the base32-encoded value.
Setup
Download the APK file.
Install apktool: sudo apt install apktool
Solution
Want to try it yourself first?
The guided walkthrough reveals hints one step at a time.
Step 1Decompile the APK
ObservationThe challenge supplies an APK, and APKs store string resources as compiled binary XML that no text editor can read. apktool decodes the archive into readable XML and smali, which has to happen before searching for anything.Use apktool to decode the APK into readable smali bytecode and XML resource files. APK files are ZIP archives containing compiled DEX bytecode.bashapktool d minion.apk -o minion_decodedWhat didn't work first
Tried: Rename the APK to .zip and open it directly to browse the contents
Unzipping the APK gives raw binary XML files (e.g., res/values/strings.arsc) that are not human-readable. Binary XML must be decoded by apktool or aapt to become plain text. The unzip approach works for extracting individual files but not for reading resource values.
Tried: Use jadx instead of apktool to decompile the APK
jadx produces readable Java, which is excellent for logic analysis, but it does not always reconstruct string resource names accurately and may inline values instead of preserving the strings.xml structure. To find a resource by key name like 'Banana', apktool's decoded res/values/strings.xml is the more direct target.
Learn more
An APK (Android Package) file is a ZIP archive that contains everything needed to install an Android app: compiled bytecode in
classes.dex(Dalvik Executable format), compiled binary XML resources inres/, the app manifest (AndroidManifest.xml), and native libraries inlib/. Because it is a standard ZIP, you can rename it to.zipand browse the contents directly.apktool decodes the APK's binary XML back into human-readable XML and disassembles the DEX bytecode into smali - a human-readable assembly-like language for the Dalvik/ART virtual machine. Smali is harder to read than Java source, but it reveals every method, field, and string constant in the app. The
-oflag specifies the output directory.Android apps compiled from Java or Kotlin can also be decompiled to near-source-level Java using jadx or JADX-GUI, which produce more readable output than smali for most analysis tasks. The choice between apktool (smali) and jadx (Java) depends on the task: apktool is better for re-packaging and patching; jadx is better for reading and understanding logic.
Step 2Search string resources for the key
ObservationThe description says the flag hides under a suspicious key in the app's string resources. Once apktool has decoded the output, grep res/values/strings.xml for the key name 'Banana'.String resources live in res/values/strings.xml. Search for the key 'Banana' - it contains a base32-encoded value.bashgrep -i 'banana' minion_decoded/res/values/strings.xmlWhat didn't work first
Tried: Search the APK's classes.dex or smali files for the secret value instead of strings.xml
String resources from res/values/strings.xml compile into resources.arsc, not into DEX bytecode. Grepping smali for 'Banana' returns nothing, because smali references the resource by integer ID rather than by name. The value only appears in strings.xml after apktool decodes it.
Tried: Run grep directly on the original minion.apk instead of the decoded output directory
The APK holds a compiled resources.arsc where every string sits in binary form, not plain XML. Grepping the raw APK may surface the odd plaintext fragment but misses most values and cannot be relied on. Decode with apktool first.
Learn more
Android string resources are defined in
res/values/strings.xmlas key-value pairs:<string name="key">value</string>. These are intended for UI text and localization, but developers sometimes store hardcoded credentials, API keys, or other sensitive values here under innocuous-sounding names. After apktool decoding, these files are plain text XML, readable with any text editor or grep.Secrets in string resources are a top finding in mobile app security audits. Unlike Java class constants which require decompilation, string resources are trivially accessible to anyone who extracts the APK - no reverse engineering skill is needed. This is documented by OWASP as M1: Improper Credential Usage in the OWASP Mobile Application Security Verification Standard (MASVS).
A comprehensive mobile app review searches not just
strings.xmlbut also other resource files, raw asset files (assets/), and native libraries for hardcoded secrets. Tools like MobSF (Mobile Security Framework) automate this scan and produce a full security report from an APK file.Step 3Decode the base32 value
ObservationThe value under the 'Banana' key is all uppercase letters and the digits 2 through 7, with '=' padding. That character set is the fingerprint of base32, so base32 -d recovers the flag.Copy the base32 string from the XML and decode it. The result is the flag.bashecho '<BASE32_VALUE>' | base32 -dpythonpython3 -c "import base64; print(base64.b32decode('<BASE32_VALUE>').decode())"What didn't work first
Tried: Pipe the value through base64 -d instead of base32 -d
Base64 draws on 64 characters, A-Z, a-z, 0-9, plus and slash, while base32 uses only 32: A-Z and 2-7. Feed a base32 string to base64 -d and you get garbage or an error, because the alphabets do not match. Uppercase-only with digits 2 through 7 is the base32 fingerprint.
Tried: Use python3 -c "import base64; print(base64.b64decode(...))" treating the value as base64
b64decode either returns wrong bytes silently or raises a binascii.Error, because the two block structures are incompatible: base32 packs 5 bytes into 8 characters where base64 packs 3 into 4, so group sizes and padding do not line up. The characters A-Z and 2-7 are mostly valid base64, which is exactly why the failure is quiet. Call base64.b32decode() instead.
Learn more
Base32 is an encoding scheme that represents binary data using only 32 printable characters: A-Z and 2-7 (RFC 4648). Like base64, it is not encryption - it is purely a representation that avoids characters that might be problematic in certain contexts (no lowercase, no special characters except
=padding). Base32 produces longer output than base64 (every 5 bytes become 8 characters vs. base64's 4 per 3 bytes) but is safer for case-insensitive systems.The
base32 -dcommand decodes a base32-encoded string from stdin. Python'sbase64.b32decode()function does the same from within a script. Base32 is used in TOTP authentication codes (Google Authenticator), DNS labels, and some URL schemes. Recognizing it by its uppercase-letter-plus-digits character set and=padding is a useful forensics skill.When encountering an encoded string in a CTF, a quick heuristic: base64 uses A-Z, a-z, 0-9, +, /; base32 uses A-Z, 2-7; base16 (hex) uses 0-9, A-F. If the string contains only uppercase letters and digits 2-7, base32 is the first thing to try. The
filecommand on the decoded output tells you what the data actually is.
Interactive tools
- 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.
- Hex ViewerView text or raw hex bytes as a xxd-style hex dump with byte offset, hex columns, and ASCII sidebar. Highlights printable characters and null bytes.
- 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.
Alternate Solution
Once you locate the base32 value in strings.xml, use the Base64 & Base32 Decoder on this site - paste the encoded string and select Base32 to decode it instantly in your browser without any command-line tools.
Flag
Reveal flag
picoCTF{1t_w4sn7_h4rd_unr4v3l1n9_th3_m0b1l3_c0d3}
APK string resources are plaintext XML after decompilation - hiding secrets there provides no protection.