自分の WordPress サイトにも、管理画面のプラグイン一覧には一度も出てこないコードが2つ動いている。mu-plugins というフォルダに置いたファイルで、片方は REST API の認証を通すため、もう片方は Tor 経由のアクセスを毎日ブロックリストへ流し込むために自分で書いた。正規の用途だ。2025年7月には、この同じ mu-plugins の仕組みを悪用して、管理画面から消えないバックドアを仕込むキャンペーンが報告されている。仕組みが同じなら、正規のコードと攻撃コードは同じ場所で同じように自動実行される。見分けるには中身を見るしかない。
目次
mu-plugins が「消せないプラグイン」である理由
WordPress の wp-content/mu-plugins/ は Must-Use Plugins(必須プラグイン)と呼ばれる特別なフォルダだ。ここに .php ファイルを置くと、通常のプラグインと違って次の3つの性質を持つ。
- 全ページのロード時に自動で実行される(有効化の操作が要らない)
- 管理画面の「プラグイン」一覧に表示されない(Must-Use という別枠にわずかに出るだけで、通常一覧には並ばない)
- ダッシュボードから無効化できない(ファイルを消すしかない)
本来はホスティング会社が全サイト共通の設定を強制したり、テーマに依存しない共通処理を差し込んだりするための仕組みだ。筆者が認証復元コードをここに置いたのも、テーマを切り替えても必ず動いてほしかったからだ。便利さの裏返しで、攻撃者にとっては「一度置けば消されにくく、気づかれにくい実行場所」になる。
2025年7月に報告されたキャンペーンの手口
セキュリティ研究者が報告した今回の攻撃は、mu-plugins の性質を素直に突いている。侵入後の流れを一次情報から整理すると、こうなる。
wp-content/mu-plugins/wp-index.phpというローダーを設置する。名前がwp-で始まるので、パッと見はコアファイルに紛れる- そのローダーは ROT13(アルファベットを13文字ずらすだけの単純な暗号)で隠したURLから、本体のペイロードを取得する。難読化は軽いが、目視や単純なスキャンをすり抜けるには十分
- ペイロードは WordPress のデータベースの
_hdra_coreというオプションに保存され、ファイルとして残りにくくする - テーマのフォルダに
pricing-table-3.phpという名前でファイルマネージャーを仕込み、ハードコードされたトークンで外部から操作できるようにする - 仕上げに
officialwpという管理者アカウントを新規作成し、adminやrootなど既存の管理者名のパスワードも書き換える
厄介なのは、管理画面のプラグイン一覧をいくら眺めても wp-index.php が出てこない点だ。研究者の言葉を借りれば「大多数の管理者はこのディレクトリを検査することがない」。存在しないと思っている場所は、誰も見に行かない。
これは一部の不運なサイトの話ではない
プラグイン経由の攻撃は例外的な事故ではなく、日常の数字になっている。2026年のプラグイン脆弱性の開示状況を見ると、規模感が分かる。
| 指標 | 数値 |
|---|---|
| 1週間の脆弱性開示(1月7日週) | 333件(うちプラグイン253件) |
| 1日あたりの平均開示 | 約36件 |
| 認証なしで悪用可能な割合 | 58.9% |
| 開示時点で未パッチ | 71% |
| 結局パッチが出ない見込み | 30% |
認証なしで悪用できる脆弱性が6割近い、という数字が特に重い。ログインを突破する手間すら要らず、公開されているURLを叩くだけで侵入口になるものが半分以上あるということだ。侵入経路がこれだけ潤沢にあると、mu-plugins への書き込みは「侵入された後」の定着手段として現実味を持つ。
自分のサイトの mu-plugins を実際に点検する
ここからが本題だ。自分のサイトを実際に点検する。筆者はロリポップの共有サーバーで WordPress を SSH と WP-CLI で運用しているので、その環境でそのまま使えるコマンドを載せる。レンタルサーバーで SSH が使えない場合も、FTP でフォルダを覗く・管理画面から確認する、と読み替えられる。点検そのものは数分で終わるし、一度手順を覚えれば毎月の習慣にできる。侵入されてから復旧に追われるコストに比べれば、はるかに安い時間の使い方だ。
1. mu-plugins に何が置かれているか一覧する
# mu-plugins フォルダのファイルを更新時刻つきで確認
ls -la wp-content/mu-plugins/
# WP-CLI なら「認識されている must-use プラグイン」を名前と説明つきで
wp plugin list --status=must-use --fields=name,status,description
筆者のサイトで実行した結果はこうだった。
name status description
ba-authorization-header must-use ロリポップ(CGI/FastCGI)は Authorization ヘッダを PHP に渡さないため…
ba-tor-blacklist must-use Daily-refresh Tor exit node IPs into IP2Location…
2ファイルとも、名前を見れば何のためのものか思い出せる。ba- という自分のサイト固有の接頭辞がついていて、説明文(Description ヘッダ)が入っている。どちらも自分で書いて置いたものだ。ここに身に覚えのないファイル、特に wp-index.php のようにコアを装った名前のものが混じっていたら、それが最初の警報になる。
2. 既知の悪性シグネチャを検索する
今回のキャンペーンや類似のバックドアが使う特徴的な文字列を、まとめて grep で探す。
# mu-plugins とテーマを対象に、既知の悪性パターンを一括検索
grep -rEl 'str_rot13|_hdra_core|officialwp|eval\(base64_decode|gzinflate\(base64' \
wp-content/mu-plugins/ wp-content/themes/
str_rot13(ROT13 難読化)、_hdra_core(ペイロード保存先のオプション名)、officialwp(バックドア管理者名)、それに eval(base64_decode( のような定番の難読化実行パターンを一度に見る。筆者のサイトでは1件もヒットしなかった(クリーン)。ヒットしたファイルがあれば、それを開いて中身を確認する。正規のプラグインがこれらの文字列を持つことはまずない。
3. 管理者アカウントに見覚えのないものが増えていないか
# 管理者権限のユーザーを登録日つきで棚卸し
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered
バックドアは officialwp のような管理者を勝手に作る。筆者のサイトでは管理者が3アカウント出てきて、すべて登録日も含めて見覚えがあった。ここに知らないユーザー名や、身に覚えのない登録日のアカウントがあれば、それは侵入の痕跡だ。mu-plugins がクリーンでも、この一覧は定期的に見る価値がある。
4. コアファイルが書き換えられていないか
上の3手順に、あとから1つ足した。WP-CLI にはコアファイルを公式の配布物と突き合わせるコマンドがある。
wp core verify-checksums
筆者のサイトでの結果は Success: WordPress installation verifies against checksums. で、あわせて警告が1行出た。
Warning: File should not exist: wp-admin/.htaccess
これは自分で置いたファイルで、管理画面に Basic 認証をかけるために使っている。正規の自作ファイルも「あるべきでない」と報告される。警告が出たら中身を見て、自分が置いたものかを判断する。ゼロにすることが目的ではない。
同じ点検をもう一度したら、ファイルが1つ増えていた
この記事を書いた時点の点検では、mu-plugins は2ファイルだった。2026年7月31日に同じ手順をもう一度走らせた。
-rw-r--r-- 1069 Jul 9 04:15 ba-authorization-header.php
-rw-r--r-- 8578 Jul 30 00:11 ba-shield-gate.php
-rw-r--r-- 1409 Jun 30 11:01 ba-tor-blacklist.php
3ファイルに増えていた。増えていたのは ba-shield-gate.php で、7月30日に自分で置いた自作WAFのゲートだった。心当たりのある変更になる。
点検の値打ちはここにある。前回の状態を知っているから、増えたことに気づける。ファイル数を覚えていなければ、3つ並んでいるのを見ても違和感は湧かない。
難読化パターンの grep は今回も0件だった。wp plugin list で見ると、3ファイルとも must-use という status で並び、バージョン列は空になる。
ba-shield-gate,must-use,
ba-authorization-header,must-use,
ba-tor-blacklist,must-use,
月に一度と書いたが、実際にはファイルを置いた日を記録しておくほうが効く。点検で出た更新時刻と突き合わせれば、身に覚えのある変更かどうかがその場で決まる。
正規の mu-plugin と悪性のものを、中身で見分ける
「mu-plugins にファイルがある=危険」ではない。ホスティング会社が置いたものや、筆者のように自分で正規に置いたものもある。存在ではなく中身の性質で判断する。実際に自分の ba-authorization-header.php の冒頭と、報告されているバックドアを並べると、違いははっきりしている。
| 観点 | 正規の mu-plugin | 悪性のバックドア |
|---|---|---|
| ファイル名 | 用途が分かる名前(例 ba-authorization-header.php) |
コアを装う紛らわしい名前(wp-index.php 等) |
| 先頭のヘッダ | Plugin Name と Description で「何をするか」を明記 | 説明なし、または即座に難読化コードが始まる |
| コードの読みやすさ | そのまま読める平文のPHP | ROT13 / base64 / gzinflate で中身を隠す |
| 外部通信 | しない、または用途が明示された既知の宛先のみ | 難読化したURLからペイロードを取得 |
| 副作用 | 単機能(ヘッダ復元、IP更新など) | 管理者作成・パスワード改変・ファイルマネージャー設置 |
筆者の認証復元プラグインは、冒頭に「ロリポップは Authorization ヘッダを PHP に渡さないため、それを復元して REST の Basic 認証を有効にする」と目的が書いてあり、本体も平文の数行だ。認証まわりを自前で扱うのは WordPress に限った話ではなく、EVE Online の公式APIを叩くダッシュボードでも OAuth2 と PKCE を自分で実装した経験がある(New Eden Intelligence Hub の作り方で解説している)。認証コードを自分で書く習慣があると、「このファイルはトークンをどう扱っているか」という視点で悪性コードを読めるようになる。難読化して宛先を隠している時点で、正規のコードではない。
まとめ:見に行かない場所こそ定期的に見る
今回のキャンペーンで突かれたのは、高度な脆弱性ではなく「誰も見に行かない場所」だった。mu-plugins は管理画面に出ず、全ページで自動実行され、消しにくい。この性質は正規の運用にも攻撃にも等しく効く。筆者は同じ仕組みを認証復元とアクセス制御に使っているからこそ、これを一括で「危険」と決めつけるのは筋が悪いと考えている。判断すべきは、フォルダにファイルがあるかどうかではなく、その各ファイルが何をしているかだ。
やることはシンプルだ。月に一度、ls でファイル一覧を見て、身に覚えのないものが無いか確かめる。grep で既知の難読化パターンを探す。管理者ユーザーの一覧に知らない名前が無いか確認する。wp core verify-checksums でコアの改ざんを見る。この4つを習慣にするだけで、見えない場所に置かれたバックドアは「見える」ようになる。前回の点検結果を残しておけば、2回目からは差分だけを見れば済む。自分のサイトのコードは、自分が一番よく読める。まずは今日、mu-plugins フォルダを一度開いてみてほしい。
参考にした一次情報: Sucuri: Uncovering a Stealthy WordPress Backdoor in mu-plugins(初出の解析・2025年7月)、The Hacker News: Hackers Deploy Stealth Backdoor in WordPress Mu-Plugins、WordPress Plugin Security 2026(webhostmost)。
【追記 2026-07-22】当初この記事はキャンペーンの観測時期を「2026年7月」と記載していたが、引用元の報告は2025年7月(Sucuri の解析と The Hacker News の報道)だった。時期を訂正し、初出の Sucuri の解析記事を出典に追加した。手口の解説と監査手順は変わらない。