WordPress wp2shell攻撃後の完全クリーンアップと再発防止手順

WordPress wp2shell攻撃後の完全クリーンアップと再発防止手順

WordPress wp2shell攻撃後の完全クリーンアップと再発防止手順

WordPress が REST Batch API を悪用した wp2shell 攻撃を受け、Web シェルを仕込まれたり偽の管理者を作られたりした場合は、WordPress 本体の更新だけでなくファイルとデータベースの完全な掃除が必要になる。攻撃者は認証なしでコードを実行できるため、必ず FTP/SSH と SQL の両面から対処する。

この攻撃は SQL インジェクションで PHP ファイルをサーバーに直接書き込み、データベース内にも偽のメニュー項目や changeset を残す。パッチを当てただけではバックドアが生き残り、再侵入される危険がある。

Before 攻撃直後、WordPress だけ更新した状態
✗ /wp-content/cache/ に不明な PHP ファイルが残る ✗ データベースに偽の customize_changeset が残る ✗ 攻撃者の管理者アカウントが有効なまま ✗ 全員のセッションが生きている
After 完全クリーンアップ後の状態
✓ キャッシュ内の Web シェルを全削除 ✓ DB から偽データを削除し FILE 権限を剥奪 ✓ 全員のパスワード変更とセッション強制切断 ✓ DISALLOW_FILE_MODS でファイル改変を遮断

上図は攻撃直後と完全クリーンアップ後の違いだ。この記事ではファイル・データベース・ユーザー管理の3層で掃除し、再発を防ぐまでの手順を詳しく解説する。

wp2shell 攻撃で何が起きているのか

wp2shell 攻撃で何が起きているのか

WordPress の REST Batch API は複数の REST API リクエストをまとめて送信できる機能だ。この脆弱性を突かれると、認証なしで SQL インジェクションを起こし、INTO OUTFILE 命令で PHP ファイルをサーバーに書き込める。書き込まれた PHP ファイルは Web シェルとして機能し、攻撃者が自由にコマンドを実行できるようになる。

さらに攻撃者はデータベースに直接アクセスできるため、パスワードのハッシュを盗んだり、自分自身に管理者権限を付与したりすることが可能だ。偽のプラグインを /wp-content/mu-plugins/ に仕込んで恒久的なバックドアにする手口も確認されている。

被害にあったサイトを完全に掃除する手順

被害にあったサイトを完全に掃除する手順

掃除は「ファイルの掃除」「ユーザーとセッションの掃除」「データベースの掃除」の3段階で進める。始める前に必ずファイルとデータベースの完全バックアップを取る。

ファイルシステムから Web シェルと偽プラグインを削除する

FTP クライアントや SSH でサーバーに接続し、まず /wp-content/cache/ ディレクトリを開く。キャッシュ系のディレクトリ(/cache//wpo-cache/ など)に不自然な英数字の羅列(e042u9xy9ra1.php のようなファイル名)があれば、それが Web シェルだ。すべて削除する。

次に /wp-content/mu-plugins/ を確認する。Must-Use プラグインは自動的に有効化されるため、攻撃者が好んで使う場所だ。身に覚えのないファイル(galex_patch.php など)があれば削除する。通常の /wp-content/plugins/ にも見慣れないプラグインが追加されていないか調べる。

STEP 1 FTP/SSH でサーバーに接続
STEP 2 /wp-content/cache/ 内の怪しい PHP ファイルを削除
STEP 3 /wp-content/mu-plugins/ の身に覚えのないファイルを削除
STEP 4 WordPress 本体を 6.9.5 または 7.0.2 以上に更新
STEP 5 wp-config.php に DISALLOW_FILE_EDIT / DISALLOW_FILE_MODS を追加

ファイルの掃除が終わったら、WordPress 本体をパッチ適用済みバージョン(6.9.5 または 7.0.2 以上)に更新する。この更新で Batch API の脆弱性自体が塞がる。

wp-config.php にファイル改変防止の定数を追加する

将来の攻撃に備え、管理画面からのファイル編集とプラグインのインストールを無効化する。FTP または SSH で wp-config.php を開き、以下の2行を追記する。

define( 'DISALLOW_FILE_EDIT', true );
define( 'DISALLOW_FILE_MODS', true );

DISALLOW_FILE_EDIT は管理画面の「テーマエディター」「プラグインエディター」を無効化する。DISALLOW_FILE_MODS は管理画面からのプラグイン追加や更新をブロックする。すでに侵入された後の対策ではなく、掃除が完了した後に再発を防ぐ設定だ。

ユーザーとパスワード、セッションの掃除

ユーザーとパスワード、セッションの掃除

攻撃者はデータベースに直接アクセスしていたため、パスワードのハッシュを抜き取った可能性が高い。管理者を含む全ユーザーのパスワードを変更する。WordPress 管理画面だけでなく、MySQL データベース自体のパスワードも変更し、wp-config.phpDB_PASSWORD を新しいものに書き換える。

パスワード変更だけでは不十分だ。攻撃者がまだログインしたままかもしれない。全セッションを強制的に切断するには、Salt Keys(ソルトキー)を再生成して wp-config.php に上書きする。WordPress 公式の Salt Keys ジェネレーターで新しいキーセットを取得し、既存のキーと置き換えれば全デバイスのログイン状態が即座に無効になる。

最後に、管理画面の「ユーザー」一覧を開き、見覚えのない管理者アカウントが作られていないか確認する。もし存在すれば即座に削除する。

データベースに残った偽のレコードを削除する

データベースに残った偽のレコードを削除する

wp2shell 攻撃は、WordPress のセキュリティフィルターをすり抜けるために、データベース上に偽のナビゲーションメニュー項目と changeset(カスタマイザーの変更履歴)を一時的に生成する。これらのレコードは post_date2020-01-01 00:00:00 に固定されており、URL ペイロードに example.invalid を含むという特徴がある。

以下の SQL を phpMyAdmin などで実行する。なお、接頭辞(プレフィックス)がデフォルトの wp_ ではない場合は、コード内の wp_ を実際のプレフィックス(例: wp2_mysite_)に置き換える必要がある。

偽の changeset を削除する

DELETE FROM wp_posts WHERE post_type = 'customize_changeset' AND post_date = '2020-01-01 00:00:00';

偽のナビゲーションメニュー項目を削除する

DELETE FROM wp_posts WHERE post_type = 'nav_menu_item' AND post_date = '2020-01-01 00:00:00';

悪意のあるメタデータを削除する

DELETE FROM wp_postmeta WHERE meta_value LIKE '%example.invalid%';

これらのクエリでデータベース上の痕跡を一掃できる。実行前には必ずデータベースのバックアップを取る。

データベースユーザーから FILE 権限を剥奪する

データベースユーザーから FILE 権限を剥奪する

今回の攻撃は SQL インジェクション経由で INTO OUTFILE 命令を使い、データベースからディスクにファイルを書き込んでいた。WordPress の通常動作に FILE 権限はまったく不要だ。この権限を WordPress のデータベースユーザーから剥奪しておけば、同種の攻撃が再び成功する可能性を大きく下げられる。

データベースに root 権限でログインし、以下の SQL を実行する。'wp_user_name'@'localhost' は実際の WordPress 用データベースユーザー名に置き換える。

REVOKE FILE ON *.* FROM 'wp_user_name'@'localhost';
FLUSH PRIVILEGES;

REVOKE FILE は FILE 権限を取り消し、FLUSH PRIVILEGES は変更を即時反映する。これでデータベースからファイルシステムへの書き込み経路が遮断される。

Before FILE 権限あり
攻撃者 → SQL インジェクション → INTO OUTFILE → ディスクに PHP ファイル書き込み
After FILE 権限なし
攻撃者 → SQL インジェクション → INTO OUTFILE権限エラーで書き込み失敗
権限あり(攻撃経路あり)  権限剥奪後(経路遮断)

よくある質問

WordPress を更新しただけで掃除は不要か

更新だけでは不十分だ。攻撃者はすでに Web シェルをサーバーに書き込んでおり、そのファイルは更新では削除されない。データベース内の偽レコードや不正な管理者アカウントも残ったままになる。必ずファイルとデータベースの両方を掃除する。

DISALLOW_FILE_MODS を設定すると管理画面からプラグインを追加できなくなるのか

そのとおりだ。DISALLOW_FILE_MODStrue にすると、管理画面からのプラグインのインストール・更新・削除、テーマのインストール・更新がすべてブロックされる。必要な更新は FTP や SSH 経由で手動で行う運用になるが、攻撃者が管理画面からファイルを改変する経路を完全に塞げる。

SQL クエリのプレフィックス wp_ を変更し忘れるとどうなるか

テーブルが存在しないというエラーが出るだけで、データが破壊されることはない。それでも、間違ったテーブルを操作しないよう実行前に必ず実際のプレフィックスを確認する。多くのレンタルサーバーではインストール時に自動生成された固有のプレフィックスが使われている。

FILE 権限を剥奪すると WordPress の動作に影響は出るか

WordPress の通常動作に FILE 権限は一切使われない。記事の投稿やプラグインの動作、データベースの読み書きに影響は出ないため、安全に剥奪できる。

この記事のポイント

  • WordPress 本体の更新だけではバックドアとデータベースの痕跡が残る
  • /wp-content/cache/ と /wp-content/mu-plugins/ の不審な PHP ファイルを削除する
  • 全ユーザーのパスワード変更と Salt Keys 再生成でセッションを強制切断する
  • データベースから post_date が 2020-01-01 の偽 changeset とメニュー項目を削除する
  • WordPress の DB ユーザーから FILE 権限を剥奪し INTO OUTFILE 経路を塞ぐ
佐々木 太陽

・ Reddit、Stack Overflow、WordPress.org フォーラムを日々巡回し、現場の悩みを拾い上げて記事化 ・ WordPress、WooCommerce、Next.js などモダンWeb制作領域のトラブルシューティングが専門 ・ 「検索しても答えが見つからなかった」を一つでも減らすことが目標 ・ エラーメッセージから根本原因にたどり着く粘り強い調査が得意 ・ 初心者がつまずきやすい箇所を先回りで解決する記事作りを心がけている

メッセージを残す