Skip to main content

M1n10n'5_53cr37 picoMini by CMU-Africa Solution

A reverse engineering challenge featuring heavily obfuscated code. Decode the scrambled logic to find the hidden secret.

Published: April 2, 2026Updated: August 13, 2026

Description

An Android APK hides the flag in its string resources under a suspicious key. Decompile the app and decode the base32-encoded value.

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.

Walk me through it
  1. Step 1Decompile the APK
    Observation
    The 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.
    bash
    apktool d minion.apk -o minion_decoded
    What 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 in res/, the app manifest (AndroidManifest.xml), and native libraries in lib/. Because it is a standard ZIP, you can rename it to .zip and 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 -o flag 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.

  2. Step 2Search string resources for the key
    Observation
    The 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.
    bash
    grep -i 'banana' minion_decoded/res/values/strings.xml
    What 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.xml as 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.xml but 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.

  3. Step 3Decode the base32 value
    Observation
    The 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.
    bash
    echo '<BASE32_VALUE>' | base32 -d
    python
    python3 -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 -d command decodes a base32-encoded string from stdin. Python's base64.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 file command 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.

Key takeaway

Android APKs are ZIP archives, and the security boundary they offer is effectively zero: anyone can extract, decompile, and read every string constant, resource value, and class without special privilege. Hardcoding a secret in string resources, BuildConfig fields, or class constants is the same as publishing it, since apktool or jadx take seconds. The same holds for any distributed client binary, iOS IPAs, Electron apps, compiled game assets: a secret meant to stay on the server must never ship to the client.

Related reading

Useful tools for Reverse Engineering

Where to go next