Skip to content

【セキュリティ】wp2shell (CVE-2026-63030) の全サイト横断の影響調査と対応状況 #3

Description

@naokazuterada

karappo が管理する WordPress サイト全体について、wp2shell(CVE-2026-63030 / CVE-2026-60137) の影響調査と対応状況をまとめる。

✅ 2026-08-04、対象バージョンだった全11サイトの対応が完了した

  • 被害あり 4サイト(danc / le-gre / chiba-sakamotokaikei / fukunaga-print)— 不正アカウント計49件をすべて駆除
  • 被害なし 7サイト(compass / noie / niwa / brand / mikey / aidaho / amu-work)
  • 全サイトでコアは修正版になり、攻撃者によるログイン・webshell 設置・DB 改ざんはいずれもゼロ

残っているのは被害4サイトのパスワード変更・クライアント連絡と、下記「未確認事項」の恒久対策。

最初の発見は danc.kyushu-u.ac.jp。詳細な調査記録はこちら: https://github.com/karappo/danc.kyushu-u.ac.jp/issues/18

何が起きたか

WordPress コアの REST batch API のルート混同(CVE-2026-63030, CVSS 9.8)と WP_Query の SQL インジェクション(CVE-2026-60137)を連鎖させることで、未認証の攻撃者が管理者アカウントを作成できる。2026-07-17 に緊急リリースと強制自動更新が世界配信された。

  • 影響: 6.9.0〜6.9.4 / 7.0.0〜7.0.1(6.8.0〜6.8.5 は SQLi のみ該当)
  • 修正: 6.9.5 / 7.0.2 / 6.8.6

なぜ karappo のサイトが被害を受けたか

WordPress の強制自動更新を受け取れない状態だったため。原因は2経路ある。

1. karappo-common(common.php ← 本リポジトリ

define('DISALLOW_FILE_MODS', true); // 自動更新なども無効化

管理画面からのファイル改変を禁じる正しい設定だが、副作用でコアのセキュリティ自動更新まで止まる。karappo-common を使う全サイトが該当。

2. 各サイトの wp-config.php

define('AUTOMATIC_UPDATER_DISABLED', true);

danc / noie.tokyo / www.fukunaga-print.co.jp などが該当。

いずれの設定にも理由があった。WP コアを git 管理して rsync --delete で配信する構成では、サーバ側で自動更新が入るとデプロイのたびに古いコアへ巻き戻るため、うかつに自動更新を有効にできなかった。

対応の方針

巻き戻りをデプロイ側で機械的に防ぐことで、自動更新を有効にできる状態を作った。

各サイトでの作業は次の通り。

  1. コアを修正版(7.0.2 等)に更新
  2. karappo-common をサブモジュール更新(該当サイトのみ)
  3. wp-config.phpAUTOMATIC_UPDATER_DISABLED を削除(該当サイトのみ)
  4. デプロイ
  5. 不正アカウントの削除
  6. wp-content/uploads 配下の webshell 確認(.depignore で除外=rsync が触らないため残る)
  7. ソルトキー再生成
  8. パスワード変更

被害が確認されたサイト

サイト 不正アカウント 攻撃期間 個別 issue 状態
danc.kyushu-u.ac.jp 18件 07-20 〜 07-26 karappo/danc.kyushu-u.ac.jp#18 ✅ 完了
le-gre.com 20件 karappo/le-gre.com#3 ✅ 完了(PW変更のみ残)
chiba-sakamotokaikei.com 4件 07-23(単一IP 194.61.28.x) karappo/chiba-sakamotokaikei.com#10 ✅ 完了(PW変更のみ残)
www.fukunaga-print.co.jp 7件 07-24 〜 07-26 karappo/www.fukunaga-print.co.jp#7 🔄 ソルト再生成 + PW変更が残

4サイトすべてで共通していたこと:

  • 攻撃者によるログイン記録はゼロ。アカウントを作成されただけで、実際に使われた形跡はない
  • wp-content/uploads 配下に webshell などのファイルは一切設置されていない
  • DB(options / 投稿)へのコード注入、不正な cron、アプリケーションパスワードの発行もなし
  • Simple History が入っていたサイトでは、いずれも _rest_api_request: true かつ _user_id: 1(最初の管理者)として実行されていた。管理者アカウントの認証情報が漏洩したのではなく、脆弱性により管理者として再評価された結果

つまり、発見が攻撃の本格化より早く、実害が出る前に塞げた

le-gre.com で削除したアカウント(20件、いずれも管理者・投稿0)

0878fcb93eda            0878fcb93eda@google.com
JLG_ad9bd154b18d        JLG_ad9bd154b18d@wp2shell.local
Nx_d2f39c80be2a         Nx_d2f39c80be2a@nx.invalid
upgrades                locatesuid23@gmail.com
w2s_5895cd7eff97        w2s_5895cd7eff97@wp2shell.local
w2s_9002eff01a          w2s_9002eff01a@local.invalid
w2s_cbca32dcc6b4        w2s_cbca32dcc6b4@wp2shell.local
w2s_e8c51e0da93e        w2s_e8c51e0da93e@wp2shell.local
w2s_efdf98514ee8        w2s_efdf98514ee8@wp2shell.local
wordpress_65dfde16b8ad  wordpress_65dfde16b8ad@wordpress.com
wordpress_762c69e17bf4  wordpress_762c69e17bf4@wordpress.com
wordpress_a0ddfe0f6478  wordpress_a0ddfe0f6478@wordpress.com
wordpress_f68ff9b8201d  wordpress_f68ff9b8201d@wordpress.com
wp_service_cb869a       wp_service_cb869a@service.localhost
wp2_c046b5747e0d        wp2_c046b5747e0d@wp2shell.invalid
wp2_f540b90bd08a        wp2_f540b90bd08a@wp2shell.invalid
wpadminTSz              wpadminTSz@wp2shell.invalid
wpenginebot             wpenginebot@wpengine.com
wpsvc_054f96a7386f      wpsvc_054f96a7386f@wordpress-svc.internal
wpsvc_7a45d46aaf84      wpsvc_7a45d46aaf84@wordpress-svc.internal

注意: 正規のサービスを装った名前が混ざる。 wpenginebot(WP Engine のボットを装うが、Heteml でホスティングしているサイトに存在するはずがない)、upgradeswordpress_*@wordpress.com0878fcb93eda@google.com など、アンダーバー付きのランダム文字列だけを目印にすると見逃す。

判定は次の3点で行う。正規ユーザー以外は例外なくこの形をしている。

  • 権限グループ = 管理者
  • 投稿 = 0
  • 「名前」欄 = 空

脆弱だったかどうかは「functions.php が common.php を読み込んでいるか」で決まる

2026-07-28 に compass.chiba-sakamotokaikei.com を調査して判明した。

サイト functions.php の読込 自動更新 被害
danc.kyushu-u.ac.jp 🔴 common.php あり 止まっていた 18件
le-gre.com 🔴 あり 止まっていた 20件
chiba-sakamotokaikei.com 🔴 あり 止まっていた 4件
www.fukunaga-print.co.jp 🔴 wp-config で停止 止まっていた 7件
noie.tokyo 🔴 あり + wp-config 止まっていた 0件
compass.chiba-sakamotokaikei.com なしhelpers.php のみ) 動いていた 0件
mikey-inc.jp(本番 = main なし(Sage 構成) 動いていた 0件
aidaho.jp なし(files-for-sage) 動いていた 0件
amu-work.com なし(files-for-sage) 動いていた 0件

注意: 「自動更新が止まっていた=必ず被害を受けた」ではない。 noie.tokyo は二重に停止していて脆弱なまま放置されていたが、不正アカウントは1件も作られていなかった。攻撃は無差別に走っているものの、全サイトが発見されるわけではない。

正しくは「自動更新が止まっていた=被害を受けうる状態だった」であり、実際に被害が出たかは攻撃者に見つかったか次第。逆に、自動更新が動いていたサイト(compass)は 7/17 の緊急更新を受け取れたため、そもそも狙われても成立しない

DISALLOW_FILE_MODSkarappo-common/common.php で定義されるため、functions.php がそれを require していなければ定義されず、自動更新は普通に動く

compass は helpers.php しか読み込んでいなかったため、2026-07-17 の緊急自動更新を受け取れて無傷だった(デプロイ前の時点で既にサーバ側は 7.0.2 になっていた)。

⚠️ compass の functions.php には「DISALLOW_FILE_MODS は karappo-common/common.php で別途定義されており、管理画面からの更新適用自体はそちらで禁止されている」というコメントがあるが、実際には読み込まれておらず、このコメントは誤り。同様の思い込みが他サイトにもある可能性がある。

被害リスクの判定方法

grep -nE "^[^/]*(require|include).*common\.php" wp/wp-content/themes/*/functions.php

ヒットすれば自動更新が止まっていた(=被害の可能性が高い)、しなければ動いていた(=被害の可能性が低い)。

ただし参照している karappo-common のコミットに DISALLOW_FILE_MODS が含まれるかも併せて確認すること(古いコミットには存在しない。例: niwa-archives.org の a537036)。

⚠️ リニューアル中のサイトは、ブランチによって判定が逆になる。 mikey-inc.jp は main(本番・Sage 構成で common.php 未読込)と staging(新テーマ・karappo-common を読込)で自動更新の可否が真逆だった。origin/main だけを走査すると「karappo-common で止まっている」と誤判定する(実際は逆で、本番は自動更新が動いていたため 7/17 の緊急更新を受け取って無傷だった)。

より重要なのは、公開のタイミングで守りの状態が反転すること。staging を本番へ昇格させるとDISALLOW_FILE_MODS が新たに効きはじめ、それまで動いていた自動更新が止まる。リニューアル公開の前に、staging 側の karappo-common を #2 込みのコミットまで上げておくこと。

サイト別の対応状況

各リポジトリの origin/main を走査した結果(2026-07-27 時点)。ローカルの作業コピーとは差異がありうるため、着手時に再確認すること。

🔴 wp2shell の対象バージョン

サイト コア 自動更新の無効化 状態
danc.kyushu-u.ac.jp 7.0.2 wp-config ✅ 対応完了
le-gre.com 7.0.0 → 7.0.2 karappo-common ✅ 対応完了(PW変更のみ残)
chiba-sakamotokaikei.com 7.0.1 → 7.0.2 karappo-common ✅ 対応完了(PW変更のみ残)
www.fukunaga-print.co.jp 7.0.1 wp-config
noie.tokyo 6.9.0 → 7.0.2 wp-config + karappo-common ✅ 完了・被害なし
mikey-inc.jp 6.9.4 (main) / 7.0.2 (staging) ※サーバは 7.0.2 main は Sage 構成のため実質なし ✅ 完了・被害なし
compass.chiba-sakamotokaikei.com 6.9.4 → 7.0.2 (common.php 未読込のため実質なし) ✅ 完了・被害なし
niwa-archives.org 6.9.4 → 7.0.2 (a537036 に DISALLOW_FILE_MODS 無し・実質なし) ✅ 完了・被害なし
aidaho.jp 6.9 ※サーバは 6.9.5 (files-for-sage に common.php 無し・実質なし) ✅ 完了・被害なし
amu-work.com 6.9 ※サーバは 7.0.2 (files-for-sage に common.php 無し・実質なし) ✅ 完了・被害なし
branddesigncontest.com 6.9.0 → 7.0.2 (files-for-sage に common.php 無し・実質なし) ✅ 完了・被害なし

「自動更新の無効化なし」のサイトは、サーバ側では 7/17 の強制更新を受け取って修正済みの可能性が高い。ただしリポジトリが古いままなので、次のデプロイで github-deploy の照合に引っかかって中断される(=正しく検知される)。

末尾3サイトは、ローカル走査での報告値(6.8.1 / 6.6.1 等)と origin/main の値が食い違っている。着手時に実際のバージョンを確認すること。

確認済み(2026-08-04)。 食い違いの原因は、ローカルの作業コピーが origin/main より大きく遅れていたこと(aidaho は9コミット、amu-work は5コミット behind)。ローカルのファイルを見て脆弱性を判定してはいけない。 origin/main の内容と、/wp/wp-links-opml.php によるサーバの実バージョン、この2つで判定すること。

🟡 SQLi のみ該当(6.8.0〜6.8.5)

karappo.co.jp (6.8.3) / shinra-company.co.jp (6.8.3) / artstriennale.city.chiba.jp (6.8.2) / hellogarden.jp (6.8.2) / kny-architects.jp (6.8.1)

⚪ wp2shell の対象外(ただし著しく古い)

5.x 〜 6.x 台が20件以上。3.8.1 / 4.0.1 / 4.4.1 など、公表済みの脆弱性を多数抱えたまま稼働しているものを含む。wp2shell に当たらないのは「脆弱なコードが入る前のバージョンだから」であって、安全という意味ではない。 別途棚卸しが必要(別 issue を立てる)。

DBバックアップ(不正アカウント削除前のスナップショット)

karappo-common/bin/db export で取得。パスワードハッシュ・DB認証情報・API キーを含むためリポジトリにはアップロードせず、寺田のローカルにのみ保存している。必要になった場合は寺田まで。

サイト ファイル 取得日時
danc.kyushu-u.ac.jp _assets/database/dev-20260727-135450.sql 2026-07-27 13:54
chiba-sakamotokaikei.com _assets/database/dev-20260727-174748.sql 2026-07-27 17:47
www.fukunaga-print.co.jp _assets/database/dev-20260727-180750.sql 2026-07-27 18:07
compass.chiba-sakamotokaikei.com _assets/database/dev-20260728-121119.sql 2026-07-28 12:11
noie.tokyo _assets/database/dev-20260728-125516.sql 2026-07-28 12:55
le-gre.com (未取得。削除前のスナップショットなし)

一部サイトでは、そもそも照合の仕組みが効いていなかった

niwa-archives.orgbranddesigncontest.com は、workflow で

DEP_REMOTE_INCLUDE_FILE: .depinc.sh    # ← raw URL ではなくリポジトリ内のファイル

としており、標準の .depinc.sh を使っていなかった。そのため 2024年以降に標準へ追加された機能をすべて取りこぼしていた(メンテナンス画面、コアのバージョン照合)。

「照合は .depinc.sh が raw URL 参照なので全サイトに反映済み」という前提が誤りだったことになる。皮肉なことに niwa は DISALLOW_FILE_MODS が無く自動更新が動くため、全サイト中で最も巻き戻りリスクが高いサイトでありながら、保護が効いていなかった。

独自の .depinc.sh を持っていた理由は、さくらインターネットで PHP バージョンを php.cgi で切り替えている都合上、そのパーミッション設定(niwa は 705 / branddesigncontest は 711)が必要だったため。

対応: karappo/github-deploy#9 でサイト固有の処理を差し込むフック(.depinc-extra.shbefore_sync_extra / after_sync_extra)を追加し、両サイトを raw URL へ移行できるようにした。niwa は移行済み(本番で php.cgi -> chmod 705 の実行とバージョン照合の動作を確認)。

確認方法(他サイトでも同じ問題がないか):

grep -h 'DEP_REMOTE_INCLUDE_FILE' .github/workflows/*.yml

https:// で始まっていなければ、標準の更新が届いていない。

調査で分かった運用上の改善点

  • Simple History は有効にしておく。 compass は inactive だったため攻撃ログが一切残っておらず、被害の有無をユーザー一覧だけで判断せざるを得なかった(2026-07-28 に有効化済み)。有効だったサイトでは、REST API 経由・_user_id: 1 として実行された事実と送信元IPまで追えた
  • uploads/ の webshell 判定は find -newermt '2026-07-17' が最も確実。 拡張子を問わず攻撃期間中に置かれたファイルを洗い出せる。ここが空なら画像に偽装したものも含めて排除できる
  • SSH が使えないサーバ(www.fukunaga-print.co.jp)では、調査用 PHP を FTP で一時アップロードして代替した(.gitignore/db-inspect-*.php の運用。使用後に削除)
  • サーバ側の実バージョンは /wp/wp-links-opml.php を開けば外部から分かる。 HTML コメントとして <!-- generator="WordPress/7.0.2" --> が出力される。SSH も FTP も使わずに済むので、リポジトリとサーバの乖離を調べるにはこれが最短。
    • karappo-common は <meta name="generator"> を削っているため、トップページのソースからは判別できない
    • /wp/readme.html にはバージョン番号が載っていないので当てにならない
    • ウェブ全体に BasicAuth がかかっている環境(ステージング等)では見えない。その場合はデプロイが成功しているかどうかで判断できる(バージョン照合を通過した=サーバとリポジトリが一致している)

未確認事項

  • 対象11サイトの不正アカウントの有無 → 全サイトの管理画面で確認完了(攻撃者は投稿0件のため公開 REST API には現れず、ユーザー一覧を直接見るしかない)
  • AUTOMATIC_UPDATER_DISABLED が無いサイトの、サーバ側の実バージョン/wp/wp-links-opml.php で全サイト確認完了
  • リポジトリのコアがサーバより古いまま残っているサイトの解消。 aidaho.jp(repo 6.9 / server 6.9.5)、amu-work.com(repo 6.9 / server 7.0.2)( karappo/amu-work.com#2 で 2026-08-04 対応済み)、mikey-inc.jp の main(repo 6.9.4 / server 7.0.2)。サーバ側は修正版なのでセキュリティ上の問題はないが、次のデプロイでバージョン照合に引っかかって中断される。デプロイの必要が生じた時点で、手元のコアをサーバと同じバージョンへ上げてからコミットすること
    • 前提として、この3サイトの workflow が DEP_REMOTE_INCLUDE_FILE に raw URL を指定しているか(=照合が効くか)を確認しておくこと。niwa / brand のようにリポジトリ内の .depinc.sh を参照していると照合が働かず、古いコアが無言で再配信される
  • 🟡 SQLi のみ該当のサイト(6.8.0〜6.8.5)の更新
  • ⚪ 対象外だが著しく古いサイト(5.x〜6.x 台が20件以上)の棚卸し(別 issue を立てる)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions