Skip to content

PacketProxyを利用したgRPC通信のMITM

funa-tk edited this page Sep 13, 2022 · 21 revisions

概要

最近のスマホゲームやスマホアプリでは、クライアント-サーバ間通信で gRPC が使われるケースが増えてきました。 プログラムのデバッグやセキュリティ診断のため、gRPC通信をMITM(閲覧や改ざん)できると、とても便利です。

本ドキュメントでは、PacketProxyを利用して、gRPC通信をMITMする方法について解説します。

github.comで公開されているgRPCのexamplesに含まれるHelloworldプログラムのクライアント-サーバ間のgRPC通信を、実際にPacketProxyでMITMしていきます。

準備

  1. Macを用意しWifiに接続します

    • IPアドレスは 192.168.11.4 とします
  2. Android端末を用意しWifiに接続します

    • IPアドレスは 192.168.11.20 とします
  3. gRPC公式のチュートリアルを参考にして、gRPCのHelloworldプログラムをビルドします

    • Helloworldサーバ
      • grpc/examples/cpp/helloworld/greeter_server
    • Helloworldクライアント(Androidアプリ)
      • grpc/examples/android/helloworld
      • ビルド済みアプリはここからダウンロードできます。
  4. ビルドしたHelloworldサーバをデプロイします

    • ホスト名: grpc.funacs.com
    • ポート番号:50051
  5. HelloworldクライアントをAndroidにインストールし、サーバにアクセスして動作確認します

    • grpc.funacs.com:50051にメッセージtestを送信し、Hello testを受信できれば問題なく動作しています

PacketProxyでgRPC通信をMITMする

それでは、Helloworldプログラムのクライアント-サーバ間のgRPC通信をMITMしていきます。

1. PacketProxyでHelloworldサーバを定義する

  • OptionsタブのServerAddボタンをクリックしてサーバを定義します。

    • サーバ名: grpc.funacs.com
    • サーバポート: 50051
    • SSL/TLSを利用: なし
    • Encodeモジュール: gRPC
      • バイナリデータをgRPCパケットと解釈するためにgRPCを指定します。
    • DNS偽装: なし
    • Upstream Http Proxy: なし
  • 最終的なサーバの設定は下記の通りです。

2. PacketProxyでListenポートを定義する

  • Listen Port: 50051

    • 基本的にはサーバのポート番号と同じポート番号(今回の例では50051)をListenするように設定しますが、クライアントから出力されたgRPCパケットを入力できるのであれば別のポート番号でも問題ありません。
  • Type: FORWARDER

    • ポートから入ってきたパケットを、指定したサーバに直接流すことができます。
  • 転送先サーバ: grpc.funacs.com:50051(gRPC)

    • 先ほど設定したサーバを指定します。
  • 署名に利用するCA証明書: 何でも良いです

    • FORWARDER タイプはSSL通信以外には関与しないので、何を指定しても影響はありません。
  • 最終的なListenポートの設定は下記の通りです。

3. HelloworldクライアントでgRPC通信する

  • HelloworldクライアントでgRPCメッセージをサーバに送ってみます。
    • ホスト名として、MacのIP(例:192.168.11.4)を指定します
    • ポート番号として、Listenポートとして定義した50051 を指定します。
    • メッセージとして、test を指定し送信ボタンをタップします。
  • すると、下記のようにPacketProxyでgRPC通信をMITMすることができました。

4. 【応用】 DNS偽装 (DNS Spoofing) を利用してみる

  • Helloworldクライアントにおいて、gRPCメッセージの送信先アドレスとして、PacketProxyが動作しているMacのIPアドレス(192.168.11.4)を指定できましたが、アプリによっては送信先の指定が難しいケースがあります。

  • その場合には、DNS偽装を利用し、grpc.funacs.com の名前解決の結果が MacのIP(192.168.11.4)になるようにすることで、Hellowordクライアントを改変することなく、送信先を正規のサーバからPacketProxyに変更することができます。

  • 設定方法は次の通りです。

DNS偽装にチェックを入れます。

DNSサーバを起動します。

Android端末のDNS設定で、DNSサーバとしてMacのアドレスを指定します。

以上でDNS偽装の設定は完了です。Androidクライアントで送信先をgrpc.funacs.comにしたまま、PacketProxyでMITMできます。

PacketProxyでSSL上のgRPC通信をMITMする

前章のgRPC通信は、SSLを利用していないgRPC通信でしたが、本章ではSSL上のgRPC通信もMITMしてみます。gRPC利用シーンとしては、SSL上でgRPCを利用することの方が一般的です。

1. サーバとクライアントをSSL対応する

  • HelloworldサーバとHelloworldクライアントをSSLを有効にしてビルドします。実際のやり方は割愛します。

  • ビルドができたら、SSL対応したサーバをデプロイします。

    • ホスト名: grpc.funacs.com
    • ポート番号:50052
  • SSL対応したクライアントはここからダウンロードできます。

2. PacketProxyの設定を変更します

  • Serverの設定をします。

    • サーバ名: grpc.funacs.com
    • サーバポート: 50052
    • SSL/TLSを利用: 有効
    • Encodeモジュール: gRPC
    • DNS偽装: なし
    • Upstream Http Proxy: なし
  • Listen Port の設定をします。

    • Listen Port: 50052
    • Type: SSL_FORWARDER
    • 転送先サーバ: grpc.funacs.com:50052(gRPC)
    • 署名に利用するCA証明書: PacketPorxy per-user CA
  • 最終的なPacketProxyの設定は、下記の通りです。

3. 動作確認する

  • SSL対応したHelloworldクライアントを利用して、正常にSSL対応したサーバにアクセスできることを確認します。

4. PacketProxyでMITMしてみる

  1. DNS偽装のチェックボックスを入れます。
  2. HelloworldクライアントでgRPCメッセージを送信します。
  3. しかし、gRPCメッセージを送信してみると、MITMできず、次のようにSSLのエラーが表示されてしまいます。
  • これは、PacketProxyから送られてくるサーバ証明書がHelloworldクライアントに信頼されていないのが原因です。

5. HelloworldクライアントにPacketProxyのサーバ証明書を信頼させる

  • サーバ証明書の信頼性は、署名に利用したCAの証明書が信頼されたものかどうかで決定されます。
  • PacketProxyのサーバ証明書は、PacketProxyのCA証明書で署名されているのですが、PacketProxyのCA証明書はAndroidのトラストストア(信頼されたCA証明書のデータベース)に保存されていないので、サーバ証明書は信頼できないものと判断されてしまいます。
  • 詳しい解説についてはPacketProxyがSSL-TLS通信をMITMできる仕組みをご確認ください。
  • 結論として、下記の4つの方法の内、いずれかの方法を使えば、クライアントにPacketProxyのサーバ証明書を信頼させることができます。
    1. PacketProxyのCA証明書を、Android端末のシステムのトラストストアに追加する
      • システムのトラストストアは、Read Onlyに設定されているため、追加するにはRoot化端末が必要となりますが、一番確実な方法になります。
    2. PacketProxyのCA証明書を、Android端末のユーザー定義のトラストストアに追加する
      • Androidにはユーザ定義のトラストストアというものが存在し、AndroidアプリがAndroid標準の通信ライブラリを使っている場合には、このトラストストアも参照してもらえます(Root化端末は必要ありません)。
      • しかし、今回のHelloworldアプリは、独自の通信ライブラリを利用しており、ユーザ定義のトラストストアを参照しないので、この方法ではMITMできませんでした。
    3. Helloworldクライアントのコードを修正して、サーバ証明書のverify関数が機能しないように改ざんします。
      • この修正をしたapkをここからダウンロードできます。
    4. Helloworldクライアントのコードを修正して、Androidシステムのトラストストアではなく、PacketProxyのCA証明書が入った独自のトラストストアを参照するようにします。
      • この修正は、余力のある方の課題とさせていただきます。

5. 再度 MITM に挑戦

  • TLSエラー対策をした後、再度gRPCメッセージを送ると、無事にMITMできるようになりました。

6. 【応用】 SSL_TRANSPARENT_PROXY を使ってみる

  • TypeとしてSSL_FOWRADERではなく、SSL_TRANSPARENT_PROXYに変更してみます。
    • SSL_TRANSPARENT_PROXYを利用する場合は、転送先サーバを指定する必要がありません。
  • 変更しても問題なくMITMできることが確認できると思います。
  • SSL_TRANSPARENT_PROXYを使うことで、転送先サーバを指定しなくても、SSLハンドシェイク時に交換されれるClientHelloメッセージ中のSNIフィールドに含まれているホスト名を参照して、自動的にPacketProxyが転送先を識別してくれるようになります。
  • 別サーバへの複数の通信がPacketProxyの同じListenポートに到着するような場合は、SSL_FORWARDERよりもSSL_TRANSPARENT_PROXYが便利です。