关于 ios-redteam-pipeline
分阶段评估 iOS 应用的安全测试技能,涵盖应用盘点、IPA 获取、静态分析和运行时分析。它检查密钥、传输安全、URL 处理与网络流量,适用于获授权的移动端评估。
主要功能
- 01盘点组织拥有的 iOS 应用
- 02获取并解包 IPA 构建
- 03扫描二进制中的端点与密钥
- 04检查 ATS 和证书固定
- 05测试 URL Scheme 与 Universal Links
- 06支持运行时插桩和流量捕获
适用场景
- 01评估漏洞赏金范围内的 iOS 应用
- 02比较 TestFlight 与 App Store 构建
- 03检查移动应用传输安全和深层链接
- 04测试时追踪应用网络流量
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ elementalsouls/ claude-bughunter --skill ios-redteam-pipeline安装后在 Claude Code 中输入/ios-redteam-pipeline确认已加载。
详细介绍
适用场景
符合以下条件时触发:
- 侦察发现目标的 Apple Developer / App Store 发布者页面下有 1 个或多个应用
- 找到 TestFlight 公共链接或企业版/临时版
.ipa/manifest.plist(OTA 安装) - 面向客户的应用、经销商/合作伙伴门户或员工移动配套应用已在 iOS 上发布
- 漏洞赏金计划将 iOS 列入范围
apk-redteam-pipeline已发现 Android 端点/密钥——iOS 版本通常采用不同的后端版本,值得进行差异比对(参见hunt-shadow-api)
请勿用于:
- 仅支持 Android 的目标——应使用
apk-redteam-pipeline - 已通过 Web 端的 JS 包分析完全覆盖的 React Native / Flutter 应用
- 评估范围内没有移动客户端的纯服务端评估
阶段 0 — 清点组织拥有的所有 iOS 应用
# App Store search API (no auth, no scraping needed)
curl -s "https://itunes.apple.com/search?term=<brand>&country=us&entity=software&limit=50" | python3 -m json.tool
# Pull the full metadata for a known bundle ID (once you have one)
curl -s "https://itunes.apple.com/lookup?bundleId=com.<brand>.app&country=us"
提取:trackId、bundleId、sellerName(开发者账户——可据此查找同属该账户的其他应用)、version、
releaseNotes(更新日志常会提及已弃用/移除的 API 行为,可为 hunt-shadow-api 提供线索)。
将 Android APK 清单中发现的同属该组织的应用 bundle ID 进行交叉比对(同一
多品牌企业集团通常会在两个平台上沿用 com.<corp>.<sub-brand> 命名方式)。
阶段 1 — 获取 IPA
首选:从你控制的真实设备获取(已购买/免费应用无需越狱)
# Install the app on a real device via Apple Configurator 2 or Xcode, then pull the .ipa
# Apple Configurator 2 (macOS): Devices > select device > right-click installed app > "Save to..."
# Or via libimobiledevice:
brew install libimobiledevice ideviceinstaller
ideviceinstaller -l # list installed apps + bundle IDs
次选:TestFlight(如果该计划公开分发测试版)
打开 TestFlight 公共链接,通过 TestFlight 应用安装,然后按上述方法提取。 TestFlight 版本的防护通常比 App Store 发布版本弱得多(调试日志未关闭、 硬编码了预发布 API 主机)——如果两种版本都有,应始终优先使用 TestFlight 版本,而非商店版本。
第三选择:企业版 / 临时分发(OTA 安装)
# itms-services:// links embed a manifest.plist with a direct .ipa URL
curl -s "https://<target>/manifest.plist" | plutil -convert xml1 -o - -
# Look for <key>software-package</key> — that URL is a directly downloadable, unencrypted IPA
curl -sk -L "<software-package-url>" -o target.ipa
企业版/临时版 IPA 未使用 FairPlay 加密——无需越狱或解密工具, 这与从设备提取的 App Store 二进制文件不同。
解密来自 App Store 的二进制文件(仅限从越狱设备中提取的情况)
App Store 二进制文件在静态存储时经过 FairPlay 加密;从越狱设备复制的二进制文件
需要先进行运行时解密(使用 frida-ios-dump、bagbak 或 flexdecrypt),静态分析工具才能
有效读取:
pip install --break-system-packages frida-tools
# with frida-server running on the jailbroken device and the app in foreground:
python3 dump.py <bundle_id> # frida-ios-dump — outputs a decrypted .ipa
阶段 2 — 解包与静态分析
# An IPA is just a zip
unzip -o target.ipa -d extracted_target/
cd extracted_target/Payload/*.app
# Info.plist — bundle ID, URL schemes, ATS config, entitlements hint
plutil -convert xml1 -o - Info.plist
# Entitlements — codesign reads the .app bundle (or the Mach-O binary), NOT Info.plist
codesign -d --entitlements :- Payload/<AppName>.app 2>/dev/null || \
security cms -D -i embedded.mobileprovision | plutil -convert xml1 -o - -
# class-dump for Objective-C symbol/class recovery (compiled binary, not the .app bundle)
brew install class-dump
class-dump -H <AppBinaryName> -o headers/
# Swift binaries: class-dump won't show much — use Hopper, Ghidra, or `nm`/`strings` instead
nm -a <AppBinaryName> | grep -i swift | head -50
strings -a <AppBinaryName> > strings_target.txt
如需快速初步排查,无需反汇编工具,单独导出字符串通常就能发现 阶段 3 要查找的大部分内容。
阶段 3 — 搜索密钥(与 apk-redteam-pipeline 使用相同的清单,另加 iOS 专属来源)
# URL grep — owned-domain references
grep -oE 'https?://[a-zA-Z0-9.-]+\.(target1|target2|target3)\.(com|io|net)[a-zA-Z0-9./_?=&%-]*' strings_target.txt | sort -u
# Cloud credentials (same 60-pattern catalog as Android — reuse verbatim)
grep -oE 'AKIA[A-Z0-9]{16}' # AWS Access Key
grep -oE 'AIza[A-Za-z0-9_-]{35}' # Google API key
grep -oE 'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*' strings_target.txt # JWT
# iOS-specific: GoogleService-Info.plist (Firebase config bundled per-platform)
find extracted_target -iname "GoogleService-Info.plist" -exec plutil -convert xml1 -o - {} \;
# Look for API_KEY, PROJECT_ID, STORAGE_BUCKET, GCM_SENDER_ID, GOOGLE_APP_ID — same Firebase
# public-read tests as apk-redteam-pipeline Stage 6.
# iOS-specific: hardcoded config plists shipped in the bundle
find extracted_target -iname "*.plist" ! -iname "Info.plist" -exec sh -c 'echo "=== {} ==="; plutil -convert xml1 -o - "{}"' \;
# App Transport Security exceptions (Info.plist) — tells you if arbitrary/insecure loads are allowed
plutil -extract NSAppTransportSecurity xml1 -o - Info.plist 2>/dev/null
备份中残留的钥匙串项目(如果你有设备备份,无论是否加密)
pip install --break-system-packages iphone_backup_decrypt # for encrypted local backups
# keychain-dumper (run ON a jailbroken device) for live keychain contents:
./keychain-dumper -e # -e also decodes entitlements per item, showing which app owns which secret
阶段 4 — 应用传输安全(ATS)配置错误与证书固定绕过
检查是否全局禁用了 ATS(最常见的 iOS 网络配置错误)
plutil -extract NSAppTransportSecurity xml1 -o - Info.plist
输出中的危险信号:
- 顶层设置
NSAllowsArbitraryLoads = true——任何位置都允许使用 HTTP(明文传输),影响整个应用。 - 对承载身份验证令牌的域设置了
NSExceptionAllowsInsecureHTTPLoads = true。 - 设置了
NSAllowsArbitraryLoadsInWebContent = true——WKWebView 流量被排除在 ATS 保护之外,这是常见的盲区。
全局 ATS 例外配置加上处于网络中间位置(恶意接入点、ARP 欺骗、恶意 VPN 配置文件), 即可在完全不触及证书固定机制的情况下拦截明文凭据/令牌。
证书固定绕过(已启用且配置正确时)
pip install --break-system-packages frida-tools objection
objection --gadget <bundle_id> explore
# inside objection:
ios sslpinning disable
如果你想用 Frida 脚本,请勿自行编写,而应使用维护中的通用绕过脚本
(例如广泛使用的 frida-ios-pinning/ios-ssl-bypass 社区脚本)。现代 iOS TLS 流量会经过
BoringSSL,因此可靠的通用 Hook 会在原生层针对 SSL_CTX_set_custom_verify /
SSL_get_psk_identity,并强制验证回调返回“OK”——这样可一次覆盖 URLSession、AFNetworking、
Alamofire 和 TrustKit,而按委托分别设置的 Objective-C Hook(例如已弃用的
NSURLConnection 委托)则无法做到:
# Pull a maintained universal BoringSSL-layer bypass and run it:
frida -U -f <bundle_id> -l ios-ssl-bypass.js --no-pause
实际使用时,优先选择 SSL Kill Switch 2(越狱设备上以 tweak 形式安装)或
objection 的 ios sslpinning disable,不要使用自行编写的脚本——两者都能覆盖常见的
证书固定实现(NSURLSession、AFNetworking、TrustKit),无需针对每个应用单独调整。
阶段 5 — URL scheme / Universal Link 枚举
# Custom URL schemes (CFBundleURLTypes) — anything can invoke these via Safari/another app
plutil -extract CFBundleURLTypes xml1 -o - Info.plist
# Universal Links / Associated Domains (applinks:) — requires a matching apple-app-site-association
plutil -extract com.apple.developer.associated-domains xml1 -o - Info.plist 2>/dev/null
curl -s "https://<domain>/.well-known/apple-app-site-association" | python3 -m json.tool
对发现的每个自定义 scheme(例如 myapp://):
- 从 Safari/Notes 触发它,并观察应用如何处理参数:
myapp://reset-password?token=x&redirect=https://evil.com——应用是否信任 攻击者提供的redirect/url/callback参数,并在 WebView 中加载该地址(开放重定向 → WebView-XSS 攻击链),或利用它绕过身份验证界面? - 如果设备上的其他应用也注册了该 scheme(scheme squatting),恶意应用就能拦截原本发给合法应用的流量——检查
CFBundleURLName是否唯一。 - Universal Links 在 AASA 验证失败或缺失时会回退到自定义 scheme——针对同一参数注入攻击面测试这两条路径。
阶段 6 — 运行时插桩(Frida / objection)
需要越狱设备(旧版 iOS 可使用 checkra1n/palera1n,或使用 Corellium 虚拟设备)—— 与 Android 不同,iOS 没有实用的 Root 模拟器替代方案。
# Setup
pip install --break-system-packages frida-tools objection
# Install frida-server on the jailbroken device via Cydia/Sileo (frida repo), matching the
# host frida-tools version exactly — version skew is the #1 cause of "failed to attach" errors.
# Full interactive exploration
objection --gadget <bundle_id> explore
# Inside objection:
ios hooking watch class <ClassName>
ios hooking watch class_method <Class>.<method> --dump-args --dump-return
ios keychain dump
ios plist dump
ios cookies get
网络流量捕获(绕过证书固定后)
mitmproxy --listen-port 8080
# Set the device Wi-Fi proxy to host:8080, install the mitmproxy CA via http://mitm.it on-device
# (Settings > General > VPN & Device Management > trust the cert, then enable full trust under
# Certificate Trust Settings — iOS requires this second step, unlike Android)
决策树 — 根据发现采取行动
| 发现 | 后续操作 |
|---|---|
NSAllowsArbitraryLoads = true | 确认令牌/凭据是否确实通过 HTTP 传输——利用它需要处于网络路径上,但其本身就是有效发现(配置层面的问题,无需 MITM 即可报告) |
| 字符串转储中存在硬编码的 JWT / API 密钥 | 与 Android 相同——针对 API 主机测试,并串联至 hunt-api-misconfig |
| 存在 GoogleService-Info.plist | 测试 Firestore/RTDB/Storage 是否可公开读取(与 apk-redteam-pipeline 阶段 6 相同) |
自定义 URL scheme 接受 redirect/url 参数 | 测试开放重定向 → WebView 加载攻击链;检查其他已安装应用是否存在 scheme squatting |
| Universal Link 的 AASA 缺失/配置错误 | 确认 scheme 回退仍会携带相同的可注入参数 |
Entitlements 显示 keychain-access-groups 与其他应用共享 | 跨应用读取 Keychain——受攻击的关联应用可读取此应用的机密信息 |
| 引用的是较旧的 API 版本,而当前 Web 应用调用的是另一版本 | 转交给 hunt-shadow-api 进行版本差异分析 |
反模式
- 不要认为 App Store 应用都经过 FairPlay 加密,因此需要越狱——TestFlight 和企业版/Ad Hoc
构建通常未加密,完全不需要解密步骤。先确认分发渠道,再考虑使用
frida-ios-dump。 - 不要因为 class-dump 没有显示内容,就忽略 Swift 二进制文件——
strings和nm仍然可用;使用支持 Swift 的反汇编工具(Hopper、启用了 Swift 符号反混淆的 Ghidra)可恢复其余内容。 - 不要只把“存在证书固定”当作发现——证书固定是一种缓解措施,并非漏洞;真正的发现是在绕过后能做什么(或者让证书固定失效的 ATS 错误配置)。
- 不要忽略由 WKWebView 加载的内容——
NSAllowsArbitraryLoadsInWebContent与顶层 ATS 设置是不同的例外,且经常被忽略。 - 不要在个人/生产环境的 Apple ID 设备上运行 Frida 插桩——使用专用的越狱测试设备或 Corellium 实例。
工具安装(一次性)
brew install libimobiledevice ideviceinstaller class-dump
pip install --break-system-packages frida-tools objection iphone_backup_decrypt
# Jailbroken test device (checkra1n/palera1n) or a Corellium virtual iOS device for runtime work
# frida-server installed on-device via the Frida Cydia/Sileo repo
相关技能与链路
apk-redteam-pipeline——Android 对应方案;如果目标同时提供两个平台的版本,请同时运行二者。同源的 Bundle ID 和共用的后端端点经常会揭示跨平台问题。hunt-shadow-api——移动端构建版本(iOS 和 Android 均如此)中硬编码的 API 版本通常比当前 Web 应用旧。链路:IPA 揭示/v1/*端点 →hunt-shadow-api对比/v1/与当前的/v3/,查找身份验证/验证逻辑回归。cloud-iam-deep——从 IPA 提取的密钥经常会包含有效的 AWS/GCP/Azure 凭据或 Firebase 配置。链路:通过 strings-grep 找到 AWS 密钥 → 使用cloud-iam-deep进行权限分析。hunt-api-misconfig——在此处提取的硬编码 JWT/API 密钥可直接用于该工具中的 JWT 算法混淆和批量赋值测试。evidence-hygiene——将提取的 Keychain 项目和密钥纳入报告前,需要先进行脱敏。offensive-osint——枚举 App Store 开发者页面是更广泛的组织侦察图谱的一部分;可与针对同一品牌的证书透明度查询配合使用。