关于 apk-redteam-pipeline
这项 Android 应用红队技能将 APK 获取、静态检查和运行时测试组织成分阶段流程,面向授权范围内的移动应用评估,覆盖应用清点、API 与组件分析。
主要功能
- 01组织名下应用清点
- 02APK 与拆分 XAPK 获取
- 03DEX 反编译与密钥搜索
- 04证书和导出组件检查
- 05Frida 插桩与流量捕获
- 06Firebase 配置审查
适用场景
- 01评估授权范围内的 Android 应用
- 02从 APK 中查找 API 主机与凭据
- 03检查导出组件和应用配置
- 04绕过证书固定后检查运行时请求
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill apk-redteam-pipeline安装后在 Claude Code 中输入/apk-redteam-pipeline确认已加载。
详细介绍
使用此技能的时机
在以下情况触发:
- Recon 发现目标开发者名下有 1 个或多个移动应用(Play Store 开发者页面)
- Web 应用直接托管
*.apk文件(例如某次测试期间在子域名上发现Recruitz.apk) - 信息窃取程序日志泄露了 APK 包 ID(例如窃取程序转储格式中出现
com.<brand>.app、com.<brand>.<sub-brand>模式) - 客户端应用、经销商/合作伙伴门户或员工移动端配套应用在测试范围内
- 漏洞赏金计划将 Android 列入测试范围
请勿用于:
- 仅限 iOS 的目标(使用不同流程——IPA 逆向、MobSF、frida-ios-dump)
- 已通过 JS bundle 分析覆盖的 React Native/Flutter Web 应用
- 仅限服务端的评估
阶段 0 — 清点所有组织自有应用
抓取 Play Store 开发者页面
# Find developer page from the target's brand name
curl --max-time 30 --connect-timeout 10 -sk -A "Mozilla/5.0" "https://play.google.com/store/apps/developer?id=<Brand+Name>" -o /tmp/dev.html
# Extract package IDs
grep -oE 'id=[a-zA-Z0-9._]+' /tmp/dev.html | sort -u
示例输出(已匿名处理——多品牌企业通常有 7 个包):
com.events.<brand>build
com.<corp>.<sub-brand-1>
com.<corp>.<sub-brand-2>
com.<corp>.<flagship>
com.<corp>.<product-line-1>
com.<corp>.<product-line-2>
com.<corp>.<sub-brand-3>
与信息窃取程序日志交叉核对
信息窃取程序日志格式包含类似 *@com.<corp>.<app> 的包名——如果你有泄露的转储文件,可从 creds_userpass.txt 中提取这些信息。
品牌排列组合猜测(多品牌企业模式)
com.<brand>.app
com.<brand>.mobile
com.<brand>.android
com.<brand>connect.app
in.<brand>.dealer
in.co.<brand>.app
阶段 1 — 获取 APK
首选:直接从 APKPure 获取(无需身份验证)
# Follow 302 redirects to actual download
curl -sk -L --max-time 60 --connect-timeout 10 \
"https://d.apkpure.net/b/APK/<package_id>?version=latest" \
-o "<package_id>.apk"
# Or via the legacy d-XX.app-mirror.example.com mirror chain (we saw this work)
次选:在 APKMirror 搜索
curl --max-time 30 --connect-timeout 10 -sk -A "Mozilla/5.0" "https://www.apkmirror.com/?post_type=app_release&searchtype=apk&s=<brand>" \
| grep -oE 'href="[^"]+\.apk[^"]*"' | sort -u
备选:在 APKPure 网页搜索
curl --max-time 30 --connect-timeout 10 -sk "https://apkpure.com/search?q=<brand>" | grep -oE 'data-dt-app="[^"]+"'
XAPK 与 APK
.xapk= 包含多个拆分 APK 的 zip 文件(base + config.armeabi-v7a + config.en + 等)- 先解压外层文件,再解压内层的
base.apk或<package>.apk - 部分 apkpure 下载会返回缺少 EOCD 签名的截断 XAPK——这是 CDN 限流的表现;更换 IP 后重试,或使用
7z x,它比unzip更宽容
# Standard unzip (works for clean APK)
unzip -o <package>.apk -d extracted_<package>/
# For truncated/repaired XAPK
7z x -y <package>.apk -o"extracted_<package>"
# For nested XAPK
for inner in extracted_<package>/*.apk; do
mkdir -p "extracted_<package>/$(basename "$inner" .apk)"
unzip -o "$inner" -d "extracted_<package>/$(basename "$inner" .apk)"
done
阶段 2 — DEX 反编译(jadx)
# Install
brew install jadx # macOS
# or
wget https://github.com/skylot/jadx/releases/latest/download/jadx-1.5.x.zip
# Decompile
jadx -d decompiled_<package>/ <package>.apk
# For XAPK that contains multiple APKs
for inner in extracted_<package>/*.apk; do
jadx -d decompiled_<package>_$(basename "$inner" .apk)/ "$inner"
done
如需快速扫描“仅字符串”而不进行完整反编译:
find extracted_<package> -name "classes*.dex" -exec strings -8 {} \; > strings_<package>.txt
阶段 3 — 搜索密钥(60 种模式目录)
# URL grep — owned-domain references
grep -oE 'https?://[a-zA-Z0-9.-]+\.(target1|target2|target3)\.(com|io|net|in)[a-zA-Z0-9./_?=&%-]*' strings_<package>.txt | sort -u
# Internal IP / port URLs
grep -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
# Cloud credentials
grep -oE 'AKIA[A-Z0-9]{16}' # AWS Access Key
grep -oE 'aws_secret_access_key[\s:=]+[A-Za-z0-9/+=]{40}' # AWS Secret
grep -oE 'AIza[A-Za-z0-9_-]{35}' # Google API key
grep -oE 'ya29\.[A-Za-z0-9_-]+' # Google OAuth refresh token
grep -oE 'gh[ps]_[A-Za-z0-9]{36}' # GitHub PAT
grep -oE 'glpat-[A-Za-z0-9_-]{20}' # GitLab PAT
grep -oE 'xox[pbar]-[A-Za-z0-9-]+' # Slack token
grep -oE 'sk-[A-Za-z0-9]{48}' # OpenAI API key
grep -oE 'sk-ant-[A-Za-z0-9_-]{90,}' # Anthropic API key
grep -oE 'AC[a-f0-9]{32}' # Twilio Account SID
grep -oE 'sk_live_[A-Za-z0-9]{24}' # Stripe live key
grep -oE 'pk_live_[A-Za-z0-9]{24}' # Stripe publishable
grep -oE 'mailgun-[A-Za-z0-9-]{40}' # Mailgun
grep -oE 'SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}' # SendGrid
# JWT (any algorithm)
grep -oE 'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*' strings_<package>.txt
# Firebase
grep -oE '"(api_key|project_id|database_url|storage_bucket|client_id|mobilesdk_app_id|google_app_id|gcm_defaultSenderId)"\s*:\s*"[^"]+"' \
extracted_<package>/res/values/strings.xml \
extracted_<package>/google-services.json \
decompiled_<package>/resources/AndroidManifest.xml 2>/dev/null
# OAuth client secrets
grep -oE 'client_secret["\s:=]+[A-Za-z0-9_-]{24,}' strings_<package>.txt
# Hardcoded passwords (heuristic — many false positives, manual review)
grep -oE '"password"\s*:\s*"[^"]+"|password\s*=\s*"[^"]+"' decompiled_<package>/sources/**/*.java 2>/dev/null
真实案例发现(已匿名处理——来自一次经授权的测试)
# Customer-facing APK shipped a hardcoded URL of this shape:
https://api.<client>.example/<path-token>/<resource-token>?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.<payload>.<sig>
# Decoded JWT payload: {"sid":<int>,"iat":<unix-ts>,"exp":<unix-ts>}
# Expired ~8 years earlier — but path tokens + 30 /v1/* endpoints still useful intel
阶段 4 — 提取固定证书
# Find .cer / .der / .pem files in assets/
find extracted_<package>/assets -iname "*.cer" -o -iname "*.der" -o -iname "*.pem" -o -iname "*.crt" 2>/dev/null
# Or in network_security_config.xml
find extracted_<package> -name "network_security_config.xml" -exec cat {} \;
# For each cert, extract subject + SAN (might reveal new internal API hosts)
for cert in $(find extracted_<package>/assets -iname "*.cer"); do
echo "=== $cert ==="
openssl x509 -in "$cert" -noout -subject -issuer -dates 2>/dev/null
openssl x509 -in "$cert" -noout -text 2>/dev/null | grep -E 'Subject:|DNS:|Issuer:|Validity'
done
真实案例
某次经授权测试中的一款面向客户的 APK 包含 assets/api_<service>_<domain>_com.cer,揭示了 api.<service>.<domain>.example 资产的存在,而被动侦察此前并未发现该资产。
阶段 5 — 枚举导出组件
AndroidManifest.xml 会列出组件。导出的组件(尤其是带有 android:exported="true" 或通过 intent-filter 隐式导出的组件)可由其他应用触发,可能构成 Intent 注入攻击面。
# Decode binary AndroidManifest if needed
apktool d <package>.apk -o decoded_<package>/ # apktool decodes binary manifest
# Or read directly from jadx output
cat decompiled_<package>/resources/AndroidManifest.xml | grep -E '<(activity|service|receiver|provider)' | head -50
# Filter exported
grep -E 'android:exported="true"' decompiled_<package>/resources/AndroidManifest.xml
针对每个导出的组件,检查:
- 它是否接受会流入 WebView 的 extras(intent → WebView → XSS / file://)
- 它是否接受 URI extras(可能通过深层链接造成 SSRF)
- 它是否将 extras 传递给其他 Activities(Intent 重定向)
阶段 6 — 检查 Firebase/云服务配置
# google-services.json — full Firebase config
find extracted_<package> -name "google-services.json" -exec cat {} \; | python3 -m json.tool
# Look for:
# project_id → can guess Firestore / RTDB URL: https://<project_id>.firebaseio.com/.json
# storage_bucket → can guess GCS bucket: gs://<bucket>
# web_api_key → can use to enumerate Firebase tenant config
# Test if Firestore is publicly readable
curl --max-time 30 --connect-timeout 10 -s "https://firestore.googleapis.com/v1/projects/<project_id>/databases/(default)/documents/users"
# Test if Realtime DB is publicly readable
curl --max-time 30 --connect-timeout 10 -s "https://<project_id>.firebaseio.com/.json"
# Test if Storage Bucket is publicly listable
curl --max-time 30 --connect-timeout 10 -s "https://firebasestorage.googleapis.com/v0/b/<bucket>/o"
阶段 7 — 运行时插桩(Frida)
当静态分析不够时使用——你可能需要在运行时转储令牌、绕过证书固定,或跟踪 API 调用。
设置
pip install --break-system-packages frida-tools objection
adb devices # ensure device/emulator connected
# Push frida-server to device (root required, or use rooted emulator like Genymotion / x86_64 AVD)
绕过证书固定(通用方法)
// frida-script-pinning-bypass.js
Java.perform(function() {
// OkHttp
try {
var CertificatePinner = Java.use('okhttp3.CertificatePinner');
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function() {
console.log('[+] OkHttp pinning bypassed for: ' + arguments[0]);
return;
};
} catch (e) {}
// HttpsURLConnection
try {
var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
TrustManagerImpl.verifyChain.implementation = function(chain) {
console.log('[+] TrustManagerImpl verifyChain bypassed');
return chain;
};
} catch (e) {}
});
frida -U -l frida-script-pinning-bypass.js -f <package_id> --no-pause
Hook HTTP 请求
Java.perform(function() {
var OkHttpClient = Java.use('okhttp3.OkHttpClient');
var Request = Java.use('okhttp3.Request');
var Call = Java.use('okhttp3.Call');
OkHttpClient.newCall.implementation = function(req) {
var url = req.url().toString();
var headers = req.headers().toString();
console.log('[REQ] ' + url);
console.log('[HDRS] ' + headers);
return this.newCall(req);
};
});
使用 objection 快速提取令牌
objection --gadget com.target.app explore
# Inside:
android hooking watch class_method <class>.<method> --dump-args --dump-return
阶段 8 — 捕获网络流量(通过 mitmproxy)
绕过证书固定后,可用于深入发现 API。
# Run mitmproxy on host
mitmproxy --listen-port 8080
# Configure Android device proxy to host:8080
# Install mitmproxy CA cert on device:
# - Pull from http://mitm.it on the device, or
# - Push to /system/etc/security/cacerts/ on rooted device
# Use the Frida pinning bypass script while traffic flows through mitmproxy
# All API calls visible in mitmproxy UI
决策树 — 如何处理发现的问题
| 发现 | 下一步 |
|---|---|
| 有效 JWT(未过期) | 针对 API 主机进行测试——它是否授予访问权限?尝试 sid 操纵 |
| 已过期的 JWT | 检查路径令牌 / API 端点结构——可作为 VPN 后续操作的有用情报 |
| AWS 访问密钥 | 使用 awscli 进行测试:aws sts get-caller-identity——许多泄露的密钥仍有权限 |
| Firebase project_id + web_api_key | 测试 Firestore/RTDB/Storage 是否允许公开读取 |
| Google API 密钥(AIza*) | 使用 https://www.googleapis.com/customsearch/v1 等进行测试——查看该密钥启用了哪些 API |
| 硬编码的 HTTP URL(http://) | 如果缺少证书固定,可能遭遇降级攻击中间人攻击 |
| 为内部主机固定的证书 | 发现了新的资产——该主机确实存在 |
| 导出的 Activity,带有 WebView | 测试 Intent 注入 → 滥用 URL 加载 |
| 堆栈跟踪残留(Stack Overflow URL) | 找出开发者提出的问题 → 推断架构 |
| 硬编码的凭据 | 立即进行喷洒攻击(遵守相关技能设定的任何上限) |
注意事项
- 不要只搜索“password”——大多数机密都有特定的高信号模式(AKIA、AIza、eyJ 等)。搜索通用词会产生过多噪声。
- 不要跳过 XAPK 分包 APK——config.armeabi 分包和 config. 分包有时包含不同的代码路径。
- 不要把过期 JWT 当作“无用情报”——路径结构、端点列表和签名算法仍然有用。上面的 8 年前过期 JWT 示例就说明了这一点。
- 不要只逆向最新版本——较旧的 APK 版本(可通过 APKMirror 的版本历史找到)有时仍含有已从新版本中移除、但服务器端仍有效的机密。
- 即使应用看起来“简单”,也不要忽略 Firebase——Firebase 规则配置错误(Firestore 允许公开读取)极为常见。
- 不要在生产设备上运行 Frida——只能使用已 root 的模拟器或测试设备。
- APKPure CDN 速率限制——下载内容被截断、缺少 EOCD 的情况很常见。更换 IP(切换 VPN 节点)后重试;对于部分下载的归档文件,
7z x比unzip更宽容。 - 大型 APK 可能导致 jadx 内存溢出——对于大于 100 MB 的 APK,使用
-Xmx4g增加 jadx 堆内存,或先改用仅运行strings的流程。 - assets/ 中的 Keystore/SQLite 文件——由于不符合正则表达式机密模式而容易被忽略。请手动检查
assets/目录树中的.db、.sqlite、.jks、.bks、.p12文件。 - ProGuard/R8 混淆——大多数 APK 都经过混淆;机密模式会保留,但类名不会。不要因为方法名是
a.b.c.d就排除某个 APK。 - Multi-dex APK——一些机密位于
classes2.dex及后续文件中;不要只检查classes.dex。 - Play Store 开发者页面可能列出非活跃 / 测试版应用——请结合下载量和最后更新时间进行交叉核对;过时的应用可能已归档,但仍会泄露机密。
验证
运行此自测,以确认整个流程端到端正常:
-
获取测试——下载任意公开可用的 APK,并验证该文件是有效的 ZIP:
download-apk() { local pkg="$1" curl -sk -L --max-time 60 --connect-timeout 10 "https://d.apkpure.net/b/APK/$pkg?version=latest" -o "/tmp/$pkg.apk" file "/tmp/$pkg.apk" # should report: Zip archive data / Java archive data } download-apk org.example.testapp -
反编译测试——apktool 能解码二进制清单:
apktool d /tmp/org.example.testapp.apk -o /tmp/testapp_decoded/ ls /tmp/testapp_decoded/AndroidManifest.xml 2>/dev/null && echo "PASS: manifest decoded" || echo "FAIL: apktool not installed or APK corrupted" -
机密搜索测试——确认模式能匹配已知测试值:
echo "AKIA1234567890ABCDEF" | grep -oE 'AKIA[A-Z0-9]{16}' && echo "PASS: AWS key pattern" || echo "FAIL" echo "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U" | grep -oE 'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*' && echo "PASS: JWT pattern" || echo "FAIL" echo "AIzaSyD4iE2xVh2G7kLpQmN3jR8tUwXyZ1aBcDeF" | grep -oE 'AIza[A-Za-z0-9_-]{35}' && echo "PASS: Google API key pattern" || echo "FAIL" -
XAPK 处理测试——确认 7z 可用于处理分包 APK:
7z --help >/dev/null 2>&1 && echo "PASS: 7z available" || echo "FAIL: install p7zip-full" -
Firebase 测试——确认 google-services.json 解析正常:
echo '{"project_info":{"project_id":"test-project"}}' > /tmp/test-gs.json python3 -c "import json; d=json.load(open('/tmp/test-gs.json')); print(d['project_info']['project_id'])" && echo "PASS" || echo "FAIL" rm -f /tmp/test-gs.json
如果 5 项测试全部通过,说明整个流程已正常运行。测试 1(获取)可能因 APKPure CDN 屏蔽而失败——这不会影响工作流的其余部分;请改用 APKMirror 或直接 APK URL。
工具安装(一次性)
brew install jadx p7zip
pip install --break-system-packages frida-tools objection
# Genymotion or AVD for rooted Android emulator
为方便下载 APK,可以添加一个 download-apk shell 函数:
download-apk() {
local pkg="$1"
curl -sk -L --max-time 60 --connect-timeout 10 "https://d.apkpure.net/b/APK/$pkg?version=latest" -o "$pkg.apk"
echo "Downloaded $(wc -c < "$pkg.apk") bytes"
file "$pkg.apk"
}
与相邻技能的衔接
offensive-osint— Play Store / APKMirror 枚举是更广泛 OSINT 的一部分hunt-cloud-misconfig— Firebase 公开读取正是本技能在发现后的专长领域hunt-api-misconfig— 获取算法和示例令牌后即可进行 JWT 操纵evidence-hygiene— 提取的机密在纳入报告前需要脱敏redteam-mindset— 在参与项目的第 1 天就进行 APK 逆向,而非事后补做
真实案例发现模板(已匿名化 — 经授权的项目)
发现:面向客户的 APK 中硬编码了 JWT 和 30 多个内部 API 端点
- 对象:旧版移动应用构建中硬编码的内容暴露了内部 API 表面
- 观察结果:反编译
com.<client>.<app>后发现内嵌的 JWT(约 8 年前已过期),以及指向api2.<client>.example的 30 多个/v1/*端点路径(仅供内部使用,外部无法访问) - 描述:APK 中包含开发者构建产物。路径令牌属于安全隐匿措施;API 端点清单可用于侦察。
- 影响:攻击者通过凭据泄露获得 VPN 立足点后,无需逆向二进制文件即可获得完整的 API 表面图。若通过 SSRF/LFI 恢复 HS256 密钥,即可伪造任意令牌。
- 建议:轮换 HS256 密钥,迁移到 RS256,从构建产物中移除硬编码 URL,审计组织内所有 APK。
- 证据:提取的字符串、解码后的 JWT、端点清单
相关技能与攻击链
cloud-iam-deep— 从 APK 提取机密时,常会获得有效的 AWS/GCP/Azure 凭据。攻击链原语:jadx 字符串搜索生成 AWS Access Key ID + Secret →cloud-iam-deepaws sts get-caller-identity→ 角色/策略枚举 → IAM 权限提升路径(24 种已记录的 AWS 提权模式之一)→ 接管云控制面。同样的流程也适用于从 APK 资源中提取的 GCP 服务账号 JSON 和 Azure shared-access-signature 令牌。hunt-api-misconfig— APK 端点清单可免费提供 API 表面信息;常见漏洞包括批量赋值、JWT 和 CORS。攻击链原语:APK 暴露/v1/users/me和/v1/admin/users→ 使用hunt-api-misconfig对/v1/users/me执行批量赋值探测({is_admin:true})→ 提升为管理员角色 → 访问/v1/admin/users。hunt-rce— 从 APK 提取的硬编码 JWT 签名密钥(HS256)可用于伪造任意令牌。攻击链原语:从 APK 字符串中提取 HS256 密钥 → 伪造管理员令牌 → 访问管理 API → 如果 API 存在 eval/template/sink → 使用hunt-rce攻击服务器。此外,如果应用通过本地安装,带有 intent 注入接收点的导出组件也可能触发Runtime.exec。offensive-osint— APK 是组织更广泛侦察图谱中的一个节点;可与泄露数据语料库和证书透明度信息结合使用。攻击链原语:APK 暴露内部 API 主机名api2.example.com→ 使用offensive-osint查询证书透明度 → 发现同级子域名 → 扩大攻击面。redteam-report-template— APK 发现需要明确说明“二进制文件泄露了什么”,因为客户工程师常会将移动应用静态分析发现斥为“混淆问题”。攻击链原语:验证发现(提取到令牌/URL/机密)→triage-validation7-Question Gate(尤其是:“此凭据现在是否仍可用于身份验证?”)→ 使用明确的二进制版本和提取复现步骤,通过redteam-report-template整理报告。