時系列で見る：WordPressを運用中のサーバが丸ごとPHPマルウェアに感染する流れ

本投稿は、[WordPressを運用中のサーバがまるごとPHPマルウェアに感染していた時の対応メモ](https://qiita.com/Ayutanalects/items/a359df4e4b445229fcf2) の続きとなります。

はてブやTwitterでの、有識者・経験者のコメントありがとうございます。根本解決のためには新規インストール＆作り直しが必要なのもごもっともです。
(あまり喋りすぎるとよくないので、甘んじるしかできないのももどかしいところではありますが…)

対応当時の、サイト止められない＋きれいなデータがどこにもない状況に理解を示してくださる方のコメントに救われました。

さて、コード読んでみたの前に、時系列を知る必要がありログをさらったのでまとめました。

# 前置きとか
## ログ、保存されていますか？
レンタルサーバによっては、デフォルトではアクセスログやエラーログをユーザがみられるようになっていないところもあります。
残っていなければ足跡をたどることもできません。定期的に確認をするサイクルよりも少し長めの期間で残しましょう。

### マルチドメインの場合：ホスト名（≒ドメイン名）を必ずログに記録するように 
S社の共用レンタルサーバーの場合、ログのデフォルト設定(？)ではホスト名の記録をしないようです。
これでは、経路やアクセスされたファイルの特定が困難になります。

設定を変えて、接続先のホスト名がわかるようにしておきましょう。
![2021-01-24_20h31_28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49852/df805a7b-f554-0d59-4604-35ed94672174.png)
残念なことに、私が連絡を受けて設定を変更する8日目までは、ホスト名なしでドメインルートディレクトリ以下のパスだけになっていました。
そのため、以下の時系列情報はディレクトリ名や前後の行動からの推測を含みます。

※余談ですが、
サーバによっては**ストレージ使用量が 80% を超えるとそもそもログの保存をしなくなる**ところもあるので、定期的なログ確認ついでに、ストレージ容量も時々確認したほうが良いです。

## 登場するサイトについて
- **ドメインA**
    - WordPress v4.9
    - WordPresコアはサブディレクトリに入っている (ドメインBとの見分けポイントになった)
    - プラグインディレクトリ特記事項
        - 運用開始時に[Acunetix WP Security](https://ja.wordpress.org/plugins/wp-security-scan/)の推奨設定にしていた
        - [Login Lockdown](https://ja.wordpress.org/plugins/login-lockdown/)はインストール済みだが有効ではなかった
    - 管理者アカウントはadmin以外で命名、全アカウントにランダムな文字列のパスワード割り当て済み
- **ドメインB**
    - WordPress v5.6
    - WordPresコアはルート直下に入っている
    - プラグインディレクトリ特記事項
        - [All-in-One WP Migration](https://ja.wordpress.org/plugins/all-in-one-wp-migration/)が有効だった
        - [all-in-one-wp-security-and-firewall](https://ja.wordpress.org/plugins/all-in-one-wp-security-and-firewall/)がインストール済みだが有効ではなかった
    - 管理者アカウントがadmin、パスワードは確認できず
- その他、非WordPressドメインC-J

## 攻撃者の行動について
GETの操作は、保管した不正ファイルのコードを使ってデコードやシミュレートができますが、
POSTの場合はそもそも何をしようとしていたログから伺うことができません。
少なくとも、使ったログのうち返送したサイズが一定でないPOSTが145件あり、
何をされたのかが全く分からない状態です（超怖い）

# ログから分かる時系列
とりあえず、これまでに分かっている不正ファイルにかかわった中でも、
特に頻繁に活動していた攻撃者(1)-攻撃者(6)のログ5600件超に注目してみていきます。
## 攻撃者(1) 到着～バックドア設置まで
### 1日目 
#### 5時半頃
攻撃者Aがサーバのいずれかのドメインに到着し、デイレクトリ名にアタリをつけてアクセス試行開始。
1時間おき2-3分の間に1サイトといった様子で巡回している。
（このようなログはどこのサイトでも見かけるはず）

```log:各ドメインで試されたディレクトリ名
/wordpress/
/wp/
/blog/
/new/
/old/
/backup/
/oldsite/
/back/
/2017/
/2018/
/div/
/temp/
```
1巡目：レスポンスを見て何らかの目星をつけている雰囲気
2巡目：HEADとGETで `/各ドメイン/xmlrpc.php`
3巡目：GET `/各ドメイン/wp-login.php` もうこの辺でWordPressがあるかどうかはわかっていそう。
※リダイレクト(302)で、ドメインAのWordPress管理画面URLが割れる。

2巡目の途中で、`おそらくドメインB/xmlrpc.php`に複数回POSTがあり、
最後に `APIでユーザー一覧` を参照してしばらく来なくなる。

※この時にユーザアカウントの何らかの情報を取得された可能性があるのかも…

**セキュリティプラグインのXMLRPC防御機能があれば防げた可能性がある。**

#### 16時頃 ～
攻撃者(1)によるドメインAのWordPressログイン試行開始。
ランダムに時間を空けて何度か訪れ、それぞれ10分～30分の間、1秒おきにPOSTを投げていた。

**インストール済みだったプラグイン「Login Lockdown」をはじめ、セキュリティ系プラグインを有効化しておくだけで防げた＆記録がとれた可能性がある**

### 2日目
#### 23時頃
攻撃者(1)、ドメインAのログイン試行をやめる。
ログインのためのPOSTはトータル3,560回ほど。
※ドメインAはログインされなかったようだ。

### 3日目
#### 15時頃
攻撃者(1)、`ドメインB/wp-login.php`へ接続して1回で管理画面のログインを成功させる。
管理画面からバックドアとなる`プラグイン型Web Shell(1)`をアップロード＆有効化。
その後`プラグイン型Web Shell(1)`に対して何度かPOST後、
`GET ドメインB/wp-info.php`(不正なファイル)をして以降しばらく来なくなる。

## 攻撃者(1)-攻撃者(6)による操作フェーズ
### 3日目
#### 17時半頃
攻撃者(2)が試行もなく`GET ドメインB/wp-info.php`(不正なファイル)を行う
/home/username/dir/subdirの構造を把握済みでPOST、GETを複数回を行う。

攻撃者(3)が試行もなく`GET 各ドメインルート/about.php`(不正なファイル)を複数回行う
攻撃者(2)と同様に、/home/username/dir/subdirの構造を把握済みでPOST、GET複数回行ったのち、
`/ドメインB/lock360.php`(不正なファイル)へ複雑にエンコードされたコマンドを投げ込むのを繰り返し、
様々なディレクトリ(特にimg/やimages/などの中）に`radio.php`、`content.php`を設置していったようだ。
　
#### 午後？
ドメインAへの接続不良やリダイレクトが発生したために、
依頼を受けたエンジニアが、ドメインA～ドメインJを修正した様子。
`.htacces`、`about.php`、`content.php`、`lock360.php`、`wp-info.php`と、`一部の(不審な)index.php`がアクセスされても動作しないように変更されたようだ。

このときに、ドメインBのプラグイン型WebShell(1)と、imgディレクトリなどに隠された一部の不正ファイルが残ってしまったようだ。
※もし、この時点で`各ドメイン/index.php`に不正なスクリプトが追加されていたら、
何度修正しても`各ドメイン/.htaccess`はロールバックしていたはずなので、この時点ではまだ追加されていないか、
あえて休眠状態だった可能性がある。
### 3日目 ～7日目
攻撃者(3)は各ファイルが削除されるまでの間、1日数回、不正なファイルを増殖させに来ていた様子。

不正ファイルとfavicon.icoを交互に見ているところがあり、ちょっと気になる。
(ユーザーエージェントが
`Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36`
なので、単純にドメインルートにあるfavicon.icoを見てる可能性もありそうだけど…。)

そのほか、攻撃者(4)が試行もなく`GET /各ドメインルート/about.php`(不正なファイル)を複数回行う。
## 対応をはじめてから
### 7日目 夕方
筆者に連絡が来て、
https://qiita.com/Ayutanalects/items/a359df4e4b445229fcf2
この時、ローラー作戦までを実施し、偶然見つけたプラグイン型Web Shell(1)を削除する。
ログの設定変更を行ったため？以降はどのドメインに対する操作かわかるようになる。

### 8日目
#### 5時頃
攻撃者(1)がドメインBのWordPress管理画面にログインし、`プラグイン型Web Shell(2)`をアップロード。

※この時は3日目と同じプラグイン名を使っている。`プラグイン型Web Shell(1)`が削除されていたので再アップロードしに来たのか、単純にWeb Shellの機能を更新しに来たのかは不明。

#### 8時頃
変更監視により、`プラグイン型Web Shell(2)`の存在に気付いて削除。

#### 17時代
攻撃者(5) 前触れもなくドメインBの`プラグイン型Web Shell(2)`にアクセスするも404
/wp-logi.php というタイポの様なログを残して以降接続なし。

攻撃者(3)これまで通り不正ファイルの操作をしようとして8つのURLがすべて404になる。
30分後にもう一度着て、先ほどと同じアドレスへ接続試行するが404

### 9日目
#### 3時頃
攻撃者(1)、ドメインBのWordPress管理画面にログインし、`プラグイン型Web Shell(3)`をアップロードしていく。

※3日目、8日目とは別のプラグイン名を使う（内容は同じ）
自動削除的な処理を回避することを想定しているのだろうか？

#### 6時頃
攻撃者(6)、前触れもなくドメインBの`プラグイン型Web Shell(3)`にアクセス。
直後のPOSTは6回。

#### 7時頃
変更監視により、`プラグイン型Web Shell(3)`を削除。
依頼者同意の上でドメインBのデータをサーバから削除。

#### 半日後
ドメインBへのアクセスを中心に、エラーログから抽出したIPアドレスを全拒否。

# その後の経過観察
問題があったWordPressサイトを閉鎖してから、今のところファイルの改変は見られませんが、
削除したサイト跡地へ、攻撃者(1)-(5)はIPアドレスを変えずにクロールしているようです。

各不正スクリプトはGoogleのsitemap情報を汚染しており、全アクセスの91%がGoogleの正規クローラによる404確認になってしまいました。
(PV数は攻撃1日目以前の+3100%ほど)

その他も色々ありますが、私個人はまだ気が抜けておらず胃に悪いです。

S社のバックアップ機能が有効になっていたので、攻撃直前のデータが残っているようでした。
それがあればなんとかなるかも…というところですが、すでに手を離れています。

# まとめ
攻撃者(1)によるドメインBのWordPressの管理者アカウントクラックが原因で、
管理画面からプラグインのアップロード＆有効化によりバックドアが設置され、
その他複数の操作が行われた形跡がある
↓
攻撃者(2)-(6)は、攻撃者(1)が作ったと思われる不正ファイルにいきなりアクセスしていて、
連携している様子。
↓
img/やcss/などにもphpファイルを仕込んでいるため、そもそも人力でFTPのみで対応するのはかなり難しいし、
sshが使える人であっても、いったんすべて削除してからきれいなもので再構築しなければ駆除ができないと思われる。

## 今回やるべきだった対応
1. 初動
    - **サイトへのアクセスを止める**
        - 対応中にも増殖するから
    - **パスワードを変更**
        - コンパネ・FTPアカウント、**WordPressの管理者アカウント**までは必須
            - 正規ログインは止めようがないから
            - ユーザーへの周知は新パスワード発行連絡でよいと思う。
    - **ホスティング会社に報告・調査依頼**
        - ただし、サイトの被害を受けているか、サーバの契約者でないと問い合わせができない場合もある
1. きれいな状態のファイルを確認(今回は対応当時手に入らなかった)
    - できれば納品データや制作時のデータ
        - サーバに上がっていたものは画像ファイル含め汚染されていない保証が難しい
1. 新サーバを確保して、新しく環境を作り直す
1. DBデータのクレンジングをして、新サーバへ移し、動作確認
1. ドメインDNSの切り替え

## 所感
- **WordPressはサイト単位で独立したDBに入れるべき**
- こういう時にマルウェアスキャナは有効かと思うが、それだけで安心はできず、変更監視、ログ監視は継続する必要がある
    - 共用レンタルサーバの場合はリソース監視についてはホスティング会社に依頼しないとできない範囲のこともある
- 例えば S社のWordPressインストーラを使うと自動的に入る[All In One WP Security & Firewall](https://ja.wordpress.org/plugins/all-in-one-wp-security-and-firewall/)や、[SiteGuard WP Plugin](https://www.jp-secure.com/siteguard_wp_plugin/) プラグインを使っていれば、回避できるタイミングがたくさんあった（ログインページ変更、画像認証、ログインロック、ログインアラート、XMLRPC防御）
    - 参考情報：https://qeee.jp/magazine/articles/8232
- マルチサイトの場合、ログにはホスト名(ドメイン名)を出力するように設定する必要がある
- POSTの内容が本命だと思うので、方々で指摘される通り**まだ終わってない状態**
 
## 少し話がそれますが…
### 製作者、発注者の方へ

**制作時に使うだけのプラグインは、運用が始まったら停止だけでなく、サーバから削除してください。**

また、WordPressに限らない話ではありますが、
特に初心者プログラマや、本業がWeb開発ではないフリーランスの方などは、
CMSのカスタマイズ時や、PurePHPのちょっとしたプログラムを作る場合に、

- GETでログイン情報を送っていたりとか、
    - ログや経路上に平文で...
- 秘匿ファイルを何のアクセス制限もかけずにドキュメントルート以下においてしまうとか、
    - 今回のような出来事をきっかけに、Google検索でヒットするようになるかも…
- そもそもSQL全文を外部から実行するために作られた何らかのバックエンド処理とか
    - データベース接続情報が同じDBの中のデータは…

といったことがあります。

徳丸 浩さんの「**[体系的に学ぶ 安全なWebアプリケーションの作り方](https://www.amazon.co.jp/dp/4797393165/r)**」や、
情報処理推進機構の「**[安全なウェブサイトの作り方](https://www.ipa.go.jp/security/vuln/websecurity.html)**」が理解・実践できるまで、
公開領域に置く物を運用しないでください。

自分もまだまだですが、その辺気を配って作っているので、同居するスクリプトの安易な実装のせいで、
ここからもっと大きな事案につながる可能性があるのは、何をおいても利用するユーザーのために避けたいです。

発注者の方は、**[安全なウェブサイトの作り方](https://www.ipa.go.jp/security/vuln/websecurity.html)** にあるチェックリスト等を活用して、
製作者とともにセキュリティに対する意識を高めていただければと…。

検品時に成果物の内容と製作者のスキルを見極められるのが一番ですが、
知識の不足は、テストサーバで「Web脆弱性診断ツール」を使うとか
有償・無償のセキュリティプラグインを導入するとか、信頼できる運用保守に課金をするとか、
ホスティング付き有償CMSの導入するとか、その他のものとWordPress専用サーバホスティングに分けて利用することでも、
補える場合があります。

### CMSサイトの運営者さんへ
やりたいことと競合するからといって、よくわからないのに勝手にセキュリティ系のプラグインを切らないでください。
有識者にサポートを求めてください。

ログインが面倒くさいとかでパスワードを簡単にしないでください。
頭で覚えられるくらいのパスワードは「簡単なもの」です。
パスワードマネージャ使ってください。息をするように多要素認証を導入するくらいでもいいと思います。

とりとめもなくなったけど以上です。