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 1
Decompile the APKObservationI noticed the challenge supplied an APK file containing the flag, and APKs store string resources as compiled binary XML that cannot be read with a plain text editor, which suggested using apktool to decode the archive into human-readable XML and smali before searching for any hidden values.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 source, which is great for logic analysis, but it does not always accurately reconstruct string resource names and may inline values instead of preserving the strings.xml structure. For finding a resource by key name like 'Banana', apktool's decoded res/values/strings.xml is the more direct and reliable 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 2
Search string resources for the keyObservationI noticed the challenge description mentioned the flag was hidden under a suspicious key in the app's string resources, which suggested grepping res/values/strings.xml for the key name 'Banana' after apktool produced the decoded output.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 defined in res/values/strings.xml are compiled into resources.arsc, not embedded in DEX bytecode. Grepping smali files for 'Banana' will return nothing because the smali code references the resource by its integer ID (e.g., 0x7f0b0023), not by name. The actual value lives only in strings.xml after apktool decoding.
Tried: Run grep directly on the original minion.apk instead of the decoded output directory
The APK contains a compiled binary resources.arsc file where all strings are stored in a binary format, not plain text XML. Grepping the raw APK may occasionally surface some plaintext fragments but will miss most values and produce no reliable output. apktool's decoded output is required 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 3
Decode the base32 valueObservationI noticed the value stored under the 'Banana' key consisted entirely of uppercase letters and digits 2 through 7 with '=' padding, which is the distinctive character set of base32 encoding and suggested running base32 -d to recover 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 uses a 64-character alphabet (A-Z, a-z, 0-9, +, /) while base32 uses only 32 characters (A-Z and 2-7). A base32 string fed to base64 -d will either produce garbage binary output or an error because the character set does not match. The uppercase-only, digits-2-7 pattern is the fingerprint for base32.
Tried: Use python3 -c "import base64; print(base64.b64decode(...))" treating the value as base64
Python's b64decode will silently produce wrong binary output or raise a binascii.Error because base32 and base64 use incompatible block structures: base32 encodes 5 bytes into 8 characters while base64 encodes 3 bytes into 4 characters, so the group sizes and padding are structurally mismatched. Even though the characters in a base32 string (A-Z, 2-7) are mostly valid in base64, the decoded bytes will be meaningless garbage rather than the flag. The correct call is base64.b32decode(), not b64decode().
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.