1 ポイント 投稿者 GN⁺ 2025-06-10 | まだコメントはありません。 | WhatsAppで共有
  • GoogleのNo-JSユーザー名復旧フォームが、電話番号と表示名の組み合わせを確認できてしまったことで、特定のGoogleユーザーの完全な電話番号を特定できる攻撃チェーンが成立していた
  • 核心は /signin/usernamerecovery/signin/usernamerecovery/lookupリダイレクトの違いで、アカウントなしとアカウントありの状態を見分けられた点にある
  • IPベースのレート制限とCAPTCHAはあったが、JSフォームのBotGuardトークンをNo-JSフォームの bgresponse に入れると制限なく動作し、さらにIPv6ローテーションも組み合わせられた
  • 攻撃者はLooker StudioでGoogleアカウントの表示名を取得し、パスワード回復フローのマスクされた電話番号と国別の番号形式を組み合わせることで候補を大幅に絞り込めた
  • 16 vCPU級で時間あたり0.30ドルのサーバーで約毎秒4万回の照合が可能で、Googleは2025年6月6日にNo-JSユーザー名復旧フォームを完全に廃止し、合計5,000ドルを支払った

No-JSユーザー名復旧フォームから始まった脆弱性

  • ブラウザでJavaScriptを無効にした状態でGoogleサービスの挙動を確認していたところ、ユーザー名復旧フォームが依然として動作していることが発見された
  • アカウント復旧フォームは以前から、難読化されたproof-of-work JavaScriptコードが生成するBotGuard系の防御に依存しており、JavaScriptが必要だと考えられていた
  • しかしNo-JSフォームは、復旧メールまたは電話番号が特定の表示名に紐付いているかを確認できるフローを提供していた

2つのリクエストでアカウントの存在を確認

  • 1つ目のリクエストは /signin/usernamerecovery に電話番号を送信し、レスポンスの Location からその電話番号に紐付く ess 値を得る方式だった
    • Cookieと gxf 値は初期ページのHTMLから取得する
  • 続いて /signin/usernamerecovery/lookupess、名、姓、bgresponse=js_disabled を入れて送信する
  • レスポンスのリダイレクトが変わることで、その電話番号と表示名を持つGoogleアカウントが存在するかどうかを判定できた
    • アカウントなし: usernamerecovery/noaccountsfound
    • アカウントあり: usernamerecovery/challenge

IP制限、IPv6、BotGuard回避

  • 初期の試行では、数回のリクエスト後にIPアドレスがレート制限にかかり、CAPTCHAが表示された
  • オランダの携帯電話番号の例では、パスワード回復フローは •• ••••••03 のようなヒントを提供していた
    • オランダの携帯番号は 06 で始まるため、残り6桁、つまり 10^6 = 1,000,000 個の候補を総当たりする必要があった
  • Vultrのようなプロバイダーは /64 のIPv6範囲を提供しており、理論上は 18,446,744,073,709,551,616 個のアドレスを利用できる
  • reqwestClientBuilder.local_address でサブネット内のランダムなIPv6アドレスを各リクエストごとに設定するPoCが作られた
  • データセンターIPでJS無効フォームを使うと引き続きCAPTCHAが出たが、JS有効のアカウント復旧フォームのBotGuardトークンをNo-JSフォームの bgresponse に入れるとリクエストが通った
    • No-JSフォームでは、そのBotGuardトークンに対するレート制限がないように見えた

誤ヒットの除外

  • 最初の実行結果には、名が Henry で姓が空欄、かつ電話番号の末尾2桁が 03 のアカウントが多数含まれていた
  • この場合、名が Henry であれば姓がどの値でも usernamerecovery/challenge が返された
  • 可能性のあるヒットを検証するため、同じ名に対して 0fasfk1AFko1wf のような任意の姓を入れて再確認した
    • それでもヒットになる場合は誤検知として除外した
  • 同じ完全な表示名、同じ国コード、同じ末尾2桁の番号を持つ別のGoogleユーザーが存在する可能性は低いと判断された

国コードと表示名の取得

  • パスワード回復フローの電話番号マスクは、国コードの推定に利用できた
  • Googleは各番号のnational formatlibphonenumber を使用していた
  • 国ごとのマスクされたnational formatを収集して mask.json を作成した
    • ロシア、オランダ、英国、米国はそれぞれ異なるマスクパターンを持つ
  • Googleは2023年に、対象と直接やり取りがある場合にのみ名前を表示する方針へ変更し、2024年4月にはInternal People APIが未認証アカウントに対する表示名の返却を完全に停止した
  • しかし Looker Studio のドキュメントを作成して被害者に所有権を移すと、被害者の操作なしでホーム画面に表示名が露出した

候補範囲の最適化とツール

  • libphonenumberの番号検証を使って、国ごとのモバイル接頭辞、既知の市外局番、桁数を含む format.json を生成した
    • オランダの例では、国コード 31、地域コード 616263646568、桁数 [7] を含む
  • リアルタイムlibphonenumber検証 によって、無効な番号に対するGoogle APIクエリを減らした
  • BotGuardトークン生成のために、chromedp を使う Goスクリプト を作成した
  • 攻撃フロー全体は3段階で進む
    • Looker StudioでGoogleアカウントの表示名を漏えいさせる
    • パスワード回復フロー でマスクされた電話番号を取得する
    • 表示名とマスクされた電話番号を入力して gpb で完全な電話番号をブルートフォースする

ブルートフォース性能と想定時間

  • 時間あたり0.30ドルのサーバーとコンシューマ向け相当の16 vCPUで、約毎秒4万回の照合が可能だった
  • パスワード回復フローで末尾2桁しか与えられない場合の想定所要時間:
    • 米国 +1: 20分
    • 英国 +44: 4分
    • オランダ +31: 15秒
    • シンガポール +65: 5秒
  • PayPalのような他サービスのパスワード再設定フローがより多くの数字ヒントを提供する場合、時間は大幅に短縮されうる
    • 例: +14•••••1779

Googleへの報告と対応タイムライン

  • 2025-04-14: ベンダーへレポートを送信
  • 2025-04-15: ベンダーがレポートを受理し分類
  • 2025-04-25: 「Nice catch!」との返答
  • 2025-05-15: パネルが1,337ドル + swagの報奨を決定
    • 悪用可能性が低いとの判断が根拠で、問題はhigh impactのabuse-related methodologyとして認定された
  • 2025-05-15: 報奨理由に異議を申し立て
    • Abuse VRP表 によれば、probability/exploitabilityは攻撃の前提条件と被害者が悪用を発見できるかどうかで決まる
    • この攻撃には前提条件がなく、被害者が悪用を発見できない点を提示した
  • 2025-05-22: パネルが追加で3,663ドルを支払い、総報奨額を5,000ドルに調整
    • likelihood を medium に引き上げた
  • 2025-05-22: ベンダーは、グローバルなエンドポイント廃止までの間に進行中の緩和策を展開したと確認
  • 2025-05-22: 2025-06-09の公開日程を調整
  • 2025-06-06: ベンダーがNo-JSユーザー名復旧フォームの完全廃止を確認
  • 2025-06-09: レポート公開

まだコメントはありません。

まだコメントはありません。