
通常の春の日に、コンピューターサイエンスの試験の「準備」をしているときに、Facebookの脆弱性に関する記事に出会いました。Facebookでは、15,000ドルが支払われたソーシャルネットワーク上のすべてのアカウントをハッキングできました。 この脆弱性の本質は、会社のテストドメインの復旧コードを反復処理することでした。 VKontakteの実際の悪化は何だと思いましたか? そして、私は彼らと同様のトリックをしようとすることにしました。 Webバージョンはすでにかなりよく研究されていることを知っているので、Androidクライアントは被害者になっていたはずです。
トラフィックを見る
まず、ページ回復プロセス中にアプリケーションがネットワークに送信する情報を調べたいと思いました。 この問題のアシスタントはFiddlerで、 公式ドキュメントに記載されているように、私はそれとAndroidデバイスを構成しました。 したがって、デバイスからのすべてのHTTP / HTTPS要求はFiddlerで利用可能になります。 これで、アプリケーションでVKontakteアカウントからログアウトし、「パスワードをお忘れですか?」ボタンをクリックしてください。 電話番号を入力すると、アプリケーションは2つのHTTPS要求を送信します。 後者は回復コード付きのSMSを送信する責任があるため、特に価値があります。

一部のクエリパラメータには特に注意が必要です。
電話-SMSの送信先の番号変更してリクエストを送信しようとしても失敗しました。 「署名」として機能する「署名」パラメーターは、その生成方法を妨害します。これについては、後で説明します。
session_id-ランダムに生成された回復操作セッション
さらなる分析のために、アプリケーションでは、ランダムリカバリコードを導入し、ネットワークアクティビティを監視し続けます。 次のリクエストが表示され、入力されたコードの正確性がチェックされます。 コードはランダムであるため、検証は失敗しました。

正直なところ、この時点で、回復コードのソートを開始し、「code」パラメーターの値を変更したかったのです。 残念ながら、このリクエストは「署名」による変更からも保護されています。 この署名の生成方法を理解する必要があります。
リバースエンジニアリング:逆コンパイル
最初の分析では、VKontakteアプリケーションの逆コンパイルを試みることができます。 これにより、Javaソースコードの一部を取得できます。
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」にある制限に依存しています。

ブルートフォースについて
私はあらゆる可能な方法で限界を回避しようとしました。 IPアドレス、リクエストパラメータ、電話番号を変更しましたが、コードを5回以上入力することはできませんでした。
そして、ここでは、ほぼ偶然に、同じsession_idを使用して、回復コードを2つの異なる番号に送信することにしました。 両方の電話で同じSMSを見たとき、私の驚きは礼拝堂ではありませんでした。 仕組みは次のとおりです。
したがって、攻撃は判明しました。
- サブスクライバーAにsession_id CでSMSを送信する要求を送信します。彼はコード1234を受け取ります
- サブスクライバーBにsession_id CでSMSを送信する要求を送信すると、彼はコード1234を受け取ります
- これで、サブスクライバAがサブスクライバBの電話番号を知っている場合、ページを復元できます。 同様のsession_idを持つ最後のSMSが送信された番号のページのみを復元することができました。
おわりに
脆弱性を発見した直後に、HackerOneに関するレポートを書きました。 17時間後、この脆弱性は排除されました。 数日後、私は2,000ドルを支払われました。 この脆弱性により、ソーシャルネットワーク上のほとんどのアカウントがハッキングされ、2要素認証を持つアカウントのみが安全でした(電話番号では復元できません)。 レポート
PSコンピュータサイエンスの試験は97点を通過しました。 残念ながら、残りのアイテムはそれほど成功していません。
UPD: libverifyライブラリはICQでも使用されていたため、脆弱性もそこに存在していました。 バグが多いICQプログラムでは、1000ドルの追加料金が支払われました。 レポート