- WeChatへの短い余談。
- プラットフォーム、アプリケーションバージョン、使用されるユーティリティ、および実行可能ファイルの復号化について。
- 約2つのプロトコル(古いものと新しいもの)。
- オブジェクトのシリアル化について。
- 暗号化とキー交換を使用。
- ヘッダーとハッシュ関数について。
- 発見された脆弱性について。
中国企業TencentのWeChatメッセンジャー
WeChatは、世界で2番目に人気のインスタントメッセンジャーです。 ユーザー数に関する公式データを見つけることは非常に困難ですが、大まかな見積もりを行うことができます。
世界中で約8億人のユーザーと話していますが、その90%は中国にいます。
中国では、ほとんどすべてのスマートフォン所有者がWeChat(中国の自己名-Weixin)を使用しています。これは、これが古典的な意味でのメッセンジャーであるだけでなく、モバイルウォレット、組み込みブラウザー、オンラインストアなどを含むシステム全体であるためです。 領収書の代金を支払うか、メッセンジャーを通じて医師に予約することができます。
緊急の課題は、中国で積極的に働いている顧客のCRMシステムとWeChatの統合でした。 これは、中国でのWeChatの普及と公式APIの欠如により促進されました。 中国でのSMS通知は高価であり、最も重要なことには、不安定であることに加えて、「読み取り」ステータスがありません。 APIを使用する顧客は、CRMシステムを通じて、商品の配達、新規注文、その他のサービス情報についてWeChatユーザーに通知することができます(顧客の番号から情報を受け取るように登録されています。これについては以下で説明します)。
プロトコル研究
iOS向けメッセンジャーの32ビットバージョンのコードを理解するために、メッセンジャーを「内部から」調査することが決定されました。 iOS 7.2.1を搭載した古く使い古したiPhone 4Sがあります。
MITMとして、 Burp Suite Free Editionを使用します。
アプリケーションをダウンロードし、すばらしいdumpdecryptedユーティリティを使用して実行可能ファイルを解読します。
WeChatリバースエンジニアリングが開始された時点で、バージョン6.3.13が関連していました。
これで、デバイスからファイルをコピーし、IDAで逆アセンブルし、開始できます。
登録例を使用した鍵交換アルゴリズムを検討してください。
アプリケーションを起動し、登録用の電話番号を入力する提案を確認します。
電話番号を入力し、MITMでアドレスhkshort.weixin.qq.com/bindopmobileforregへのHTTPリクエストを確認します。
リクエストの本文は次のもので構成されます。
- ヘッダー(黄色);
- 暗号化されたデータ(青色)。
コードの非常に長い静的分析の後、クライアントがシリアル化されたオブジェクトを介してサーバーと通信していることがわかりました。 オブジェクトは、 プロトコルバッファライブラリを使用してシリアル化されます。
最初のメッセージは、次のデータを転送します。
- 電話番号
- 電話システムの言語
- デバイスID
- クライアントバージョン。
- 応答を解読するためのキー(ランダムな16バイト)。
- その他のデータ(私たちにとってあまり面白くない)。
メッセージはシリアル化され、RSAアルゴリズムを使用してサーバーの公開キーで暗号化されます。 サーバーの応答は、要求で渡されたAESキーで解読されます。 応答は、すべてが正常であるか、エラーが示されていることを示します。
SMSを取得してコードを入力します。 SMSからのコードのみで同じ要求が形成され、応答でいわゆるチケットを取得します。 チケットを取得したら、登録リクエストを送信できます。 名前を入力し、[OK]をクリックします。
キー交換は、最終フィールド「secp224r1」上で楕円曲線を使用するDiffie-Hellmanアルゴリズムに従って行われます。 秘密鍵と公開鍵が生成され、リクエストがアドレスhkshort.weixin.qq.com/newregに送信されます。 サーバーはそのキーを生成し、いわゆるCryptUinとServerIDも提供します。これについては後で説明します。 応答として、サーバーは公開キーとセッションキーを送信します。
これでサーバーの公開キーを取得し、セッションキーを復号化する共有キーを計算します。 この時点から、クライアントとサーバーの通信は、128ビットのキー長の対称AESアルゴリズムを使用して実行されます。
一般に、接続のセットアップとキー交換アルゴリズムには超自然的なものはありません。 データを正しく暗号化し、キーを交換することはタスクの半分であり、各メッセージのヘッダーを正しく作成する必要もあります。 データが正しくシリアル化および暗号化されていても、ヘッダーが正しくない場合、サーバーはエラー応答を送信します。 タイトルは次のようになります。
bfa65f16050520252cb6b770021001754cc8fd5e57e085457800fb0242420001aeb890f40b010a0080
次に、各フィールドについて詳しく説明します。
1.プロトコル識別子。 各パケットはこのバイトで始まります。
2.フラグ。 SrvIDの長さ、ヘッダー自体の長さ、および元のメッセージの圧縮に関する情報を保存します。
メッセージが圧縮されていない場合、圧縮フラグは0b10に設定され、そうでない場合は0b01に設定されます。
3.アプリケーションのバージョン。 コメントはありません。
4. CryptUin。 登録後、各アカウントには4バイトの一意の識別子が割り当てられます。
5. SrvID。 現在のセッションのID。 新しい接続ごとに変更されます。
6. uiCgi。 チームコード。 各チームには独自のuiCgiとURLがあります。 たとえば、bindopmobileforregコマンドの場合、uiCgiは0x91で、newregの場合は0x7eです。 ほとんどの数値は、次のアルゴリズムを使用してパックされます。
private static void Write7BitEncodedInt(BinaryWriter store, int value) { Debug.Assert(store != null); // Write out an int 7 bits at a time. The high bit of the byte, // when on, tells reader to continue reading more bytes. uint v = (uint)value; // support negative numbers while (v >= 0x80) { store.Write((byte)(v | 0x80)); v >>= 7; } store.Write((byte)v); }
この例では、uiCgiは0x17bで、パッケージ形式はfb02です。
7.元のメッセージの長さ。 シリアル化されたデータの長さ。 番号もパッケージ化されていますが、0x80より小さいため、変更されません。
8.圧縮されたメッセージの長さ。 したがって、圧縮は実行されなかったため、前の圧縮と違いはありません。
9.フラグ。
10.ハッシュ。
次のように計算されます。
hash1 = md5(cryptUin.shareKey);
hash2 = md5( strlen(data).shareKey.hash1.data)
resultHash = adler32(hash2)
sharedKey
は、ハンドシェイク中に受信した共有キーです。
ハッシュもパッケージ化されています。
11.フラグ。 これらの旗の意味は謎のままでしたが、静的であるため、それらを個別に研究する意味はありませんでした。
現在、別のプロトコルがすでに使用されています。これについては、以降の出版物で説明します。 実際、それは上記のラッパーです。 古いプロトコルは引き続きサポートされています-これを行うには、デバッガーでMmtlsCtrlFlagフラグをクリアする必要があります。
スパム保護。
スパムから保護するために、ユーザーは「友情の確認」オプションを有効にできます。 この場合、あなたが友達であることを確認した後にのみ、彼にメッセージを書くことができます。 友人確認リクエストには、ウェルカムメッセージが含まれる場合があります。
多数のウェルカムメッセージを送信すると失敗します。 15個の要求を送信した後、他のすべての要求は送信を停止し、キューに入れます。 16番目のリクエストは、前の15人の誰かがあなたを友達として追加したときにのみ送信されます。 しかし、ユーザーはこれを知らず、アプリケーションインターフェイスもそれを報告しません。 トラフィック分析と実験の助けを借りて調べることができました。
その過程で、興味深い脆弱性も発見されました。 アプリケーションには、電話番号でユーザーを検索する機能があります。 サーバーは、そのようなユーザーがいないと答えるか、そのユーザーに関する情報(名前、性別、都市、写真など)を返します。 ただし、これらのリクエストを頻繁に送信すると、サーバーは「Too many retry。」というメッセージで応答します。 後でもう一度やり直してください。」 ユーザーが存在しない場合、サーバーは常にこれを報告し、応答が「試行回数が多すぎます。 後でもう一度試してください-これはユーザーが存在することを意味します。 これを使用して、ユーザーベースを構築できます。 ちなみに、WeChatを使用する非常に「面白い」ユーザーがいましたが、通常の方法では見つけることができず、招待状を送信することさえ不可能です。おそらくこれらは中国の「特別な」人々です。 「興味深い」番号の登録を要求した場合でも、サーバーはこのユーザーがすでに登録されていることを通知し、SMSではなくアカウントの復元を提案します。
20,000ストリームを使用すると、1日1つのアカウントから中国からWeChatデータベース全体を収集することができ、アカウントはブロックされません。
また、ユーザー間でエンドツーエンドの暗号化が行われないことを個別にお知らせしたいと思います。 メッセージは対称キーでのみ暗号化され、サーバーはメッセージを復号化し、受信者の対称キーで再暗号化して受信者に送信します。
この記事は入門記事であり、Habrコミュニティの関心がある場合、WeChatのイベント(オブジェクトのシリアル化など)は別の記事に値するため、以下のWeChatの出版物が表示される場合があります。