PukiWikiやQHM(クイックホームページメイカー生産終了)で作られたサイトが突然403 Forbiddenになり、
バックアップを調べると見覚えのないPHPや巨大なHTMLが増えていた――。
この状態では、不審ファイルだけを削除しても安全とはいえません。
入口となった脆弱性や欠損した設定ファイルを残せば、表示が戻らないだけでなく再感染するおそれがあるためです。
わたしは20年以上Webシステムに携わり、WordPressだけでなく古いPHP製CMSや自作システムの復旧も行っています。今回は、PukiWiki 1.4.7を基にしたQuick Homepage Maker 5.2のバックアップを、原本を保全したまま静的解析しました。
- PukiWikiで見つかったWebシェルと賭博スパムの実態
- 旧SWFUploadが侵入口として疑われた理由
- 改ざんファイルを証拠ごと安全に隔離する手順
- index.phpだけを戻してはいけない理由と再発防止策
この記事では、実際に確認できた事実と推定を分けながら、調査から復旧設計までの流れを紹介します。
サイト名・ドメイン・攻撃者の外部URLなどは伏せています。
PukiWikiの403は感染を疑うべき調査の入口だった
今回のご相談は「サイトを開くと403 Forbiddenになり、閲覧できない」というものでした。
403はアクセス制御やサーバー設定でも発生するため、
この表示だけでマルウェアが原因とは断定できません。
しかし、受領したバックアップにはCMS本来の構成では説明できないファイルが複数あり、
感染と改ざんの調査が必要な状態でした。
- CMS:PukiWiki 1.4.7 / Quick Homepage Maker 5.2 revision 2019
- 受領物:サイトファイル一式をまとめたZIP
- 規模:33,486ファイル、106ディレクトリ、約3.07GB
- 調査方法:原本ZIPを変更せず、ハッシュを記録した作業コピーで静的解析
調査では、拡張子だけでなくファイルの中身、設置場所、
同一ハッシュ、CMSの標準構成、タイムスタンプのまとまりを横断して確認しました。
感染サイトのファイル確認で見るべきポイントは、以下の記事でも解説しています。
PukiWiki内から21件の感染ファイルと複数の欠損を確認した
全体走査の結果、高い確度で感染と判断できるファイルは21件でした。
さらに、自動攻撃の試行を示す痕跡2件と、正規設置か攻撃者設置かを所有者に確認すべきGoogle所有権確認ファイル1件を分けて管理しました。
- パスワード認証、ファイル操作、コマンド実行、アップロード機能を備えたWebシェル
system($_GET['cmd'])形式の簡易コマンド実行シェル- 検索エンジンには賭博コンテンツを見せ、人間には通常ページを見せるクローキング
- 外部の賭博サイトへ誘導するドアウェイページと大型HTML
- Wiki本文へ直接書き込まれた外国語の賭博スパム
index.php、pukiwiki.ini.php、qhm.ini.phpの欠損

Wikiのスパム本文には、通常の編集で作られる差分・バックアップ・カウンターの対応記録が見当たりませんでした。
これは正規の編集画面ではなく、Webシェルなどからファイルへ直接書き込まれた可能性を高める材料です。
ただし、ログが完全に揃っていないため、記録がないことだけを決定的証拠にはしていません。
外から見える賭博ページだけを消しても、Webシェルが残れば何度でも作り直されます。
一般的な賭博サイト改ざんの症状と初動は、次の実例記事も参考にしてください。
PukiWikiへの侵入口は旧SWFUploadの任意ファイルアップロードが最有力だった
侵入口として最も疑わしかったのは、
Quick Homepage Makerに同梱された旧式の画像アップロード機能SWFUploadです。
アップロード処理はファイル名の文字種を確認していましたが、
PHPなどの危険な拡張子やMIMEタイプを拒否せず、元の拡張子のままWeb公開領域へ保存する構造でした。
$upload_name = $_FILES['Filedata']['name'];
if (preg_match('/^[-_.+a-zA-Z0-9]+$/', $upload_name)) {
$upload_file = SWFU_DATA_DIR . $upload_name;
move_uploaded_file($_FILES['Filedata']['tmp_name'], $upload_file);
}実際の保存先swfu/d/には、Webシェル、コマンド実行スクリプト、ルートへ自身を複製するドロッパーが集中していました。
さらに、拡張子の大文字・小文字を変えた複数のアップロード試行ファイルが同時刻帯に残っており、
自動スキャナーが実行可能な拡張子を探った痕跡と整合します。

ただし、当時のアクセスログは受領物に含まれていません。
したがって「旧SWFUploadが最有力」とはいえても、侵入元IPや最初のPOSTリクエストまで確定したとは表現していません。
タイムスタンプも攻撃者が変更できるため、古い日付のファイル1件だけから感染開始年を断定しないことが重要です。
ファイルアップロード対策は、拡張子の許可リスト、MIME検証、サーバー側でのファイル名生成、
認証済み利用者だけへの制限、Webルート外への保存を組み合わせるのが基本です。
PukiWikiのクローキングは通常閲覧だけでは発見しにくい
guardian.phpという一見安全そうな名前のファイルには、検索エンジンのユーザーエージェントを判定し、
攻撃者の外部サーバーから取得したコンテンツを返す処理が追加されていました。
一方、一般の利用者にはPukiWiki本来の起動処理を続けるため、
ブラウザで普通に開いただけでは不正表示に気づきにくい構造です。

このファイルは本来のindex.phpに似たブート処理を持ち、代替入口として動作できるものでした。
ただし、元のindex.phpを改名したものか、攻撃者が別途作成したものかまでは証明できません。
調査報告では「書き換えられたindex.phpそのもの」と断定せず、確認できたコードの動作だけを記録しました。
- Google検索結果のタイトルや説明が自社ページと一致するか確認する
- 不審PHP内のUser-Agent・Referer判定と外部通信先を確認する
- 一般ブラウザだけでなく検索エンジン向け応答も安全な環境で比較する
- Search Consoleの所有者、登録URL、セキュリティ警告も確認する
PukiWikiの改ざんファイルは元の構造を保って24件隔離した
駆除では、発見したファイルをその場で完全削除しませんでした。
原本ZIPを保全し、解析用コピーから判定済み24件を隔離フォルダへ移動しています。
感染21件、攻撃痕跡2件、所有者確認が必要な1件を同じ一覧で管理し、分類の違いも残しました。
- 受領ZIPのSHA-256を記録して原本を変更しない
- 危険なパスや展開サイズを確認して隔離解析フォルダへ展開する
- シグネチャ、難読化、外部通信、標準構成との差分から対象を分類する
- 元の相対パスを保ったまま隔離フォルダへ移動する
- 24件すべてのハッシュ一致と元位置からの不在を確認する
同じディレクトリ構造で隔離すると、どこに何が置かれていたかを後から説明でき、
誤検知が判明した場合も戻しやすくなります
ファイル名、元パス、分類、理由、ハッシュをTSV形式の一覧にも残しました。
cache/内のファイルも調査対象には含めますが、復旧時にそのまま戻す必要は通常ありません。
キャッシュは不正コードやスパム表示が残る可能性があるため、安全なコアとデータを復元した後に再生成します。
一方、wiki/やattach/は利用者コンテンツなので、機械的に全削除せず、ページ単位・添付単位で精査します。
PukiWikiはindex.phpだけ戻さずコアと設定を一体で復旧する
今回のバックアップではindex.phpだけでなく、pukiwiki.ini.phpとqhm.ini.phpも欠けていました。
index.phpを1枚戻しても、設定が不足すれば起動できません。
さらに、侵入口のアップロード処理とWebシェルが残っていれば、表示が戻った直後に再感染する可能性があります。
クリーンな比較元には、後期の同一メジャーバージョンを参考にしました。
ただし、公開されているQuick Homepage Maker 5.2のタグと今回のrevision 2019が完全一致するとは限りません。
差分を見ずに新しい一式を上書きすると、独自設定や拡張機能を壊すため、対象環境の版に合わせた復元が必要です。
また、依頼時点の実行環境はPHP 5.3という非常に古い世代でした。
PHP公式ではPHP 5.3のサポート終了日は2014年8月14日とされています。
復旧後も古いPHPを公開運用し続けるのではなく、対応バージョンへの段階的移行、静的サイト化、
別CMSへの移行を含めた出口戦略が欠かせません。
PukiWikiの再感染防止はアップロードの入口と実行の出口を両方ふさぐ
再発防止では、使われていないSWFUploadを停止・削除するのが第一候補です。
業務上どうしても残す場合は、アップロード処理の検証だけに頼らず、
保存先でPHPなどを実行できないようWebサーバー側でも制限します。
<FilesMatch "\.(php|php3|php4|php5|phtml|pht|phar|cgi|pl|py|sh)$">
Order allow,deny
Deny from all
</FilesMatch>この例は旧Apache系の記法です。実際にはサーバーのApache・nginx・PHP-FPM構成に合わせて設定し、
テスト用ファイルがHTTP経由で実行されないことまで確認します。設定ファイルを置いただけでは完了ではありません。
- 管理者、FTP・SFTP、サーバー、データベース、メールの認証情報を変更する
- 危険な拡張子を拒否し、画像は内容とMIMEタイプも検証する
- アップロード先・セッション・内部データをWebから直接読めないようにする
- 同一アカウント内の別サイト、Cron、SSH鍵、Search Console所有者を確認する
- キャッシュを破棄して再生成し、検索結果とアクセスログを一定期間監視する
駆除直後は正常でも、攻撃者が残した別の入口から再侵入されることがあります。
復旧後の監視項目は、次の記事もあわせて確認してください。
PukiWikiマルウェア駆除のよくある質問
PukiWikiマルウェア駆除は原因特定と証拠保全まで行う
今回の実例では、PukiWiki/QHMの33,486ファイルを静的解析し、感染21件を特定しました。攻撃痕跡と要確認ファイルを含む24件は、元のディレクトリ構造とハッシュを保って隔離しています。侵入口は旧SWFUploadの任意ファイルアップロードが最有力でしたが、アクセスログがないため断定の範囲も明確にしました。
古いCMSの復旧では、目立つスパムを消すだけでなく、欠損したコアと設定、直接書き換えられたコンテンツ、実行可能なアップロード先、流出し得る認証情報まで一体で扱う必要があります。もう更新されていないシステムでも、現状を正確に調べれば、安全に止める・復元する・移行する判断材料を作れます。
PukiWikiや古いCMSのマルウェア感染を自分で復旧できない時は

PukiWiki・Quick Homepage Maker・自作PHPなど、古いCMSの乗っ取りやマルウェア感染でお困りなら、
クイックレスキューが調査から隔離・復旧・再発防止まで対応します。
- PukiWikiや古いCMSが403・真っ白・不正表示になった
- 見覚えのないPHPや賭博ページが大量に見つかった
- index.phpや設定ファイルが削除・改ざんされている
- WordPress以外なので対応先が見つからない
- 証拠を残しながら安全に隔離・復旧したい
- 万一復旧できない場合やマルウェア駆除できない場合は全額返金保証で安心
- 90日間再感染保証・動作保証で安心
- 初期費用・調査費用0円で安心






