ios-redteam-pipeline について
アプリの一覧化や IPA の取得から、静的・動的解析までを段階的に進める iOS アプリ向けセキュリティテストスキルです。認可されたモバイル評価で、シークレット、通信保護、URL 処理、ネットワーク通信を調べます。
主な機能
- 01組織が所有する iOS アプリの一覧化
- 02IPA ビルドの取得と展開
- 03バイナリ内のエンドポイントとシークレットの検査
- 04ATS と証明書ピンニングの確認
- 05URL スキームと Universal Links のテスト
- 06動的計測と通信キャプチャの支援
活用シーン
- 01バグバウンティ対象の iOS アプリ評価
- 02TestFlight と 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を使用してください - React Native/Flutter アプリが、Web 側の JS バンドル分析ですでに完全にカバーされている場合
- モバイルクライアントが対象範囲に含まれない、サーバーサイドのみの評価の場合
ステージ 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 の一覧から見つかった関連アプリのバンドル 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 ホストがハードコードされているなど)。両方ある場合は、必ず App Store ビルドより 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 — App Transport Security(ATS)の設定ミスと証明書ピンニングの回避
ATS が一括で無効化されていないか確認する(iOS のネットワーク設定ミスで最もよく見られるもの)
plutil -extract NSAppTransportSecurity xml1 -o - Info.plist
出力に含まれる危険な兆候:
- トップレベルで
NSAllowsArbitraryLoads = true— HTTP(平文)がアプリ全体で許可されています。 - 認証トークンを扱うドメインに対する
NSExceptionAllowsInsecureHTTPLoads = true。 NSAllowsArbitraryLoadsInWebContent = true— WKWebView のトラフィックが除外されています。見落としやすい問題です。
ATS の一括例外とネットワーク上の攻撃位置(不正な AP、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 を経由するため、信頼性の高い汎用フックでは、ネイティブ層の SSL_CTX_set_custom_verify /
SSL_get_psk_identity を対象にし、検証コールバックが「OK」を返すよう強制します。これにより、
URLSession、AFNetworking、Alamofire、TrustKit をまとめて捕捉できます。一方、非推奨の
NSURLConnection デリゲートなど、デリゲートごとの Objective-C フックでは捕捉できません。
# 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 スキーム/ユニバーサルリンクの列挙
# 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
見つかったカスタムスキームごとに(例:myapp://):
- Safari/Notes から呼び出し、アプリがパラメーターをどう処理するかを確認します。
myapp://reset-password?token=x&redirect=https://evil.com— アプリは攻撃者が指定したredirect/url/callbackパラメーターを信用して WebView で読み込むでしょうか(オープンリダイレクト → WebView-XSS の連鎖)、あるいは認証画面の回避に利用するでしょうか? - 同じスキームがデバイス上の別のアプリにも登録されている場合(スキームスクワッティング)、悪意のある
アプリが正規アプリ宛ての通信を傍受できます。
CFBundleURLNameが一意であることを確認してください。 - AASA の検証に失敗した場合、または AASA が存在しない場合、ユニバーサルリンクはカスタムスキームにフォールバックします。 同じパラメーターインジェクションの攻撃面について、両方の経路をテストしてください。
ステージ 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 スキームが redirect/url パラメーターを受け付ける | オープンリダイレクト → WebView 読み込みの連鎖をテストし、インストール済みの他のアプリによるスキームスクワッティングを確認します |
| ユニバーサルリンクの AASA が存在しない/誤設定されている | スキームへのフォールバックでも同じインジェクション可能なパラメーターが引き継がれることを確認します |
Entitlements で keychain-access-groups が他のアプリと共有されている | アプリ間の Keychain 読み取りが可能です。侵害された兄弟アプリが、このアプリのシークレットを読み取ります |
| 現在の Web アプリが呼び出すものより古い API バージョンが参照されている | バージョン差分の確認を hunt-shadow-api に引き継ぎます |
アンチパターン
- App Store のアプリは FairPlay で暗号化されており、脱獄が必要だと思い込まないでください — TestFlight やエンタープライズ/アドホックビルドは暗号化されていないことが多く、復号手順がまったく必要ない場合もあります。
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 に対応するスキルです。対象アプリが両方のプラットフォームで提供されている場合は、両方を実行してください。プラットフォーム間で共通するバンドル ID やバックエンドのエンドポイントが見つかることがよくあります。hunt-shadow-api— モバイルビルド(iOS、Android ともに)では、現在のウェブアプリより古い API バージョンがハードコードされていることがよくあります。連携の流れ:IPA から/v1/*エンドポイントを特定し、hunt-shadow-apiで/v1/と現在の/v3/を比較して、認証や検証の不備を調べます。cloud-iam-deep— IPA からのシークレット抽出で、有効な AWS/GCP/Azure の認証情報や Firebase の設定が見つかることがよくあります。連携の流れ:文字列の grep で AWS キーを検出し、cloud-iam-deepで権限を分析します。hunt-api-misconfig— ここで抽出したハードコードされた JWT や API キーを使って、そちらで JWT のアルゴリズム混同や一括割り当てのテストを行います。evidence-hygiene— 抽出した Keychain の項目やシークレットは、レポートに含める前にマスキングする必要があります。offensive-osint— App Store の開発者ページの列挙は、より広範な組織の偵察グラフの一部です。同じブランドの証明書透明性検索と組み合わせてください。