-
Notifications
You must be signed in to change notification settings - Fork 37
PacketProxyを利用したgRPC通信のMITM
最近のスマホゲームやスマホアプリでは、クライアント-サーバ間通信で gRPC が使われるケースが増えてきました。 プログラムのデバッグやセキュリティ診断のため、gRPC通信をMITM(閲覧や改ざん)できると、とても便利です。
本ドキュメントでは、PacketProxyを利用して、gRPC通信をMITMする方法について解説します。
github.comで公開されているgRPCのexamplesに含まれるHelloworldプログラムのクライアント-サーバ間のgRPC通信を、実際にPacketProxyでMITMしていきます。
-
Macを用意しWifiに接続します
- IPアドレスは 192.168.11.4 とします
-
Android端末を用意しWifiに接続します
- IPアドレスは 192.168.11.20 とします
-
gRPC公式のチュートリアルを参考にして、gRPCのHelloworldプログラムをビルドします
- Helloworldサーバ
- grpc/examples/cpp/helloworld/greeter_server
- Helloworldクライアント(Androidアプリ)
- grpc/examples/android/helloworld
- ビルド済みアプリはここからダウンロードできます。
- Helloworldサーバ
-
ビルドしたHelloworldサーバをデプロイします
- ホスト名: grpc.funacs.com
- ポート番号:50051
-
HelloworldクライアントをAndroidにインストールし、サーバにアクセスして動作確認します
- grpc.funacs.com:50051にメッセージ
test
を送信し、Hello test
を受信できれば問題なく動作しています
- grpc.funacs.com:50051にメッセージ

それでは、Helloworldプログラムのクライアント-サーバ間のgRPC通信をMITMしていきます。
-
Options
タブのServer
のAdd
ボタンをクリックしてサーバを定義します。-
サーバ名
:grpc.funacs.com
-
サーバポート
:50051
-
SSL/TLSを利用
:なし
-
Encodeモジュール
:gRPC
- バイナリデータをgRPCパケットと解釈するために
gRPC
を指定します。
- バイナリデータをgRPCパケットと解釈するために
-
DNS偽装
:なし
-
Upstream Http Proxy
:なし
-
-
最終的なサーバの設定は下記の通りです。

-
Listen Port
:50051
- 基本的にはサーバのポート番号と同じポート番号(今回の例では
50051
)をListenするように設定しますが、クライアントから出力されたgRPCパケットを入力できるのであれば別のポート番号でも問題ありません。
- 基本的にはサーバのポート番号と同じポート番号(今回の例では
-
Type
:FORWARDER
- ポートから入ってきたパケットを、指定したサーバに直接流すことができます。
-
転送先サーバ
:grpc.funacs.com:50051(gRPC)
- 先ほど設定したサーバを指定します。
-
署名に利用するCA証明書
: 何でも良いです-
FORWARDER
タイプはSSL通信以外には関与しないので、何を指定しても影響はありません。
-
-
最終的なListenポートの設定は下記の通りです。

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

- すると、下記のようにPacketProxyでgRPC通信をMITMすることができました。

-
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できます。

前章のgRPC通信は、SSLを利用していないgRPC通信でしたが、本章ではSSL上のgRPC通信もMITMしてみます。gRPC利用シーンとしては、SSL上でgRPCを利用することの方が一般的です。
-
HelloworldサーバとHelloworldクライアントをSSLを有効にしてビルドします。実際のやり方は割愛します。
-
ビルドができたら、SSL対応したサーバをデプロイします。
- ホスト名: grpc.funacs.com
- ポート番号:50052
-
SSL対応したクライアントはここからダウンロードできます。
-
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の設定は、下記の通りです。

- SSL対応したHelloworldクライアントを利用して、正常にSSL対応したサーバにアクセスできることを確認します。
- DNS偽装のチェックボックスを入れます。
- HelloworldクライアントでgRPCメッセージを送信します。
- しかし、gRPCメッセージを送信してみると、MITMできず、次のようにSSLのエラーが表示されてしまいます。

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


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