WordPressを運用中のサーバがまるごとPHPマルウェアに感染していた時の対応メモ


(2021.1.26 追記) 本稿の続きを書きました。
時系列で見る:WordPressを運用中のサーバが丸ごとPHPマルウェアに感染する流れ
https://qiita.com/Ayutanalects/items/e7919afadc7d8394820f


制作会社から「自社で管理中のサイトがおかしい」との連絡を受けて、
中をのぞいたら、PHP製の複数種類のマルウェアに感染していたので対応をメモ。

以下の内容は、あくまでも自分の対応時のものです。
攻撃者がスクリプトを変更すれば同じ方法では検出できなくなるのでご注意ください。

初期状態

症状

  • 自社管理中のWordPressサイトにアクセスすると、全く知らないサイトにリダイレクトされる
  • 同上のWordPress管理画面URLが403 Forbiddenとなりログインできない
  • その他、同じサーバ内の別ドメインのサイト(非WordPress)のURLも403 Forbiddenになっている
  • 数日前から同様の症状があり、他のエンジニアが対応したが再発した。

サーバーの状態

  • レンタルサーバはS社
    • 自分も長年使っているけど、こういった事象は起きたことがなく断じてサーバ会社のせいではない。
  • 10ドメイン運用中
    • うち、WordPress運用中2ドメインと申告あり
    • 忘れ去られたテストサイト等、何のアクセス制限もかかっていない放置WordPressがほかに2つあることを、find -type f -name "wp-config.php"で検出した。
    • サーバ内にあるのはWordPress 4.7/4.9/5.6、そのうち運用中は4.9と5.6
  • 規定のドキュメントルート以下に、各ドメインのディレクトリができるタイプ
  • S社のサーバコントロールパネルを確認
    • WAF
      • 全ドメインで未使用 →リスク説明の上有効にする
    • ログとリソース
      • アクセスログ、エラーログは過去12か月分残っている様子。
      • リソースはコンパネ上のグラフ表示しか頼れないが、最初に症状が起きた日から今日までの、1日の転送量がそれ以前の7-8倍、CPU利用時間は5倍程度に増えている

依頼者からの制限

  • サイトの停止は極力避けたい
    • 検索エンジンで飛んで来る人もいて、詐欺サイトにリダイレクトされているのに止めたくないとは…
  • サイトの健全な状態のデータなし
    • サイト納品データもないそうなので、差分チェックができない
    • もともとサーバ管理者だったエンジニアとは連絡が取れないまま運用しているとのこと

作業前にやったこと

  1. 各ドメインのサイトのアクセスを止める
    • サイトを止めたくないとのことで却下
  2. コントロールパネルのパスワード、FTPのパスワードを変更
    • 元が簡単なものだったので、最大桁でできるだけ固くなるように変更
    • WordPressのパスワード、DBパスワードもやりたいけど、影響範囲がわからないので保留
  3. ドメインごとにzip圧縮してローカルに保管
  4. データベースdumpもローカルに保管
  5. 未使用のWordPressはサーバから削除

やったこと

コアファイルの正常化

WordPressのコアファイルを公式配布の同バージョンで上書きする。
https://ja.wordpress.org/download/releases/
現在のバージョンは、wp-includes/version.phpを確認する。
wp-includes、wp-adminはすべて上書きし、正規の内容をアップロードしなおして、
トップレベルのwp-config.php以外については差分を取りながら行う。

※この時はサイトの停止を控えるために上書きしましたが、wp-include、wp-adminの内部を駆除するためには、
一度ディレクトリごと削除してから公式配布物を再アップロードするのが正しい対応となります。

DB内データの簡単な検索

WordPressにアクセスしたときにリダイレクトがかかる原因を想像しながら、
投稿本文に加工された可能性は低いが、DBダンプを転送先ドメイン名、encode、evalで軽く検索。
→何も出ず

ファイルのチェック

次に、ファイル改ざんを確認していく。
ドキュメントルートの.htaccessとindex.phpを確認

.htaccess
<FilesMatch ".(py|exe|php)$">
 Order allow,deny
 Deny from all
</FilesMatch>
<FilesMatch "^(about.php|radio.php|index.php|content.php|lock360.php)$">
 Order allow,deny
 Allow from all
</FilesMatch>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

どう見ても前半がおかしい。

そしてこの情報は信じてはいけない。
https://wordpress.org/support/topic/wordpress-website-create-htaccess-files-automatic-in-all-folder/

続いてindex.phpは…

もともとのファイルの前半に不正なコードが挿入されている。
index.phpはパーミッションが444に設定されており、FTPクライアントによっては 正常なコードに上書きできていないことに気づきづらいので注意
index.phpと.htaccessの問題のコードを取り除くと、サイトにアクセスできるようになる。

ちなみに、.htaccessだけを修正しても、
index.phpに書かれたコードの処理で「サイトに誰かがアクセスしてきたら」.haccessが不正なものに上書されるので、必ずindex.phpを先に始末すること。

いったんこれで、不正サイトへのリダイレクトは改善された。

本題

どうやってこれを追加したのか?が不明なので、かぎの掛かっていない入口が開いたまま。
幸いなことに、このWordPressサイトはコアがサブディレクトリにインストールされているため、
ドメインルートにあるファイルが少なく、見通しが良かった。

ディレクトリレイアウト
/www
  - /domainA.com
    - /blog (ここにWordpressがインストールされている)
    - .htaccess
    - .htaccess__  <--- だらしないなあ…
    - .htaccess___ <--- だらしないなあ…2
    - index.php
    - about.php    <--- .htaccessにあったとおりなので削除
    - wp-info.php  <--- ん??
  - /domainB.com
  - /domainC.com
  - /domainD.com
   ....

.htaccess各種はすべて同じ容量・内容なので削除する。

ここにあるはずのないwp-info.phpが怪しい。

外部から好き勝手出来る感じなので、削除する。

wp-info.phpはどうしてここにあるのか?は相変わらず不明だった。
(WordPressならよくあること、ではなくて、10年くらいこの仕事してるけどこういった改ざんは初めて見た。)

また、ほかの静的HTMLのサイトが置かれているドメインディレクトリにも、
wp-info.phpやwordpressのような.htaccessが設置されていたので、
WordPressを狙ったものがサーバに設置されてから増殖したようだった。
同じように修正と削除で対応をする。

続いて、www以下をローラー作戦。

ここからはSSHで作業

主に使うもの(レンタルサーバでも使えるもの、解説はふんわり)

find ファイルやディレクトリ名で検索する
オプションは
-type f ファイルのみ検索。ディレクトリのみ調べるときは-type d
-size 例えば10151byteのファイルサイズを調べるときには、-size 10151c
-name ワイルドカードを組み合わせて部分一致。.htaccess___をヒットさせる例)-name ".htaccess*"
詳しくは https://qiita.com/hana_shin/items/d1b420568d3c2223bd07

grep ファイルの中に含まれる文字列を検索して、ファイル名を教えてくれる
オプションは、
-l ファイル名のみ表示
-r ディレクトリ再帰
-i 大小文字区別なし
詳しくはhttps://qiita.com/hirohiro77/items/771ffb64dddceabf69a3

怪しいファイルの所在を確認して削除する

改ざんされた.htaccess
find . -type f -size (改ざんされた.htaccessのbyte)c -name ".htaccess*"
→WordPressで使用している場合は正常なものに書き換える。

不正コードが追加されているindex.php(以降、検索に使う文字列は、内容の特徴をつかんでいればなんでもいい)
grep -lri "OO_0O_O0_0" ./*
→問題のあるスクリプト部分を取り除く

不正なwp-info.php
grep -lri "PEZpbGVzTWF0Y2gg" ./*
→削除する

改ざんされた.htaccessで挙げられていたもの

ファイル名の例)lock360.php(ちなみに、中国語のコメント付き)
grep -lri "400 days" ./*
→削除する

ファイル名の例)about.php、radio.php、content.php、index.php
これらは同じソースをもとに、改行とインデントを変えたりしてファイルサイズを微妙に変えているので、注意する。
grep -lri "Mini Shell" ./*
※ちなみにこれで、.htaccessに記載がなく、wordpressのプラグインファイルに偽装した別のものも見つかった。
→削除する

だんだん一覧を眺めてつかめてきたら、容量やファイルの特徴で細かく調べていく。

これらの作業は、アップロード元データや、汚染前のバックアップがあれば、
ファイルDiffするだけでわかるのだけど、今回それがほとんど残っていないので大変な手間だった。
かろうじて1サイトのテーマファイルがあったので、差分をとってみるも、テーマファイルは大丈夫そうだった。

さて、以上のように検出して、明らかに不正なものとわかるものは削除する。

ドメインを横断して、ディレクトリの深いところにまで、
同じようなファイルサイズ、ファイル名でPHPファイルが設置されていた。
その数12,000以上。

不活性化であれば拡張子をとるリネームでもいいのだけど、今回は残るよりええやろの気持ちでほとんどを削除した。
検索結果に除外したいものがないところまで絞り込んで、慎重かつ豪快にやった。
find . -type f -name "ファイル名" -size (ファイルサイズ)c | xargs rm -rf

削除・修正したはずのファイルがロールバックしていることがある

不正なスクリプトを削除しきれていないうちは、ひどいときには分単位でファイルの再上書き・新規作成が行われていた。

変更を監視する

www以下でこのコマンドを実行すると、実行時点で2021-01-21 21:00以降に追加更新のあったファイルがわかる。
date && find . -type f -newerct "2021-01-20 21:00" -ls

怪しいファイルの削除を開始した時間から調べて、
これをCRONでまわして、ドキュメントルート外にログ保存する。

何か追加されたときには、速やかにスクリプトが実行されないようにして、ファイルの上書きもできないようにする。
0byteにしたり、パーミッション444にしたり。

.htaccess
RewriteEngine On
RewriteCond ^filename\.php$ [R=404,L]

とかでも。
なんならアクセスしてきたIPアドレスを記録するのに使ってもいいかも。

エラーログから、アクセス拒否するIPアドレスを探す

エラーログに「削除されたファイル」へのアクセスが乗る頃なので確認する。
ファイル名でフィルタして、出てきたIPアドレスを片っ端からdenyする。

.htaccess
Order Allow,Deny
Allow from all
Deny from xxx.xxx.xxx.xxx
Deny from xxx.xxx.xxx.xxx
...

10個あるドメインすべてを巡回しているものがほとんどだったので、すべてのサイトに設置した。

これまでの一連の処理で、数分単位でおきていたファイルの書き換えやファイルの追加がおさまった。
6時間ほど、ファイルに変化がなかった。

自動更新で何かが?

寝て起きて変更監視ログを見ると、WordPress 5.6のサイトで、午前3時台に、あるプラグインと、別のプラグインの言語ファイル、ドメインルートに見慣れないファイルが作成されていた。

その他のドメインルートにも、wp-info.phpが復活していた。

  1. 言語ファイル
    • 内容は正常そうなのでそのまま
  2. プラグイン:6つあるファイルすべてがSid Gifari Shell UplaoderというWeb Shellの一種で、意図してインストールしたものだとしてもEvil。
    • 問答無用で削除
  3. ルートの見慣れないファイル
    • 内容を見て削除
  4. wp-info.php
    • 内容を無害化してパーミッション400でおいておく。

ちなみに、2のプラグインは、翌日同じ内容で新しい名前のディレクトリで復帰していた。
原因を調査するにも時間がかかりそうなので、さすがにこちらは、一旦閉鎖することになった。

その他のサイトは改ざんが落ち着き動作が安定したため、
WordPress4.9とプラグインをアップグレードして、様子見ということになった。

WordPressを安全に使うために

  • パスワードの桁数・固さは設定できる最大の固さになるようにしよう
  • 常に最新のバージョンを使おう
  • 自動更新に耐えうるカスタマイズをしよう
  • WordPress関連の脆弱性情報をキャッチアップしよう
  • 健康な状態のファイルとDBのバックアップは必ず取っておこう
  • パーミッション設定はしっかりとしよう
  • WordPressと同じユーザーアカウントで動作する別のドメインのサイトは、ファイル構造=URLは盗み見られるものだと覚悟しよう。
  • アクセス制限もかけずに秘匿ファイルを保存してはいけません。

WordPress公式推奨パーミッション設定
https://ja.wordpress.org/support/article/changing-file-permissions/#%e3%83%91%e3%83%bc%e3%83%9f%e3%83%83%e3%82%b7%e3%83%a7%e3%83%b3%e8%a8%ad%e5%ae%9a%e4%be%8b

続くかも

だいぶ長くなってしまったので、「不正なソースコードを読んでみた編」は別の投稿にまとめようと思います。
ソースとアクセスログを合わせて理解が深まりました。