mcpField
B
bypassing-mobile-pinning

bypassing-mobile-pinning

github.com/trilwuセキュリティ17754 回インストール12 週間前に更新
リポジトリ

bypassing-mobile-pinning について

認可されたモバイルアプリテストで、TLS プロキシに通信が表示されない原因を診断し、対処するスキルです。CA の信頼、プロキシ設定、相互 TLS などを切り分け、Android と iOS のスタックに応じた確認やフックを案内します。

主な機能

  1. 01プロキシに通信が表示されない原因の診断
  2. 02Android と iOS の TLS スタック特定
  3. 03スタックに応じたフック位置の選択
  4. 04相互 TLS とプロキシ迂回の確認
  5. 05リクエスト変更による傍受の検証

活用シーン

  1. 01モバイルアプリの TLS ハンドシェイク失敗の調査
  2. 02認可されたテストでアプリの API リクエストを確認
  3. 03ピンニングと他のネットワーク問題の切り分け

インストール

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

$npx skills add https://github.com/trilwu/secskills --skill bypassing-mobile-pinning

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

詳細

モバイルのピンニングを回避する

ここで時間を無駄にする原因の大半は、そもそも存在しないピンニングを回避しようとすることです。プロキシに何も表示されない原因は少なくとも6つあり、それぞれ対処法が異なります。そのうちの1つはピンニングではなく、ユーザー CA を信頼しない Android のデフォルト設定であり、もう1つはアプリが独自の TLS スタックを持ち、プロキシを経由していないケースです。回避スクリプトを試す前に、原因を診断してください。

テストが許可されているアプリに対してのみ使用してください。

使用する場面

  • Burp/mitmproxy/Charles にモバイルアプリからの TLS ハンドシェイク失敗が表示される
  • アプリが明らかにネットワークに接続しているのに、プロキシには何も表示されない
  • プロキシを設定すると、アプリに「ネットワークエラー」、「安全でない接続」などが表示される
  • 対象にピンニングがあると把握しており、そのスタックに適したフックが必要である

使用しない場面

  • Flutter の対象 — reversing-flutter-apps を使用してください。Flutter でも同じような失敗が起きますが、原因と対処法はエンジンに特有です
  • モバイルアセスメント全般 — testing-mobile-applications を使用してください
  • トラフィックが見えるようになった後のバックエンド — testing-apis を使用してください
  • ネイティブ .so の詳細なリバース — analyzing-binaries を使用してください

まず原因を診断する

何かをインストールする前に、これを実行してください。症状から原因を特定できます。

症状考えられる原因対処法
プロキシに CONNECT が表示され、その後 TLS アラート unknown_ca/bad_certificate が表示されるCA が信頼されていないシステム CA としてインストールするか、Network Security Config にパッチを適用する
プロキシに CONNECT が表示され、その後一部のドメインに限ってアラートが表示される対象ホストに限定された実際のピンニングピンニングのチェックにフックを仕掛ける
プロキシにまったく何も表示されないのに、アプリは動作するアプリがシステムプロキシを無視しているネットワーク層でトラフィックを強制的に経由させる(Flutter、gRPC、一部の SDK)
プロキシにハンドシェイクが表示されるが、サーバーがクライアントを拒否する相互 TLS — アプリがクライアント証明書を提示しているクライアント証明書を抽出する。これはピンニングではありません
エミュレーターでは動作するが、実機では失敗する(またはその逆)TLS ではなく、ルート/エミュレーター検出別の問題です — 検出のセクションを参照してください
ログイン後にのみ失敗するAPI ホストにのみ適用されたピンニングフックを仕掛けてから、再度確認する
# What does the app actually declare?
apktool d target.apk -o out
cat out/res/xml/network_security_config.xml 2>/dev/null
rg -n 'networkSecurityConfig|usesCleartextTraffic' out/AndroidManifest.xml

# Which TLS stack is in play?
rg -l 'okhttp3|CertificatePinner|TrustKit|AFNetworking|Alamofire' out/ 2>/dev/null | head
unzip -l target.apk | rg 'libssl|libcrypto|libconscrypt|libflutter|libil2cpp|libmonodroid'

ピンニングを疑う前に、Network Security Config を確認してください。 Android 7 以降では、アプリはユーザーがインストールした CA をデフォルトで信頼しません。ピンニングのコードがまったくないアプリでも、通信の傍受に失敗することがあります。対処法は2つあります。

# A. Install your CA as a SYSTEM certificate (no app modification — preferred)
#    Emulator:
emulator -avd <name> -writable-system
adb root && adb remount
openssl x509 -inform PEM -subject_hash_old -in burp.pem | head -1   # → e.g. 9a5ba580
cp burp.pem 9a5ba580.0 && adb push 9a5ba580.0 /system/etc/security/cacerts/
adb shell chmod 644 /system/etc/security/cacerts/9a5ba580.0
#    Rooted device: use a Magisk module so the change survives reboot

# B. Patch the config and repack (when you cannot get a system CA)
#    Add to network_security_config.xml:
#      <base-config><trust-anchors>
#        <certificates src="system"/><certificates src="user"/>
#      </trust-anchors></base-config>
apktool b out -o patched.apk && apksigner sign --ks debug.keystore patched.apk

方法 A のほうが適しています。アプリに一切変更を加えないため、整合性チェック、署名チェック、Play Integrity に影響しません。デバイスにシステム CA を導入できない場合に限り、方法 B を選んでください。

Android:スタック別のフック

スタックを特定してからフックします。汎用スクリプトが機能しないと、実際には対象レイヤーを間違えているだけなのに、「バイパスに失敗した」と見えてしまいます。

# Start here — covers the common Java-layer stacks in one shot
objection -g com.target.app explore
# then: android sslpinning disable

# Or a maintained universal script
frida -U -f com.target.app -l android-ssl-bypass.js --no-pause
スタック識別子フックポイント
OkHttp 3/4okhttp3.CertificatePinnerCertificatePinner.check() — 正常に戻るようにします
HttpURLConnectionjavax.net.ssl.*カスタム X509TrustManager.checkServerTrusted()
Conscryptlibconscrypt_jni.soPlatform.checkServerTrusted、またはネイティブレイヤー
Apache HttpClientorg.apache.httpSSLSocketFactory の検証処理
WebViewonReceivedSslErrorhandler.proceed() を強制します
ネイティブ / BoringSSLlibssl.so、カスタム .soSSL_CTX_set_custom_verify、ssl_verify_peer_cert
Xamarinlibmonodroid.so、libxamarin*Mono レベルの ServicePointManager / HttpClientHandler
Unitylibil2cpp.soUnityWebRequest の証明書ハンドラー、または BestHTTP の検証処理
gRPClibgrpc.soチャネルの認証情報。多くの場合、プロキシも無視します

ネイティブのピンニングでは、汎用スクリプトは機能しなくなります。 objection が成功を報告しているのに通信が失敗する場合、チェックは .so 内にあります。BoringSSL の検証エントリーポイントはエクスポートされていないため、スクリプトはパターンスキャンでそれらを特定しますが、パターンはバージョンごとに異なります。対象を確認してください。

# Is a native TLS stack even present?
unzip -j target.apk 'lib/arm64-v8a/*' -d libs && rg -l 'SSL_|BoringSSL|s2n' libs/
# Trace the native calls to see which library terminates TLS
frida-trace -U -f com.target.app -i 'SSL_*' -i '*verify*'

Unity、Xamarin、Flutter のビルドでは、まずシンボルを復元します — reversing-unity-il2cpp、reversing-flutter-apps — そのうえで、手当たり次第にパターンスキャンするのではなく、名前が判明した関数をフックします。

iOS

# Frida-based, covers NSURLSession and the common libraries
objection -g com.target.app explore
# then: ios sslpinning disable

# Jailbroken device, no Frida
# SSL Kill Switch 3 — system-wide, survives app updates
ライブラリフックポイント
NSURLSession(デフォルト)URLSession:didReceiveChallenge: → サーバーのトラストを使った .useCredential
TrustKitTSKPinningValidator の結果
AFNetworkingAFSecurityPolicy.evaluateServerTrust:
AlamofireServerTrustManager / ServerTrustEvaluating
ネイティブ/BoringSSLAndroid と同様 — 検証コールバックをフックします

ジェイルブレイクされていないデバイスの場合:Frida ガジェットを使って IPA を再パッケージ化し(objection patchipa)、再署名してサイドロードします。強化されたアプリでは、整合性チェックやアテステーションチェックに引っかかる可能性があります。

それでも失敗する場合

バイパススクリプトをさらに試すのではなく、以下を順番に確認してください。

  1. アプリがプロキシを使っていません。 一部のスタックはシステムのプロキシ設定をまったく無視します。iptables による透過的なリダイレクト、または VPN/tun ベースのプロキシで通信を強制します。
    adb shell su -c 'iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to <proxy>:8080'
    
  2. 証明書の透明性(CT)の強制。 一部の SDK は SCT を要求しますが、使用している CA にはありません。CT ライブラリを探し、CT 検証処理を個別にフックします。
  3. 相互 TLS。 サーバーがクライアント証明書を要求しています。アプリバンドルから .p12 またはキーストアを抽出し、そのパスワード(コードや strings に含まれていることがよくあります)を復元して、プロキシのクライアント証明書設定に読み込みます。これはピンニングとは別の仕組みであり、ピンニングのバイパスでは対処できません。
  4. ルート化/ジェイルブレイク、または Frida の検出。 アプリが計装を検出して処理を失敗させると、TLS の問題のように見えます。frida 文字列のスキャン、ポート 27042 のプローブ、/proc/self/maps の検査、または Play Integrity を確認してください。まず検出をバイパスしてから、ピンニングのバイパスを再試行します。
  5. 通信が HTTP ではありません。 WebSockets、gRPC、MQTT、または TLS 上のカスタムプロトコルです。HTTP プロキシには表示されないため、ソケットレイヤー、またはプロトコル対応プロキシでキャプチャします。
  6. 特定のホストにだけピンニングが適用されています。 分析用通信は成功するのに、API 通信は失敗します。プロキシに表示されるホストを比較して確認してください。

実際に成功したことを確認する

アプリが単にエラーを出さなくなっただけではなく、リクエストをエンドツーエンドで確認し、変更できることをもって、バイパスが成功したと判断します。

1. Proxy shows the API host, not just analytics and CDN traffic
2. Request and response bodies are readable
3. A modified request produces a different server response
4. Authenticated flows still work through the proxy

ステップ 1 だけが成功する場合、実際の API 通信は別の経路を通っており、フォールバック経路を見ているだけかもしれません。

却下すべき誤った理屈

  • 「バイパスに失敗した。アプリは強力なピンニングを使っているに違いない。」 原因は6つあり、そのうちの1つがピンニングです。症状の表から原因を特定してください。
  • 「CAをインストールしたので、TLSは動作するはずです。」 ユーザー証明書としてインストールした場合、Android 7以降では、証明書の使用を明示的に許可していないアプリでは動作しません。
  • 「objectionではピンニングが無効になったと表示された。」 objectionが対応しているJavaレイヤーのチェックは無効になります。ネイティブのピンニングには影響しません。
  • 「プロキシにトラフィックがないので、アプリはオフラインだ。」 それは、アプリがあなたのプロキシを使っていないことを意味します。
  • 「APKにパッチを当てればいい。」 再パッケージ化すると署名が無効になり、整合性チェックに引っかかります。システムCAとランタイムフックを優先し、パッチは最後の手段にしてください。
  • 「ピンニングをバイパスできたので、評価は完了だ。」 ピンニングのバイパスは準備であり、指摘事項ではありません。ピンニングは多層防御の一環であり、ピンニングがないことは重大度の低い注記です。実際の指摘事項は、その先にあるAPIにあります。

参考資料

  • testing-mobile-applications — これによって実施できる、より広範な評価
  • reversing-flutter-apps — Flutter独自のTLSスタックとプロキシの挙動
  • reversing-unity-il2cpp、reversing-react-native-apps — フレームワーク固有のスタックにフックする前のシンボル復元
  • testing-apis — トラフィックを確認できるようになった後に、実際の指摘事項が見つかる場所
  • objection、Frida、SSL Kill Switch 3、apktool、apksigner、Magisk

コメント