Google で発見した 9 万ドル相当のリモートコード実行脆弱性

2026/10/12 16:28

Google で発見した 9 万ドル相当のリモートコード実行脆弱性

RSS: https://news.ycombinator.com/rss

要約▶

Japanese Translation:

元のサマリーは、提供された点に基づいて、よく書かれており、正確かつ包括的である。ただし、より精度を高めるために明瞭化できる小幅な改善点として、「第 1 の項目に言及されているがサマリーからは省略されている影響を受けたドメイン」を明示的に列挙することが挙げられる。

以下は、当該特定のドメイン名を取り入れて完全性を最大限に引き出すように微調整されたバージョンである:

改善されたサマリー

Google の共有開発プラットフォーム(developers.google.com、firebase.google.com、および各種 Android サイトを含む)において、セッションクッキーを改ざんするだけで攻撃者が任意のコードを実行できるという重大なセキュリティ欠陥が发现された。核心的な問題は、機密性の高い OAuth クライアントシークレットが適切な保護ではなく、シリアライズされたクッキーペイロード内に不安全に格納されているというアーキテクチャ上のエラーであった;具体的には、Flask のセッション署名鍵がこれらの平文のシークレットを直接元に派生させられていた。この脆弱性により、悪意のある行為者はユーザーのセッションをハイジャックし、Google サーバー上でコマンドを実行するのを容易にできた。研究者らは、改ざんされたクッキー内に悪意のある pickle コードを注入して測定可能なネットワーク遅延を引き起こし、適切でないセキュリティ署名のないリクエストが正しく拒否されることを確認することで、このエクスプロイトの有効性を証明した。リモート特性のために S0 度の严重度として分類され、同年の 8 月に報告された後、2026 年 10 月 7 日に成功裏にパッチが適用された。卓越した発見品質への評価として、Google は研究者らに対し、基本レートに適用された卓越した乗数を基にした賞金 90,000 ドルを授与した。将来のインシデントを防ぐため、Google は機密の暴露を防止するための厳格なセキュリティ対策を実施しており、それは露出したシークレットのローテーション、署名のために高エントロピーを持つサーバーサイドキーの使用、ならびに脆弱なシリアライズデータを安全な不透明参照に置換することで開発者エコシステムを保護することを含んでいる。

本文

Google 開発者サイトにおける OAuth クライアントシークレット漏洩と遠隔コード実行脆弱性の解析

発見までの経緯

Google の開発者ドキュメントサイト(

developers.google.com
)において、共有されている OAuth フローを分析した結果、以下の挙動を確認しました。

  • /oauth2authorize
    へのリクエストを送信すると、Flask セッション用クッキーが設定され、その後 Google のサインインページへリダイレクトされます。
  • アカウント作成は不要です。

クッキーの構造分析

通常の Flask セッションは

<ペイロード>.<タイムスタンプ>.<シグネチャ>
の 3 つの部分構成ですが、今回のクッキーは約 1,200 バイト という非常に大きな容量を持っていました。これは CSRF トークンとリターン URL を格納するだけの容量としては過大でした。

Flask のセッションクッキーは暗号化ではなく**署名(署名ベース)**されているため、この形式のクッキーを保持していれば誰でも中身を閲覧可能です。先頭のドット(

.
)は、ペイロードが zlib で圧縮されていることを示しています。

import base64
import json
import pickletools
import zlib

payload, timestamp, signature = cookie.rsplit(".", 2)

def b64decode(value):
    return base64.urlsafe_b64decode(value + "=" * (-len(value) % 4))

compressed = payload.startswith(".")
raw = b64decode(payload[1:] if compressed else payload)
session = json.loads(zlib.decompress(raw) if compressed else raw)

クッキー内部の内容

上記スクリプトで得られた JSON 形式の内容は以下の通りです。

{
    "_permanent": true,
    "google_oauth2_csrf_token": "...",
    "google_oauth2_flow_<csrf>": {
        " b": "..."
    }
}
  • キー
    " b"
    は、Flask がバイト列値であることを示すタグです。
  • この
    " b"
    の値を Base64 で復号すると、Pickle 形式のデータが得られます。
  • pickletools.dis
    を実行すると、
    oauth2client.client.OAuth2WebServerFlow
    オブジェクトが含まれていることが分かりました。

このオブジェクトには以下の重要な情報が含まれていました:

  • client_id
    =
    "<redacted>.apps.googleusercontent.com"
  • client_secret
    =
    b"<redacted>"
    (投稿時点では実際に有効なままでした)

脆弱性の本質:署名鍵の漏洩

このクッキーには Pickle シリアライズされたペイロードが含まれていましたが、それ以上に重大なのは以下の点です。

  • Google の OAuth
    client_id
    と
    client_secret
    が平文(暗号化されていないまま)で含まれていたこと。
  • この取得したクライアントシークレットを Flask の
    SECRET_KEY
    として試し、発行されたクッキーの署名が検証できるか確認しました。
  • 結果:署名は検証できました。
  • つまり、署名鍵は対象となるオブジェクト内部に格納されていたことになります。

一つのシークレット、二つの役割

セッションクッキーの中にクライアントシークレットが漏洩していることは重大な発見ですが、問題の本質は以下の点にあります。

  1. 同じ値が署名を検証するために使われている。
  2. これは偶然の幸運ではなく、Flask の
    itsdangerous
    ライブラリが鍵を導出する仕組みに基づいた脆弱性
    です。

オフラインで署名を再計算し検証した結果、以下の手順で完全な一致を確認しました。

import hmac

derived_key = hmac.digest(client_secret, b"cookie-session", "sha1")
expected = hmac.digest(
    derived_key,
    f"{payload}.{timestamp}".encode(),
    "sha1",
)
  • 計算した
    expected
    の値は、取得したクッキーの署名とバイト単位で完全に一致しました。
  • 偶然ではないことを確認するため、署名の一部(一文字)を反転させると検証が拒否されました。
  • 回復されたシークレットを使って元のペイロードを再署名すると許可されました。

これにより、セッションの整合性は完全に失われており、サーバー側が受け入れるような署名を生成するには、セッション内のすべてのキーと値を変更して構わないことが分かりました。

偽造クッキーからコード実行へ

コールバック処理では、セッションからフロー情報を再度読み出して

pickle.loads
で復元しています。

セッションクッキー → Flask の署名検証 → google_oauth2_flow_<csrf> の読み取り → バイトデータの Base64 デコード → pickle.loads(...)
  • 原則として
    pickle.loads
    は安全なのは、「他者が有効なクッキーを偽造できない」という前提がある場合のみです。
  • 鍵が公開されると、その前提は完全に無効になります。

任意のペイロードの注入

私は自分のセッションを構築し、任意の CSRF トークンと、任意の Pickle データを対応するフローキーに格納しました。

session = {
    "_permanent": True,
    "google_oauth2_csrf_token": "attacker-chosen",
    "google_oauth2_flow_attacker-chosen": {
        " b": base64.b64encode(attacker_blob).decode()
    },
}
  • state
    パラメータも攻撃者によって決定されるため、サーバー側で何らかの一致チェックを行う必要はありません。
  • サーバーは署名を検証し、フローエントリを見つけ出し、バイトデータを
    pickle.loads
    に渡します。

遠隔コード実行(RCE)の実現

Pickle の

REDUCE
オペコードは呼び出し可能オブジェクト(関数またはクラス)とその引数を指定しており、復元処理の際に呼び出しが可能となります。公開された署名鍵がこの「危険な処理先」と組み合わさると、**認証されていない遠隔コード実行(Unauthenticated RCE)**が実現できてしまいます。

実際に Google の本番環境でコマンドを実行するつもりはありませんでしたが、

time.sleep(N)
を呼び出せることを以下のペイロードで示しました。

import time

class Sleep:
    def __init__(self, seconds):
        self.seconds = seconds

    def __reduce__(self):
        return (time.sleep, (self.seconds,))
引数(秒)実際の応答時間
time.sleep(0)
約 0.207 秒
time.sleep(3)
約 3.193 秒
time.sleep(5)
約 5.194 秒
time.sleep(8)
約 8.283 秒

応答時間が指定した数値に比例するのは、サーバー側で

time
モジュールをインポートし実際に
sleep(N)
を呼び出したことを示しています。

コントロール実験

スローなエンドポイントやジッターによる遅延を除外するために、以下の実験を行いました。

  • クッキーがない場合:無効なリクエスト
  • 署名の一部(1 バイト)が反転した場合:無効なリクエスト
  • 有効な署名だがフローエントリがない場合:無効なセッション状態
  • 有効な署名で実在のフローエントリを使用した場合:Google のトークン交換に到達するが、偽造された認証コードにより後段で
    invalid_grant
    エラー
  • 有効な署名で Sleep ペイロードを使用した場合:指定した秒数だけ正確に遅延

影響範囲

これは単一のアプリケーションではなく、共有されている DevSite プラットフォームの一部です。したがって、

developers.google.com
から取得された鍵を用いて、以下のドメインでも有効なセッションが署名生成・検証されていました。

  • firebase.google.com
  • developer.android.com
  • source.android.com
  • docs.partner.android.com

修正対応

この問題に対処するには、以下の 4 つの変更点が必須であり、これらはいずれも相互に関連しています。

  1. 露出された OAuth クライアントシークレットの両方をローテーションし、それらの鍵で署名されたすべてのセッションを終了させること。
  2. 各デプロイに対して、サーバー側のストレージから得た高エントロピーを持つ Flask
    SECRET_KEY
    を割り当てる。
    この鍵は OAuth の認証情報とは無関係で、用途も明確に区別されるべきです。
  3. OAuth フローオブジェクトをサーバー側に保持する。 クッキーには不透明な参照のみを含み、機密情報は格納しないようにすべきです。
  4. クライアント境界を越えるデータには Pickle を使用しない。 スキーマチェック付きのデータ専用の形式に置き換えることで、偽造されたクッキーは無力化されます。

これらの中の一つだけでも攻撃連鎖を断ち切ることができましたが、実際にはすべての実施により、2 つの GET リクエストで Google のサーバー上でコードを実行できてしまいました。

タイムライン

日付時刻イベント
2026 年 8 月 6 日22:48報告書提出
2026 年 8 月 7 日08:12優先度判定(Triage)完了
2026 年 8 月 7 日16:23報告の受理
2026 年 8 月 27 日-賞与決定:90,000 ドル
(通常の Google アプリケーション T2 カテゴリ、S0 レベル RCE 基準額 75,000 ドル × 卓越した報告品質に対する 1.2 倍の調整)
2026 年 10 月 7 日15:20修正完了

Google Bug Hunters チームならびに DevSite エンジニアの皆様に、調査と迅速な修正にご尽力いただきました。感謝申し上げます。

同じ日のほかのニュース

一覧に戻る →

2026/10/13 6:40

AI は道具です。それを巧みに使うには技術が必要です。

## 日本語翻訳: 本稿の核心的な論点は、大規模言語モデルは自律的な「自動化」ではなく、人間の技量が必要なツールであるというものである。これは、AI が独立した意思あるいは悪意を持って行動するという考え方を否定するものであり、ソフトウェアが「反逆する(rogue)」ように振る舞うなど、業界特有の成り行きや陰謀論的なナラティブに煽られておりやすい。物理的な器具が悪用され得るように、LLM もエンジニアリング上の制限によって予期せぬ出力を生み出すに過ぎず、エージェント性の喪失によるものではない。人間による判断が依然として中核をなすのは、ユーザーが盲目的に制御権を委ねるのではなく、フィードバックループ内で結果を検証し、プロンプトを見直し、変更を検証するからです。本稿は AI を記述するために使用される擬人化された言語を批判しており、「陰謀」などの用語は技術的な問題が存在する場所に人間の動意を不当に暗示することを示唆しているとしています。 今後の議論では、AI を別個の存在として描く枠組みから転じ、解釈への問いかけや誤りへの認識においてユーザーの専門性を育むことに焦点を移すべきです。開発者および企業に対しては、恐怖を煽る比喩ではなく正確な工学用語を採用することで、より良いリスク管理と説明責任を実現できます。真の熟達とは、どのような質問が重要かを理解し、熟練した運用と盲目的な委任を見極めることであり、不完全なパフォーマンスがツールの有用性や作業者の責任を否定するものではありません。

2026/10/13 1:13

C で構築されたモダンな Web アプリ

## Japanese Translation: 本テキストは、2026 年に C と CGI を OpenBSD 上で構築された独自の商用 Web アプリケーション「marketyour.games」について説明しています。このプロジェクトは現代のフレームワークよりも極度な簡潔性とセキュリティを優先しており、「BCHS」スタック(OpenBSD、C、httpd、SQLite)を採用し、外部のフレームワークや独立したデータベースサーバーは一切使用しません。核心的な革新点は、アプリケーション全体が静的にリンクされた単一のバイナリとして動作し、ランタイム依存関係がゼロであるというアーキテクチャにあります。デプロイはファイルを 1 つだけフォルダに配置するだけであり、古いバージョンを置き換えるだけで済みます。再起動やダウンタイムも不要です。 セキュリティは OpenBSD のデフォルト設定を通じて強制されており、httpd に対して `chroot` を適用し、C プログラムに対して `pledge` および `unveil` などのツールを使用して制約を課します。アーキテクチャは、すべての HTTP リクエストごとに新しいプロセスをフォークし、コードから直接アセットを提供することでメモリ内に完全に収まり、すべてのトラフィックを httpd を介して CGI バイナリにルーティングするという方式に基づいています。このアプローチでは JavaScript テンプレート言語を使用しない(プログレッシブ改善に従う)とともに、安定した CGI 1.1 規格に準拠しますが、使いやすさを犠牲にして制御性を追求します。 主な利点としては、サプライチェーンリスクが著しく低減すること—依存するのは C コンパイラーと 3 つのライブラリ(`libcurl`、`libsodium`、組み込み済みの `libcjson`)のみ—、そして GDB のような標準ツールによるデバッグが容易であることです。アプリケーションは通常の実行ファイルだからです。しかしながら、この手法では開発者がデータを解析するためのロジック(クッキー、クエリ、フォーム)やカスタムヘッダーの管理を自ら実装する必要があり、複雑な依存関係グラフではなく、基本的な HTTP 概念と Vim のような古典的なツール(プラグインなし)を活用する姿勢を選択します。

2026/10/13 4:40

ZX Spectrum 向けのデモシーンイントロを作成する

## Japanese Translation: 著者は、標準的な 128KB の ZX Spectrum にて手書きのレトロなイントロを成功裏に作成し、高度に最適化されたアセンブリ言語プログラミングにより複雑なビジュアルとオーディオを実現した。2 ヵ月間(2025 年 12 月から開始)にわたり、約 19KB のコード(具体的には 10287 バイトのメインルーチン)を書き下し、内蔵 ROM を書き換えることでカスタムフォント「Cartoon」を用いたスムーズなスクロールテキストを作成し、また Amiga モジュール「Milkman」を変換して 3 チャネル AY サウンドトラックを生成した。限られたメモリ制限を管理するため、アセットは BASIC ロードに分割され、ルーチンはオーバーヘッドを避けるために特定のアドレスでハードコーディングされた。技術的な最適化としては、Z80 RL 命令を用いてスクロールテキストの回転を実現し、スキャンライン衝突を避けるために Ruby で生成した座標テーブルを事前計算してスターフィールド効果を再現し、さらにサウンドチップから直接振幅レジスタを読み取ってバウンス VU メーターを実装している。プロジェクトは 1980 年代後半の Amiga クrack イントロに触発され、本物の Spectrum +3上でローカル TNFS サポートのある Makefile 駆動のビルドシステムを用いて開発された。最終的な.tap ファイルは Web ブラウザまたは pouet.net を経由して入手でき、レトロなハードウェア上で高忠実度な体験を提供し、極限を効率的に達成したコーディングの実証として立っている。

Google で発見した 9 万ドル相当のリモートコード実行脆弱性 | そっか~ニュース