mcpField
A
apk-redteam-pipeline

apk-redteam-pipeline

github.com/elementalsoulsセキュリティ5.3k281 回インストール今日更新
リポジトリ

apk-redteam-pipeline について

apk-redteam-pipelineは、許可を得たAndroidアプリのセキュリティ評価向けスキルです。APKの一覧化・取得から静的解析、実行時解析、発見事項の検証までを案内します。

主な機能

  1. 01Androidアプリの一覧化とAPK取得
  2. 02DEX逆コンパイルとシークレット検索
  3. 03証明書とエクスポートコンポーネントの確認
  4. 04Firebase設定の調査
  5. 05Fridaによる計測と通信キャプチャ

活用シーン

  1. 01対象範囲内のAndroidアプリの評価
  2. 02埋め込みエンドポイントや認証情報の発見
  3. 03エクスポートコンポーネントのIntentリスク確認
  4. 04実行時トークンやAPIリクエストの追跡

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/elementalsouls/claude-bughunter --skill apk-redteam-pipeline

インストール後、Claude Code で/apk-redteam-pipelineを入力して読み込まれたか確認します。

詳細

このスキルを使用する場面

次の場合に実行します。

  • 調査で、対象の開発者名(Play ストアのデベロッパーページ)に紐づくモバイルアプリが1つ以上見つかった場合
  • ウェブアプリが *.apk ファイルを直接ホストしている場合(例:ある案件でサブドメインから Recruitz.apk が見つかった)
  • インフォスティーラーログから APK のパッケージ ID が漏えいしている場合(例:インフォスティーラーダンプ形式に含まれる com.<brand>.app、com.<brand>.<sub-brand> のパターン)
  • 顧客向けアプリ、ディーラー/パートナー向けポータル、または従業員向けモバイルコンパニオンアプリがスコープ内にある場合
  • バグバウンティプログラムで Android がスコープ内に指定されている場合

次の場合は使用しないでください。

  • iOS のみが対象の場合(別のパイプラインを使用します。IPA のリバース、MobSF、frida-ios-dump)
  • JavaScript バンドル分析で既にカバーされている React Native/Flutter のウェブアプリ
  • サーバーサイドのみの評価

ステージ 0 — 組織が所有するすべてのアプリの一覧作成

Play ストアのデベロッパーページのスクレイピング
# Find developer page from the target's brand name
curl -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 \
  "https://d.apkpure.net/b/APK/<package_id>?version=latest" \
  -o "<package_id>.apk"

# Or via the legacy d-XX.winudf.com mirror chain (we saw this work)
第二候補:APKMirror で検索
curl -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 -sk "https://apkpure.com/search?q=<brand>" | grep -oE 'data-dt-app="[^"]+"'
XAPK と APK
  • .xapk は、複数の分割 APK(base + config.armeabi-v7a + config.en + など)を含む zip です
  • まず外側のファイルを解凍し、次に内側の base.apk または <package>.apk を解凍します
  • apkpure のダウンロードでは、EOCD 署名が欠落した不完全な XAPK が返されることがあります。これは CDN のレート制限が原因です。IP を切り替えて再試行するか、unzip より寛容な 7z x を使用します
# 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 — シークレットの grep(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 によって暗黙的にエクスポートされているもの)は、他のアプリから起動できるため、インテントインジェクションの潜在的な攻撃対象領域となります。

# 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 を受け付けるか(インテント → WebView → XSS/file://)
  • URI extras を受け付けるか(ディープリンクを介した SSRF の可能性)
  • extras を他の Activity に渡すか(インテントのリダイレクト)

ステージ 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 -s "https://firestore.googleapis.com/v1/projects/<project_id>/databases/(default)/documents/users"

# Test if Realtime DB is publicly readable
curl -s "https://<project_id>.firebaseio.com/.json"

# Test if Storage Bucket is publicly listable
curl -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
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_keyFirestore/RTDB/Storage の公開読み取りをテストします
Google API キー(AIza*)https://www.googleapis.com/customsearch/v1 などに対してテストし、キーで有効になる API を確認します
ハードコードされた HTTP URL(http://)証明書ピンニングがない場合、ダウングレードによる MITM の可能性があります
内部ホスト向けにピン留めされた証明書新たなアセットの発見です。そのホストは実在します
WebView を含むエクスポート済み Activityインテントインジェクションから URL 読み込みの悪用までをテストします
スタックトレースの痕跡(Stack Overflow の URL)開発者の質問を特定し、アーキテクチャを推測します
ハードコードされた認証情報すぐにスプレー攻撃を試みます(関連スキルの上限があれば遵守してください)

反パターン

  • 「password」だけを grep しないでください — ほとんどのシークレットには、より高い確度で検出できる固有のパターン(AKIA、AIza、eyJ など)があります。一般的な単語を grep すると、ノイズが多くなります。
  • XAPK の分割 APK を見落とさないでください — config.armeabi の分割や config. の分割には、異なるコードパスが含まれていることがあります。
  • 期限切れの JWT を「使えない情報」として扱わないでください — パス構造、エンドポイント一覧、署名アルゴリズムは依然として役立ちます。上記の有効期限が 8 年前の JWT の例が示すとおりです。
  • 最新バージョンだけをリバースしないでください — APKMirror のバージョン履歴から取得した古い APK には、新しいバージョンでは削除されていても、サーバー側ではまだ有効なシークレットが含まれていることがあります。
  • アプリが「シンプル」に見えても Firebase を無視しないでください — Firebase のルールの設定ミス(Firestore の公開読み取り)は非常によくあります。
  • 本番環境のデバイスで Frida を実行しないでください — ルート化済みのエミュレーターまたはテスト用デバイスだけを使用してください。

ツールのインストール(一度だけ)

brew install jadx p7zip
pip install --break-system-packages frida-tools objection
# Genymotion or AVD for rooted Android emulator

APK のダウンロードを簡単にするため、download-apk シェル関数を追加できます:

download-apk() {
  local pkg="$1"
  curl -sk -L --max-time 60 "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 — APK のリバースは、後回しではなくエンゲージメントの初日に行います

実環境での発見事項テンプレート(匿名化済み — 許可を得たエンゲージメント)

発見事項:顧客向け APK にハードコードされた JWT と 30 以上の内部 API エンドポイント

  • 対象:レガシーのモバイルビルドにハードコードされた痕跡があり、内部 API の範囲が明らかになった
  • 観察事項:com.<client>.<app> を逆コンパイルしたところ、埋め込まれた JWT(約 8 年前に期限切れ)と、api2.<client>.example に対する 30 以上の /v1/* エンドポイントパスが見つかりました(外部からは内部専用)
  • 説明:APK には開発者用ビルドの痕跡が含まれています。パストークンは隠蔽によるセキュリティであり、API エンドポイントの一覧は偵察に利用できるレベルの情報です。
  • 影響:VPN 接続後の足掛かり(認証情報の侵害経由)を得た攻撃者は、バイナリをリバースせずに API 全体の一覧を把握できます。HS256 シークレットを回復(SSRF/LFI 経由)すれば、任意のトークンを偽造できます。
  • 推奨事項:HS256 シークレットをローテーションし、RS256 に移行し、ビルドからハードコードされた URL を削除し、組織内のすべての APK を監査してください。
  • 証拠:抽出した文字列、デコードした JWT、エンドポイント一覧

関連するスキルとチェーン

  • cloud-iam-deep — APK からシークレットを抽出すると、有効な AWS/GCP/Azure の認証情報が見つかることがよくあります。チェーンの基本フロー:jadx による文字列の grep で AWS Access Key ID と Secret を取得 → cloud-iam-deep の aws sts get-caller-identity → ロール/ポリシーの列挙 → IAM 権限昇格の経路(文書化された 24 種類の AWS 権限昇格パターンのいずれか)→ クラウドプレーンの乗っ取り。同じフローは、APK リソースから抽出した GCP サービスアカウント JSON や Azure の Shared Access Signature トークンにも適用できます。
  • hunt-api-misconfig — APK のエンドポイント一覧から API の攻撃対象領域をそのまま把握できます。Mass assignment、JWT、CORS の脆弱性がよく見つかります。チェーンの基本フロー:APK から /v1/users/me と /v1/admin/users が判明 → hunt-api-misconfig で /v1/users/me に Mass assignment のプローブ({is_admin:true})を送信 → 管理者ロールに昇格 → /v1/admin/users にアクセス。
  • hunt-rce — APK から抽出したハードコードされた JWT 署名シークレット(HS256)を使うと、任意のトークンを偽造できます。チェーンの基本フロー:APK の文字列から HS256 シークレットを取得 → 管理者トークンを偽造 → 管理 API にアクセス → API に eval/テンプレート/シンクがある場合は、hunt-rce でサーバーに到達。また、エクスポートされたコンポーネントにインテントインジェクションのシンクがある場合、アプリがローカルにインストールされていれば Runtime.exec に到達できる可能性があります。
  • offensive-osint — APK は、より広範な組織の偵察グラフを構成するノードのひとつです。漏えい情報のコーパスや証明書の透明性と組み合わせてください。チェーンの基本フロー:APK から内部 API のホスト名 api2.example.com が判明 → offensive-osint で証明書の透明性を調査 → 関連するサブドメインを発見 → 攻撃対象領域を拡大。
  • redteam-report-template — クライアント側のエンジニアは、モバイルアプリの静的解析で見つかった問題を「難読化の問題」として片付けることが多いため、APK の調査結果は「バイナリから何が漏れているか」を明確に示す必要があります。チェーンの基本フロー:検証済みの問題(トークン/URL/シークレットを抽出)→ triage-validation の 7-Question Gate(特に「この認証情報は現在も認証に使えるか?」)→ バイナリのバージョンと抽出手順の再現方法を明記して redteam-report-template で報告書を作成。

コメント