VKontakteの脆弱性:ページ復旧コードを含むメッセージを他の誰かの番号に送信します





通常の春の日に、コンピューターサイエンスの試験の「準備」をしているときに、Facebookの脆弱性に関する記事に出会いました。Facebookでは、15,000ドルが支払われたソーシャルネットワーク上のすべてのアカウントをハッキングできました。 この脆弱性の本質は、会社のテストドメインの復旧コードを反復処理することでした。 VKontakteの実際の悪化は何だと思いましたか? そして、私は彼らと同様のトリックをしようとすることにしました。 Webバージョンはすでにかなりよく研究されていることを知っているので、Androidクライアントは被害者になっていたはずです。



トラフィックを見る



まず、ページ回復プロセス中にアプリケーションがネットワークに送信する情報を調べたいと思いました。 この問題のアシスタントはFiddlerで、 公式ドキュメントに記載されているように、私はそれとAndroidデバイスを構成しました。 したがって、デバイスからのすべてのHTTP / HTTPS要求はFiddlerで利用可能になります。 これで、アプリケーションでVKontakteアカウントからログアウトし、「パスワードをお忘れですか?」ボタンをクリックしてください。 電話番号を入力すると、アプリケーションは2つのHTTPS要求を送信します。 後者は回復コード付きのSMSを送信する責任があるため、特に価値があります。







一部のクエリパラメータには特に注意が必要です。

電話-SMSの送信先の番号

session_id-ランダムに生成された回復操作セッション
変更してリクエストを送信しようとしても失敗しました。 「署名」として機能する「署名」パラメーターは、その生成方法を妨害します。これについては、後で説明します。



さらなる分析のために、アプリケーションでは、ランダムリカバリコードを導入し、ネットワークアクティビティを監視し続けます。 次のリクエストが表示され、入力されたコードの正確性がチェックされます。 コードはランダムであるため、検証は失敗しました。







正直なところ、この時点で、回復コードのソートを開始し、「code」パラメーターの値を変更したかったのです。 残念ながら、このリクエストは「署名」による変更からも保護されています。 この署名の生成方法を理解する必要があります。



リバースエンジニアリング:逆コンパイル



最初の分析では、VKontakteアプリケーションの逆コンパイルを試みることができます。 これにより、Javaソースコードの一部を取得できます。



方法
1. dex2jarjd-guiをダウンロードして解凍します

2. apkアプリケーションを通常のアーカイブとして開き、.dexファイルをd2j-dex2jar.batに「ドラッグ」します



jd-guiで受信したすべての.jarファイルを開きます。 そして、ためらうことなく、「署名」という行を検索します。







Mail.Ruによるlibverifyライブラリは明らかに一般リストから外れています。 見て間違いではありませんが、生成された文字列は以前のリクエストからのURLに非常に似ています。



localObject3 = String.format(Locale.US, "%s%s?%s&signature=%s", new Object[] { d(), e(), localObject3, URLEncoder.encode(ru.mail.libverify.utils.mb(f() + (String)localObject4 + ru.mail.libverify.utils.mc(ab())), "UTF-8") });
      
      





このライブラリは、隠蔽によるセキュリティの最高の伝統で作られており、すべてのコードは確実に難読化されています。 そのため、jd-guiを使用して、「署名」の背後にある不明な文字列からどのMD5ハッシュが隠れているのかを見つけることができました。







リバースエンジニアリング:分解



ru.mail.libverify.utils.mb()関数に文字列が入力する内容を調べる必要がありました。 これを行う最も簡単な方法は、アプリケーションコードをわずかに変更することです。 さて、試してみましょう。 まず、次のコマンドでapktoolを使用します。



 apktool.jar d vk.apk -r ( -r   )
      
      





さて、smaliコードのあるフォルダーで、MD5生成が行われるファイルを見つけます。 私の場合、パスは次のとおりでした:smali_classes3 \ ru \ mail \ libverify \ utils \ m.smali。 必要なメソッドに渡します:



 ... .method public static b(Ljava/lang/String;)Ljava/lang/String; .locals 8 .param p0 # Ljava/lang/String; .annotation build Landroid/support/annotation/NonNull; .end annotation .end param :try_start_0 const-string/jumbo v0, "UTF-8" invoke-virtual {p0, v0}, Ljava/lang/String;->getBytes(Ljava/lang/String;)[B :try_end_0 .catch Ljava/io/UnsupportedEncodingException; {:try_start_0 .. :try_end_0} :catch_2 move-result-object v0 :try_start_1 const-string/jumbo v1, "MD5" invoke-static {v1}, Ljava/security/MessageDigest;->getInstance(Ljava/lang/String;)Ljava/security/MessageDigest; move-result-object v1 invoke-virtual {v1}, Ljava/security/MessageDigest;->reset()V invoke-virtual {v1, v0}, Ljava/security/MessageDigest;->update([B)V invoke-virtual {v1}, Ljava/security/MessageDigest;->digest()[B move-result-object v0 ...
      
      





学習される文字列は、最初のパラメーターレジスター(p0)で関数に渡されました。 したがって、取得するには、たとえばLogcatのどこかにパラメーターを出力する必要があります。 コードに数行を追加します。



 ... .method public static b(Ljava/lang/String;)Ljava/lang/String; .locals 8 .param p0 # Ljava/lang/String; .annotation build Landroid/support/annotation/NonNull; .end annotation .end param # PATCH # String v0 = "vk-research"; const-string/jumbo v0, "vk-research" # Log.d(v0, p0),  p0   invoke-static {v0, p0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I :try_start_0 const-string/jumbo v0, "UTF-8" ...
      
      





変更を保存した後、apktoolを使用してアプリケーションをビルドします。



 apktool.jar b vk -o newvk.apk
      
      





ここでapkに署名する必要があります。APKSignerを使用しました。



その後、以前に元のアプリケーションをアンインストールしてから、変更したVKクライアントをインストールして実行できます。 デバイスからlogcatを取得するには、 Android Debug Bridgeを使用します。 AndroidデバイスをUSB経由で接続し、コマンドを順番に実行します。



 adb devices adb logcat
      
      





デバイスに参加してログを見る機会が得られたら、もう一度「パスワードを忘れましたか?」をクリックして、電話番号を入力します。 adbウィンドウにエントリが表示されます。







ハッシュされた行は、URLの一部、要求パラメーター、および別のハッシュ行(506e786f377863526a7558536c644968)で順番に接着されていることが明らかになります。 そして今、「署名」生成アルゴリズムを知っているので、「署名済み」リクエストの送信を開始できます。



リサーチ



研究のために、C#で簡単なプログラムを作成しました。このプログラムは、SMSを送信する要求を送信し、コードを入力しようとしました。 それを使用して、ランダムな回復コードを入力しました。 しかし、予想の限界に達しました。







その後、制限がなくなることを期待して、SMSを再送信しようとしました。 残念ながら、メッセージが来ましたが、まだコードを入力できませんでした。







別のsession_idでリクエストを送信することにしました。SMSには他の回復コードが付属していましたが、今は「ATTEMPTLIMIT」ではなく「RATELIMIT」にある制限に依存しています。







ブルートフォースについて
着信SMSを見ると、コードの一般的な機能に気付きました。 復旧コードは4桁で構成され(執筆時点では6桁増加)、同じ桁は近くにありませんでした。 これは、合計で最大6500の可能なコードオプションです。 Facebookでコードを推測するなど、コードを推測するのに5回試行することは可能だと思いました 。 しかし、彼はこのベンチャーを延期しました。



私はあらゆる可能な方法で限界を回避しようとしました。 IPアドレス、リクエストパラメータ、電話番号を変更しましたが、コードを5回以上入力することはできませんでした。



そして、ここでは、ほぼ偶然に、同じsession_idを使用して、回復コードを2つの異なる番号に送信することにしました。 両方の電話で同じSMSを見たとき、私の驚きは礼拝堂ではありませんでした。 仕組みは次のとおりです。





プロキシのため、追加の検証が必要です。



したがって、攻撃は判明しました。



  1. サブスクライバーAにsession_id CでSMSを送信する要求を送信します。彼はコード1234を受け取ります
  2. サブスクライバーBにsession_id CでSMSを送信する要求を送信すると、彼はコード1234を受け取ります
  3. これで、サブスクライバAがサブスクライバBの電話番号を知っている場合、ページを復元できます。 同様のsession_idを持つ最後のSMSが送信された番号のページのみを復元することができました。


おわりに



脆弱性を発見した直後に、HackerOneに関するレポートを書きました。 17時間後、この脆弱性は排除されました。 数日後、私は2,000ドルを支払われました。 この脆弱性により、ソーシャルネットワーク上のほとんどのアカウントがハッキングされ、2要素認証を持つアカウントのみが安全でした(電話番号では復元できません)。 レポート



PSコンピュータサイエンスの試験は97点を通過しました。 残念ながら、残りのアイテムはそれほど成功していません。



UPD: libverifyライブラリはICQでも使用されていたため、脆弱性もそこに存在していました。 バグが多いICQプログラムでは、1000ドルの追加料金が支払われました。 レポート



All Articles