優しい言葉を持っている読者の多くは、 コサックの一連のゲーム、数時間に及ぶ戦い、軍事的なトリック、比類なきサウンドトラックを思い出すと思います。
15年後、彼らは帰国し、現在はオンラインになっています。新しいバージョンの問題と脆弱性については、この記事で説明します。
免責事項
すべての調査は良い目的のためだけに行われ、 著作権者と開発者は発見された問題について意図的に通知され、記事は本質的に有益であり、記載された問題の積極的な使用を求めていません。
ネットワークパケット構造
それはすべて、「ネットワークデータはどのように暗号化されるのか」という簡単な質問から始まりました。最初のパケットをキャッチした後、答えは明らかになりました。 XOR操作、署名、誠実で真実のバイトはありません。
同時に(可能な分析の単純さを実現した後)、さらに進んで、サーバーの基本的な操作(ログイン、登録、パスワード回復)を再作成することを決定しました。これを理解する必要がありました。
次のダースのパッケージをキャッチした後、「帽子」は明るく明確になりました。
struct NetPacketHeader { unsigned int Size; // ( ) unsigned char Direction; // unsigned char Mode; // - unsigned int SessionId0; // , unsigned int SessionId1; // , , : };
シリアル化の主な機能は、同じ方法で特定されました。
- 書き込みと読み取りは、特別なクラスを使用して行われます(最初の推測は、それらが単に縮小された構造であるということでした)
- 文字列には2つのタイプがあります-小と大、文字列のサイズは開始前に示され、最初の文字は1バイト、2番目の文字は2バイト
- 数値データはそのまま記録されます(浮動が満たされませんでした)
- セパレータとして機能するゼロで厳密に埋められた特別なブロックがあります:4バイト-配列の終わり、6バイト-レコードの終わり(最初はコンパイラからの構造のオフセットであると思われました)
そして、サーバーのリストは、名前( data \ resources \ servers.dat )による通常の検索で見つかりました。私たち自身のプロダクションの読み取り可能なスクリプトは、データの管理者であることがわかりました。
これらの手順が完了すると、パッケージの分解は忍耐力と注意力だけにまで低下し始めました。たとえば、
3D 00 00 00 9A 01 00 00 00 00 00 00 00 00 07 31 2E 30 2E 30 2E 37 05 31 2E 32 2E 33 14 61 61 61 61 61 61 61 61 61 61 61 40 67 6D 61 69 6C 2E 63 6F 6D 0A 61 61 61 61 61 61 61 61 61 61 61 61 0E 39 30 30 30 2D 38 30 30 30 2D 35 30 30 30
サイズ:3D 00 00 00(パケットの合計は75バイトですが、61バイトが本文で、他の14バイトがヘッダーです)
方向:9A
モード:01
SessionId0:00 00 00 00
SessionId1:00 00 00 00
VersionStringSize:07
VersionString:31 2E 30 2E 30 2E 37(1.0.0.7)
UpdateStringSize:05
UpdateString:31 2E 32 2E 33(1.2.3)
EmailStringSize:14
EmailString:61 61 61 61 61 61 61 61 61 61 61 40 67 6D 61 69 6C 2E 63 6F 6D(aaaaaaaaaa@gmail.com)
PasswordStringSize:0A
PasswordString:61 61 61 61 61 61 61 61 61 61 61 61(aaaaaaaaaaa)
GameKeyStringSize:0E
GameKeyString:39 30 30 30 2D 38 30 30 30 2D 35 30 30 30(9000-8000-5000)
これは、クライアントからサーバーに入るための要求のパケットです。
ロビーで働く
短時間で、( asioに基づいて)プリミティブサーバーが作成され、元のクライアントと完全に通信できる小さなサーバーと1時間で通信できるようになりました。
- ログイン要求の処理
- プロセス登録リクエスト
- パスワード回復リクエストを処理する
- プライベートおよびパブリックチャットメッセージを処理する
- サーバー上のユーザーのリストとロビーを表示する
このような迅速な実装は、どのコーヒーよりも元気になり、残りの時間をロビーで作業することになりました。
- 作成/更新/削除
- ユーザー同期(フラグの色、国など)
- 作成者によるスロットの開閉
最初の段落から、それほど明確ではない(最初は)問題が始まりました。
パッケージの3つのペア(クライアントリクエストとサーバーレスポンス)-作成、更新(削除を含む)、およびパブリックロビーへの入り口をキャッチして分解することが判明しました。
後者は頭痛を引き起こし、パブリックロビーへの入り口は突然鳴りましたが、プライベートロビーは明確ではありませんでした。データの正確性を確認するためにユーザーが入力したパスワードを探す場所は明確ではありませんでした。プライベートロビーに入り、帽子と1つの全体(ロビーの作成者のセッションの識別子)のみが含まれているため、説明のためにフードの下に登らなければなりませんでしたが、Delphiコードのコンパイルに慣れていない私の目は賢明なものを見つけませんでした。
最終的に、サーバーはロビーのパスワードを「完全に」処理することは一切ありません。つまり、クライアントはパスワードを最も純粋な形式で受け取るため、理論的にはロビーと元のサーバーにアクセスできました。
理論は3つのステップで証明されています。
希望の部屋のパスワードを取得する
接続確認
ユーザーの対応
そうです-private == public。
夜の議題の残りの項目は比較的簡単に通過し、ユーザーの同期は単純なミラーでした。1人のユーザーからロビーのすべての参加者へ、スロットを閉じる(たとえば、ロビーから参加者を追い出す)ことはほとんど心配せず、サーバーはスロットが閉じた場合にスロットを閉じる要求を受け入れます私は参加者で忙しかった-彼を追い出し、アラートを送信しますが、クライアント側のパッケージを無視すると-視覚的にコミュニケーションを失うことなくロビーに留まるため、残りの参加者がゲームを開始するのに問題が発生します-良い 私は失敗した時刻から取得された答えを質問します。
ロビーの作成者のPC名の問題、それが作成者!=ホストの場合、参加者になぜそれが配られるのかという問題は、まだ答えられていない質問であり、小さな懸念ではありませんでした。
夜の終わりに、ゲームへの入り口にたどり着くことができました。ゲームプレイを同期するための10回の試行のうち、成功したのは1人だけで、一部だけが成功しました。より多く。
まとめると
ネットワークアプリケーションを一般に公開しなければならなかったプログラマーは、主なルールを感じました。クライアントを決して信用しないでください。
各クライアントアクションにはサーバーの承認が必要です。さもなければ、プロジェクト全体を台無しにせず、最も予期しない時間に爆発する地雷を敷設すると、大きなリスクがあります。良い例ですが、悪いネットワーク作業-MU Onlineゲーム、それは10年以上前です、ゲームパッケージの複製(パッケージ操作を使用)のささいな問題により、個人ストアの機能を無効にする必要がありました。
説明された例から理解できるように、議論のための別のトピックは個人データの保存です:このようなアプローチ、クリーンバイト、特に文字列の転送は、情報分野で働いている企業にとって大きな罪であり、アカウント盗難の
→ GitHubの例