ポーカールームのトラフィックを傍受する方法、または「C#でMitM SSLプロキシを記述する」

かつて私は強迫観念を持っていました:どんな種類のポーカークライアントがそれをサーバーに送信するかを見る ご存知のように、大規模なポーカールームではデータ転送にSSLを使用しています。 非対称暗号化に基づくプロトコルは、MitM(中間者-中間者)という私に知られている1種類の攻撃の影響を受けるだけです。



SSL接続でMitMを実装するように設計された大量のソフトウェアから気がついたので、これらのツールの開発者または私から手を離して成長しているという結論に達しました。 しかし、このアイデアはひどく強迫観念であり、すべてを手動で行うことにしました。 このすべての結果に興味があるなら、猫をお願いします。





この記事は、MitM攻撃を実装するための簡単なツールを作成することで啓発されます。 MitMが何であるかを知らない人は、 ここでそれについて読むことができます



目的



Cake Pokerのクライアントは、私の長年の知り合いのために、テストの対象として選ばれました。 それはすべて、私がポーカークライアントを立ち上げ、古き良きリソースマネージャーに登ったところから始まりました。そこで、そこから約12の接続が見つかりました。



実験することで、常に保持されている接続を分離することができました。 私はMitM攻撃を行うためにそれを目の当たりにしました。



接続のソースはわかっており、最終的な目標はわかっています-lb6.playdata.co.uk。



トラフィックのリダイレクト



ここで、クライアントとサーバーの間をくさび止めする必要がありました。 必要がないので、特に注意が必要なものは何も発明しませんでした-127.0.0.1に関連するホストにドメイン名を追加しただけです。 lb6.playdata.co.ukがある場合、つまりlb1.playdata.co.ukとlb8.playdata.co.ukの両方があると推測することは難しくありません。 彼は彼らと同じことをし、ホストにログインしました。 私が理解するように、最終インスタンスは星の位置によって選択されます。 ポーカークライアントを起動すると、接続を待機してハングします。 いいね これは、トラフィックがマシンにリダイレクトされたことを意味します。 続けましょう。





プロキシ



次のタスクは、C#でプロキシを作成することでした。 はい、はい、将来のプログラムを準備するための簡単なプロキシ。 自転車を発明しないために、私はすぐに自分に適したソリューションをグーグルで探しました: Task Parallel Libraryを使用したC#のTCPプロキシ

彼はそれを少しリファクタリングし(ネストが多いときに嫌いです)、接続エンドポイントをハードコーディングして起動しました。 ポーカークライアントを起動します-すべてが動作します。 リソースマネージャーでは、ポーカークライアントからのトラフィックがプロキシに送信され、プロキシからサーバーに送信されて戻ってくることがわかります。



SSL上のMitM



次に、SSLにMitM攻撃を実装する必要があります。 これを2つの段階に分けます。最初の段階は、クライアントとプロキシの接続です。 2つ目はサーバーとのプロキシです。

最初の段階を実装するために、クライアントがプロキシに接続するとき、受信したデータをそれ以上送信しませんが、いわゆるハンドシェイク手順を開始します。 C#では、作成済みのNetworkStreamの上に構築されたSslStreamクラスのインスタンスを使用してこれを行うことができます。 作成時に、プロトコルに関する情報およびその他の特定の情報が送信されます。

その後、証明書をクライアントに転送します。 これは、SslStreamクラスのAuthenticateAsServerメソッドを使用して行われます。このメソッドでは、パスを証明書ファイルに渡す必要があります。



x509証明書ファイルは、Makecertユーティリティを使用して生成されたもので、Visual Studio開発コンソールから呼び出すことができます。 パラメーターに少し苦しむ必要がありましたが、うまくいきました。 使用方法の良い説明は次のとおりです。C#でのSSL通信 。 名前には、* .playdata.co.ukを指定します。 この名前は、ポーカークライアントが使用するすべてのドメインをカバーします。

makecert -n CN=MyCA -cy authority -a sha1 -sv “MyCA.pvk” -r “MyCA.cer” //   certmgr -add -all -c “MyCA.cer” -s -r LocalMachine Root //      makecert -n CN=*.playdata.co.uk -ic MyCA.cer -iv MyCA.pvk -a sha1 -sky exchange -pe -sr currentuser -ss my SslServer.cer //  
      
      





サーバーの秘密キーとCAキー(認証局)を生成し、キーに署名しました。 CAキーは、「信頼されたルート証明機関」に配置されます。 Windowsキービューアを使用してサーバーキーを見ると、システム上で実行されているポーカークライアントと同様に、システムがそれを有効と見なしていることがわかります。





受信した証明書へのパスをAuthenticateAsServerメソッドに渡します。 すべてがうまくいけば、クライアントからプロキシへのSSL接続を取得し、クライアントはそこにデータを送信します。 ここで、クライアントが要求に適切に答える必要があります。 これを行うには、MitM攻撃の第2段階、つまりプロキシからサーバーへのSSL接続を構築する必要があります。 また、サーバーへのNetworkStreamの上にSslStreamを構築し、AuthenticateAsClientメソッドを使用してログインします。 クライアントとサーバーのSSL接続から送信されるデータは相互に送信されます。

ハンドシェイクプロセスC#
 var certificate = new X509Certificate("SslServer.cer", "123"); var clientStream = new SslStream(client.GetStream(), false); clientStream.AuthenticateAsServer(certificate, false, System.Security.Authentication.SslProtocols.Default, false); var server = new TcpClient("200.26.205.63", 4520); var serverSslStream = new SslStream(server.GetStream(), false, SslValidationCallback, null); serverSslStream.AuthenticateAsClient("lb3.playdata.co.uk");
      
      







しばらくすると、リソースマネージャーで、プロキシとの間で送受信されるバイト数の不一致に気付くことができます。 これは、異なるキーを暗号化すると異なるサイズの結果が得られるためです。



おわりに



次は? 次に、データをテキストファイルに保存するコードを追加して、ポーカークライアントがサーバーに送信する内容を分析できるようにします。 実際には、MitMプロキシが作成されています。



少しブラックジャックを追加することは残っています。 たとえば、通過するトラフィックを解析したり、ユーザーカードを削除して送信したりするなど。

クライアントが送信するものを監視し、これを自分のアクションと相関させることが便利になるように、トラフィックパーサーをその場で作成しました。 私に起こったことのデモンストレーション:





ソースMITMプロキシ
 using System; using System.Diagnostics; using System.IO; using System.Net; using System.Net.Security; using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using ConnectionAnalizer; namespace MITMProxy { class Program { static readonly TcpListener Listener = new TcpListener(IPAddress.Any, 4520); const int BufferSize = 4096; static void Main() { Listener.Start(); new Task(() => { while (true) { var client = Listener.AcceptTcpClient(); new Task(() => AcceptConnection(client)).Start(); } }).Start(); Debug.WriteLine("Server listening on port 4502. Press enter to exit."); Console.ReadLine(); Listener.Stop(); } private static void AcceptConnection(TcpClient client) { try { var certificate = new X509Certificate("SslServer.cer", "123"); var clientStream = new SslStream(client.GetStream(), false); clientStream.AuthenticateAsServer(certificate, false, System.Security.Authentication.SslProtocols.Default, false); var server = new TcpClient("200.26.205.63", 4520); var serverSslStream = new SslStream(server.GetStream(), false, SslValidationCallback, null); serverSslStream.AuthenticateAsClient("lb3.playdata.co.uk"); new Task(() => ReadFromClient(client, clientStream, serverSslStream)).Start(); new Task(() => ReadFromServer(serverSslStream, clientStream)).Start(); } catch (Exception ex) { Console.WriteLine(ex.Message); throw; } } private static bool SslValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslpolicyerrors) { return true; } private static void ReadFromServer(Stream serverStream, Stream clientStream) { var message = new byte[BufferSize]; while (true) { int serverBytes; try { serverBytes = serverStream.Read(message, 0, BufferSize); clientStream.Write(message, 0, serverBytes); } catch { break; } if (serverBytes == 0) { break; } } } private static void ReadFromClient(TcpClient client, Stream clientStream, Stream serverStream) { var message = new byte[BufferSize]; var fileInfo = new FileInfo("client"); if (!fileInfo.Exists) fileInfo.Create().Dispose(); using (var stream = fileInfo.OpenWrite()) { while (true) { int clientBytes; try { clientBytes = clientStream.Read(message, 0, BufferSize); } catch { break; } if (clientBytes == 0) { break; } serverStream.Write(message, 0, clientBytes); memoryStream.Write(message, 0, clientBytes); stream.Write(message, 0, clientBytes); } client.Close(); } } } }
      
      








All Articles