1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WordPress の緊急脆弱性「wp2shell」を突かれてフィッシングの踏み台にされかけた話 ― 検知から除染まで

1
Posted at

  • 自社サイト(WordPress)に大量のバウンスメール(Undelivered Mail Returned to Sender)が届き始めたのが発端
  • 調査すると、フィッシングメールの From を自社ドメインに詐称 されて踏み台にされていた
  • 並行して、自社 WordPress が緊急脆弱性 wp2shell(CVE-2026-63030 / CVE-2026-60137) で侵害され、DB に 100 体以上の不正管理者アカウント が作られていたことが判明
  • 原因は コアの自動更新を無効にしていて、公開済みのセキュリティ修正(7.0.2)を約 2 週間受け取れなかった こと
  • 「フィッシング踏み台化」と「WordPress 侵害」は 同時期に発覚した別事象。連動していた可能性はあるが断定はできなかった

会社名・ドメイン・IP・アカウント名はすべて仮名/ダミー値に置き換えています。


1. 発端:大量のバウンスメール

ある朝、会社のメールボックス(hp_info@example.com)に、同じ件名のメールが数百通単位で届いていた。

Subject: Undelivered Mail Returned to Sender
From: MAILER-DAEMON@mailhost.example-isp.net (Mail Delivery System)

in:spam で数えると 500 通超。すべて「宛先不明(User unknown)で配信できませんでした」という自動返送だった。

最初は「誰かが From を詐称してスパムを送り、その不達通知が返ってきているだけ」(いわゆるバックスキャッター)かと思った。だが、バウンスの中身(元メッセージのヘッダー)を見て話が変わった。


2. ヘッダー解析:ただの詐称ではない

バウンスメールには、配信に失敗した「元のメール」がまるごと添付されている。そのヘッダーがこれ(ダミー値)。

Return-Path: <hp_info@example.com>
Received: from [127.0.0.1] (unknown [203.0.113.79])
    by smtp3.mailhost.example-isp.net (Postfix) with ESMTPSA id ABCDEF0123456
    for <victim@example-biglobe.ne.jp>; Tue, 4 Aug 2026 10:52:39 +0900 (JST)
From: "ビッグロー部 警備チーム" <hp_info@example.com>
To: victim@example-biglobe.ne.jp
Subject: 2要素認証を有効にする

ポイントは 2 つ。

with ESMTPSA

Postfix がこの表記を付けるのは SMTP 認証に成功したセッションだけA は Authenticated の A。つまり送信者は、契約配下のいずれかのアカウントの 正しい ID / パスワードを持っている。認証必須の SMTP サーバーで、外部 IP から送信が成立している以上、「From を詐称しただけ」では説明がつかない。

② 送信元 IP 203.0.113.79

自社サーバーでも ISP のサーバーでもない、まったく無関係な海外 IP。ここから認証が通っている。

メール本文の中身

quoted-printable をデコードすると、典型的なフィッシングだった。

  • 「セキュリティ保護のため、ログイン試行が検出されました」
  • ボタンのリンク先が正規ドメインではなく、使い捨てのトンネリングサービス(*.trycloudflare.com 配下)
  • フッターに © 2026 ##victimdomain##. という テンプレートの置換漏れ まで残っていた

宛先はすべて特定 ISP のドメイン(*.example-biglobe.ne.jp)。これは後述する 大規模漏洩の流出リスト を標的にしたキャンペーンだと推測できた。


3. 背景:ISP メール基盤の大規模漏洩

2026 年 6 月、某 ISP 各社が採用していたメール基盤(第三者製ソフトウェアの脆弱性)が不正アクセスを受け、数百万件規模のメールアドレス・パスワードが漏洩 する事件があった。総務省の行政指導や、漏洩情報を悪用したフィッシングへの注意喚起も出ていた。

今回のフィッシングの宛先がすべてこの ISP のドメインだったのは、流出リストを標的にしていたから と考えるのが自然だった。

つまり構図はこう。

① ISP 基盤から有効アドレスのリストが大量流出(6月)
        ↓
② 攻撃者が「日本の正規企業の SMTP」を踏み台に確保(信頼性が高く弾かれにくい)
        ↓
③ 流出リスト宛にフィッシングを大量送信、From は正規企業を詐称
        ↓
④ 解約済みアドレス分がバウンスし、詐称された企業に大量返送 ← イマココ

自社は、この踏み台と From 詐称に使われた「被害者側」 だった。


4. もう一つの発見:WordPress が乗っ取られていた

「なぜ自社ドメインが踏み台に選ばれたのか」を追ううちに、自社 WordPress を調べることになった。ここからが本題。

4-1. まず SMTP 認証情報の在処を疑う

WordPress の WP Mail SMTP プラグインには、メール送信用の SMTP アカウント(kir000000.smtp-dev 相当)が保存されていた。「WordPress が侵害されて、この認証情報が抜かれたのでは?」という仮説。

パスワードを変更しても送信は止まらなかった(後述の通り、反映ラグと別要因が絡んでいた)。だが、この調査の過程で WordPress 本体の異常 に行き着く。

4-2. WordPress のバージョンを確認

現在のバージョン: 7.0.0
このサイトは新しいバージョンの WordPress の自動更新を受け取りません。

これが致命傷だった。7.0.0 のまま、しかも自動更新オフ。


5. wp2shell とは何か

2026 年 7 月 17 日に公表された WordPress コアの緊急脆弱性。発見者により「wp2shell」と命名された。2 つの CVE を連鎖させる攻撃チェーン。

CVE 内容
CVE-2026-63030 REST API のバッチ処理(/wp-json/batch/v1)で、サブリクエストと検証結果の対応がずれる不具合。WordPress 6.9 で混入
CVE-2026-60137 WP_Queryauthor__not_in に攻撃者制御の値が渡ると SQL インジェクションが成立

この 2 つを組み合わせると、未認証・プラグイン不要・標準構成 のサイトに対して、外部から任意コード実行(Pre-Auth RCE)が成立する。

影響バージョンと修正版

バージョン wp2shell(RCE) SQLi 単体 対応
6.8.0〜6.8.5 対象外 影響あり 6.8.6 へ
6.9.0〜6.9.4 影響あり 影響あり 6.9.5 / 7.0.2 へ
7.0.0〜7.0.1 影響あり 影響あり 7.0.2 へ

自社サイトは 7.0.0 で、RCE 成立条件のど真ん中だった。

タイムラインの一致

7/17  wp2shell 公表・7.0.2 リリース
7/18  PoC コード出回り、悪用開始
7/19  ← 自社 DB に不正管理者が出現し始めた日(PoC 公開の翌日)
7/19〜8/03  不正アカウントが断続的に量産され続ける

自動更新をオフにしていたため、公開済みの修正を 約 2 週間半、受け取れないまま無防備 だった。


6. 侵害調査:ファイルは綺麗、DB が汚染

6-1. ファイルシステムの調査

まず「webshell が置かれていないか」を疑い、7/19 以降に作成・変更されたファイルを洗い出す。

# mtime は touch で偽装されうるので ctime も見る
find /var/www/site -name "*.php" -newermt "2026-07-17" -type f -printf "%T+ %p\n" | sort
find /var/www/site -name "*.php" -newerct "2026-07-17" -type f -printf "%C+ %p\n" | sort

# webshell が潜みやすい場所
find /var/www/site/wp-content/uploads -name "*.php" -type f

# 難読化コードの検出
grep -rln --include="*.php" -E "eval\s*\(\s*(base64_decode|gzinflate|str_rot13)" /var/www/site

結果は 7/19 以降に変更された PHP ファイルはゼロ(ctime ベースでも)。webshell の痕跡なし。.htaccess も精査したが不正な auto_prepend_file やリダイレクトは無し。

攻撃者は ファイルを一切残していなかった。これが「ファイル調査だけでは見逃す」タイプの侵害だった。

6-2. DB の調査

ファイルが綺麗なので、DB を直接見る。

SELECT ID, user_login, user_email, user_registered
FROM wp_users ORDER BY user_registered DESC;

結果に絶句した(ダミー値)。

ID   user_login          user_email                     user_registered
115  wordfence           wordfence@wordfence.com         2026-08-03 17:54
114  w2s_841bee8fd2e6    w2s_841bee8fd2e6@wp2shell.local 2026-08-03 10:29
...
3    wpsvc_7c1073c8c50d  ...@wordpress-svc.internal      2026-07-19 05:30
2    admin_user          admin@example.com               2025-07-11 02:21  ← 正規
  • ID 3〜115 のほぼ全部が不正ユーザー
  • メールドメインが @wp2shell.local @wp2shell.invalid @nx.invalid など、攻撃ツールが自動生成したダミー
  • 正規ユーザーは ID 2 の 1 つだけ

さらに、customize_changeset が不正ユーザー数とほぼ一致する件数(116 件)作られていた。各バックドアアカウントが実際にログインして活動していた 痕跡だ。

SELECT post_type, post_status, COUNT(*)
FROM wp_posts WHERE post_author NOT IN (2)
GROUP BY post_type, post_status;
-- customize_changeset / oembed_cache / request のジャンク多数
-- タイトル「x」の公開投稿 2 件(SEO/フィッシング用スパム)

幸い、wp_optionssiteurl / home は改ざんされておらず、個人情報を含む実データの窃取痕跡も見当たらなかった(request 型の投稿はすべて中身が空のジャンクだった)。


7. 除染:順序を間違えると「賽の河原」

ここが最重要。脆弱性を塞ぐ前に不正ユーザーを消しても、数分でまた作られる。 順序が命。

Step 0. 証拠保全

mysqldump -u dbuser -p dbname > /backup/compromised_$(date +%Y%m%d_%H%M).sql

Step 1. 攻撃経路を塞ぐ(本体更新までの繋ぎ)

.htaccess の先頭で、攻撃口の batch エンドポイントを遮断。

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/wp-json/batch [NC,OR]
RewriteCond %{QUERY_STRING} rest_route=/batch [NC]
RewriteRule .* - [F,L]
</IfModule>

Step 2. WordPress を 7.0.2 に更新(根本対策)

これで CVE-2026-63030 / 60137 が塞がる。これをやらない限り再発する。

Step 3. 不正ユーザー・ジャンクを削除

-- スパム投稿とリビジョン・メタ
DELETE FROM wp_postmeta WHERE post_id IN (423, 449);
DELETE FROM wp_posts WHERE ID IN (423, 449);
DELETE FROM wp_posts WHERE post_type = 'revision' AND post_parent IN (423, 449);

-- 攻撃で溜まったジャンク
DELETE FROM wp_posts WHERE post_type IN ('customize_changeset','oembed_cache','request');

-- 不正ユーザー(正規 ID 2 以外すべて)
DELETE FROM wp_usermeta WHERE user_id NOT IN (2);
DELETE FROM wp_users   WHERE ID     NOT IN (2);

-- 孤児メタの掃除
DELETE pm FROM wp_postmeta pm
LEFT JOIN wp_posts p ON pm.post_id = p.ID WHERE p.ID IS NULL;

Step 4. 認証情報を総入れ替え

  • WordPress 管理者パスワード変更
  • DB パスワード変更 → wp-config.php へ反映
  • SMTP パスワード変更

Step 5. salt(認証キー)の再生成 ← 見落としがち

攻撃者は 2 週間ログインしていた。そのセッション Cookie は、パスワードを変えても salt を変えない限り有効なまま。

curl https://api.wordpress.org/secret-key/1.1/salt/
# 出力された 8 行を wp-config.php の AUTH_KEY〜NONCE_SALT と丸ごと差し替え

これで全セッションが無効化され、攻撃者の居座り Cookie も即死する。


8. 波及調査:被害はどこまで届いたか

同一サーバー上に他サイトや Redmine が同居していたので、横展開を確認した。

# サーバー全体で侵入期間の不審ファイル
find /var/www -newermt "2026-07-19" -name "*.php" -type f | head -30

# 権限昇格の痕跡(SUID バイナリ)
find / -perm -4000 -newermt "2026-07-19" -type f 2>/dev/null

# SSH ログイン履歴
last | head -20

結果:

対象 判定
他ディレクトリの不審ファイル なし(更新分はすべて正規コアファイル)
SUID バイナリの異常 なし → 権限昇格・root 奪取なし
SSH ログイン すべて正規オフィス IP のみ → OS 侵害なし
同居の他サイト Apache vhost がコメントアウト済みで配信停止・無傷
DB ユーザー サイトごとに分離済み → DB 経由の横展開不可

wp2shell は Web サーバー実行ユーザー(apache)権限の RCE であり、通常は OS レベルや他サイトへは波及しない。今回も 被害は 1 サイトの WordPress DB 内に封じ込められていた。DB ユーザー分離・SSH の IP 制限・不要 vhost の無効化といった普段の設計が効いていた。


9. 「2 つの事象」を混同しない

報告をまとめる段で、事実関係を正確に切り分ける必要があった。

【事象A】フィッシング踏み台化・From 詐称
  → ISP 基盤の大規模漏洩に関連した外部要因。自社は被害者側

【事象B】WordPress の wp2shell 侵害
  → 自動更新オフで放置していた、自社側で防ぎ得た問題

この 2 つは 同時期に発覚した別事象。B の侵害で SMTP 認証情報が抜かれ A に悪用された、という連動の可能性はあるが、SMTP 側の認証ログを確認できなかったため 断定はできなかった

重要なのは、B を「漏洩のせい」と混ぜないこと。B の本質は運用課題(更新の放置)であり、そこを曖昧にすると再発防止の教訓がぼやける。


10. 教訓と再発防止

① コアのセキュリティ更新は自動で受け取る

「更新すると壊れるのが怖い」で 全部 止めていたのが最大の敗因。壊れやすいメジャー更新は手動でよいが、後方互換性の高いマイナー/セキュリティ更新まで止める必要はない

// wp-config.php
// ❌ これを書くと「全部」止まる(今回の敗因)
// define('AUTOMATIC_UPDATER_DISABLED', true);

// ✅ マイナー(セキュリティ)だけ自動、メジャーは手動
define('WP_AUTO_UPDATE_CORE', 'minor');
動作
true メジャーもマイナーも自動
'minor' マイナー/セキュリティのみ自動(推奨)
false コア更新は全部手動

② 「壊れるのが怖い」への現実解

  • 本番クローンの ステージング環境 で先に更新検証
  • 更新直前の バックアップ(今回、日次 cron バックアップがロールバック保険として機能した)
  • この組み合わせなら「勝手に壊れる」と「穴を放置する」の両方を避けられる

③ 侵害調査は「ファイル」と「DB」の両面で

今回はファイルが完全に綺麗で、DB だけが汚染 されていた。find によるファイル調査だけで「異常なし」と判断していたら見逃していた。wp_users / wp_posts / wp_options は必ず直接確認する。

④ 多層防御と最小権限が効く

  • DB ユーザーをサイトごとに分離 → 横展開を物理的に阻止
  • SSH / 管理画面の IP 制限 → OS 侵害・不正ログインを抑止
  • 使わない vhost は設定ごと無効化 → 忘れた頃の侵入口を消す

⑤ ステータスは「送信元 IP」と「ヘッダーの実時刻」で判断する

バウンス対応で、パスワードを変えたのに止まらない と焦った。だが ESMTPSA の有無、Return-Path、そして バウンスが届いた時刻ではなく内側の元メッセージの送信時刻 を見れば、反映ラグなのか別要因なのかを冷静に切り分けられる。ヘッダーは嘘をつかない。


まとめ

  • 発端はフィッシングの踏み台化(外部要因の被害)だったが、調査の過程で 自社 WordPress の wp2shell 侵害(自社の運用課題)が別途判明した
  • ファイルは無傷でも DB に 100 体超のバックドア管理者 が作られる、という「見えにくい侵害」だった
  • 除染は 経路遮断 → 本体更新 → アカウント削除 → 認証情報刷新 → salt 再生成 の順序が命
  • 根本原因は セキュリティ更新の放置WP_AUTO_UPDATE_CORE = 'minor' とステージング+バックアップで、次はこうならないようにする

脆弱性そのものより、「更新を止めていた運用」こそが本当の穴 だった、という話でした。


参考

  • WordPress 7.0.2 Security Release(WordPress.org)
  • CVE-2026-63030 / CVE-2026-60137 Security Advisory
  • 各種 wp2shell 解説記事(影響バージョン・攻撃チェーンの詳細)

本記事の固有名詞・IP・アカウント名・ID はすべて仮名/ダミー値です。実際の対応時系列と手順を再構成しています。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?