offensive-ctf/mobile-ctf/SKILL.md
Lab/CTF: mobile challenges; APK/AAB/IPA, Android backups, DEX/smali, SQLite/XML/keystore, Unity/IL2CPP, mobile forensics.
npx skillsauth add aeondave/malskill mobile-ctfInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Solve mobile CTF challenges by classifying the artifact type first, then choosing the narrowest extraction path before escalating to dynamic or native analysis.
.ab (Android backup), DEX file, or mobile game binary.1. Classify artifact: APK | Android backup (.ab) | IPA | Unity game
2. Quick-win attempt: strings + grep flag{ on raw artifact
3. Static analysis path per artifact type
4. If flag not found: dynamic analysis (Frida/objection) or native reversing
5. Validate and submit
Always try strings <file> | grep -i "flag{" first. Saves time on ~30% of challenges.
reversing-technique for obfuscated native .so libraries, IL2CPP dumps, or complex custom crypto.mobile-technique for dynamic instrumentation details, SSL pinning bypass, and runtime hooking.forensic-technique if the artifact is a full device image or PCAP from a mobile session.# 1. Strings grep (fast — catches plaintext flags immediately)
strings target.apk | grep -i "flag{"
# 2. Unzip and inspect structure
unzip -o target.apk -d apk_out/
find apk_out/ -type f | grep -v META-INF | sort
# Key files to check immediately:
# apk_out/classes*.dex — Java bytecode (decompile with jadx)
# apk_out/lib/ — native .so libraries
# apk_out/assets/ — bundled files (images, data, configs)
# apk_out/res/ — resources (strings.xml, layout XMLs)
# apk_out/AndroidManifest.xml — decoded manifest
# Install jadx: https://github.com/skylot/jadx/releases
jadx -d jadx_out/ target.apk
# Grep for flag and common patterns
grep -r "flag{" jadx_out/
grep -r "SecretKeySpec\|AES\|cipher\|encrypt\|decrypt\|base64\|sha\|md5" jadx_out/ | grep -v "^Binary"
# Find main activity entry point
cat jadx_out/resources/AndroidManifest.xml | grep -i "MAIN\|LAUNCHER" -B2
When strings or jadx shows SecretKeySpec, Cipher.getInstance, or a suspicious short string near crypto imports:
# Find the key and algorithm in DEX strings
strings apk_out/classes.dex | grep -E "^[A-Za-z0-9+/]{8,32}$" | head -20
# Look for: AES key (16/24/32 chars), IV (16 chars), mode string (AES/ECB/PKCS5Padding)
# Decrypt with Python once key/IV/ciphertext found
python3 - <<'EOF'
from Crypto.Cipher import AES
import base64
key = b'<key_from_apk>' # exact bytes from hardcoded string
ct = base64.b64decode('<b64_ct>') # ciphertext found in app
iv = b'\x00'*16 # ECB has no IV; CBC: extract IV from app
# AES-ECB (most common in easy CTF challenges)
cipher = AES.new(key, AES.MODE_ECB)
pt = cipher.decrypt(ct)
# Strip PKCS7 padding (last byte = pad length, 1..blocksize)
pad = pt[-1]
print(pt[:-pad] if 1 <= pad <= 16 else pt)
# AES-CBC
# cipher = AES.new(key, AES.MODE_CBC, iv=iv)
# pt = cipher.decrypt(ct)
EOF
# Check assets for unusual files (large images, non-standard files)
ls -lh apk_out/assets/
find apk_out/assets/ -type f | xargs file
# Stego on asset images
zsteg apk_out/assets/suspicious.png # LSB stego (PNG/BMP)
steghide info apk_out/assets/suspicious.jpg # JPEG stego (needs passphrase)
strings apk_out/assets/suspicious.png | grep -iE 'flag\{|ctf\{'
# Check data after EOF marker
python3 -c "
data=open('apk_out/assets/suspicious.png','rb').read()
eof=data.rfind(b'\\x89PNG')
print(repr(data[-200:])) # check tail for appended data
"
# strings.xml — often contains hardcoded values
cat jadx_out/resources/res/values/strings.xml | grep -i "key\|secret\|flag\|token\|pass"
# Google Services config — Firebase API key, project ID
cat apk_out/google-services.json 2>/dev/null
# Firebase Realtime Database (default rules often public)
# URL pattern: https://<project-id>.firebaseio.com/.json
curl "https://<project-id-from-config>.firebaseio.com/.json"
Android backups contain app data, shared storage, and sometimes sensitive files.
# Read and validate header
python3 -c "
with open('backup.ab','rb') as f: print(repr(f.read(60)))
"
# Expected: b'ANDROID BACKUP\n<version>\n<compressed>\n<encryption>\n'
# Header size = length of that ASCII block (often 24 bytes, count manually)
# Extract: header is variable length — skip 4 newline-terminated fields, then zlib-decompress
python3 - <<'PY'
import zlib, pathlib
raw = pathlib.Path('backup.ab').read_bytes()
p = 0
for _ in range(4):
p = raw.index(b'\n', p) + 1
pathlib.Path('backup.tar').write_bytes(zlib.decompress(raw[p:]))
PY
tar xf backup.tar -C extracted/
# List all non-manifest files
find extracted/ -type f | grep -v "_manifest" | sort
# 1. Grep everything for the flag
grep -r "flag{" extracted/ 2>/dev/null
# 2. Binary search (flag might be in SQLite or binary files)
python3 -c "
import os, re
for root, _, files in os.walk('extracted/'):
for f in files:
p = os.path.join(root, f)
try:
d = open(p,'rb').read()
m = re.findall(rb'(?:flag|ctf|HTB|THM)\{[^}]{1,60}\}', d)
if m: print(p, m)
except: pass
"
# 3. Visual inspection of images — check ALL images, including Photos/
# Flag may be PRINTED ON A DOCUMENT photographed by the device owner
# View each with an image viewer or multimodal LLM — "Easy leaks" style
find extracted/ -name "*.jpg" -o -name "*.png" | sort
# 4. SQLite databases
for db in $(find extracted/ -name "*.db" | grep -v shm | grep -v wal); do
python3 -c "
import sqlite3, sys
c = sqlite3.connect(sys.argv[1])
for t in c.execute(\"SELECT name FROM sqlite_master WHERE type='table'\").fetchall():
rows = c.execute(f'SELECT * FROM \"{t[0]}\" LIMIT 5').fetchall()
if rows: print(sys.argv[1], t[0], rows[:3])
" "$db" 2>/dev/null
done
# 5. SharedPreferences XML
find extracted/ -name "*.xml" | xargs grep -l "." | xargs cat 2>/dev/null
| Path | Content |
|------|---------|
| extracted/apps/<package>/sp/*.xml | SharedPreferences — app settings, tokens |
| extracted/apps/<package>/db/*.db | App databases |
| extracted/shared/0/Pictures/ | Device camera roll — may show documents |
| extracted/shared/0/DCIM/ | Camera photos |
| extracted/shared/0/Download/ | Downloaded files |
Arno-style: Unity game with libil2cpp.so. The game logic (C# code) is compiled into native ARM binary — jadx shows only Unity wrapper stubs.
# Confirm IL2CPP: look for these .so files
ls apk_out/lib/arm64-v8a/
# libil2cpp.so → compiled C# game code (all classes, methods, strings)
# libgame.so → optional additional native code
# global-metadata.dat in assets/ → type/method metadata for IL2CPP
# Extract metadata
find apk_out/assets/ -name "*.dat" -o -name "global-metadata*"
# Il2CppDumper — recovers class/method names from IL2CPP binary
git clone https://github.com/Perfare/Il2CppDumper
# Run: Il2CppDumper libil2cpp.so global-metadata.dat output/
# Produces: dump.cs (C# class stubs with all method names and field offsets)
# Search dump.cs for flag/key
grep -i "flag\|key\|secret\|password\|htb\|cheat\|unlock" output/dump.cs -i | head -20
# strings on libil2cpp.so (may expose flag directly in read-only data)
strings apk_out/lib/arm64-v8a/libil2cpp.so | grep -i "flag{\|flag\|key"
# Ghidra: load libil2cpp.so with Il2CppDumper output for guided reversing
# → reversing-technique for full binary analysis
# IPA is a ZIP
unzip -o target.ipa -d ipa_out/
find ipa_out/Payload/ -type f | sort
# Main binary: ipa_out/Payload/<AppName>.app/<AppName>
file ipa_out/Payload/AppName.app/AppName # Mach-O universal binary or ARM64
# Quick flag search
strings ipa_out/Payload/AppName.app/AppName | grep -i "flag{"
grep -r "flag{" ipa_out/ 2>/dev/null
# If App Store encrypted: frida-ios-dump or bfinject on real device
# → mobile-technique for dynamic analysis
Use when: flag is constructed at runtime, key is derived (not hardcoded), or logic is obfuscated.
# Frida — hook the decryption function and capture plaintext
# frida-tools 12+ spawns and auto-resumes; do NOT pass --no-pause (deprecated)
frida -U -f <package_name> -l hook_crypto.js
# Generic crypto hook (catches AES/DES decryption output):
# See mobile-technique for full Frida script patterns
# Objection — rapid assessment
objection -g <package_name> explore
# android sslpinning disable
# android hooking watch class com.example.CryptoClass
| Symptom | Action |
|---------|--------|
| strings finds flag{ in APK | Done — submit |
| SecretKeySpec + short string in dex | AES hardcoded key → decrypt with Python |
| Large image in assets/ | Stego → zsteg / visual inspection |
| .ab file, photos in extracted backup | View ALL photos — flag may be on photographed document |
| libil2cpp.so + global-metadata.dat | Unity/IL2CPP → Il2CppDumper |
| Firebase config in APK | Check Realtime DB public endpoint |
| Multi-dex, no obvious strings | jadx + grep → Frida if static fails |
| Native .so with JNI_ exports | reversing-technique for binary analysis |
.app/Mach-O artifacts: bundle structure, plist/keychain/SQLite extraction, Mach-O static analysis, FairPlay decryption, and Frida/objection dynamic recovery.development
Auth/lab ref: Unicorn Engine CPU-only emulation for shellcode, decryptors, custom VM handlers, instruction tracing, memory hooks, and register-level experiments.
development
Auth/lab ref: Renode board and SoC simulation for MCU/RTOS firmware, UART/GPIO/peripheral modeling, GDB remote debugging, REPL platforms, and RESC scripts.
development
Auth/lab ref: Qiling OS-layer binary emulation for PE/ELF/Mach-O/UEFI/shellcode with rootfs, syscall/API hooks, filesystem mapping, and runtime patching.
databases
Auth/lab ref: QEMU user-mode and full-system emulation for cross-arch binaries, firmware, kernels, disks, serial consoles, networking, and GDB stubs.