DockerでたてたWordPressにWPScanをかけて修正方法を模索してみた

[シーエー・アドバンス Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ca-advance) 6日目の記事です。

こんにちは。
シーエー・アドバンス技術統括本部の @asami-H-Ishi です。
開発未経験で入社して3年目ですが、2年くらい診断員として業務に携わってきました。
現在は診断チームと開発側の脆弱性診断に関わる調整業務を担当しています。

# はじめに

弊社では、WordPressで作成されたWebサイトに対して脆弱性診断を実施する際、下記のように診断しています。

- Burp Suite Pro を使った通常どおりの診断
- WPScanを利用した診断
- WPScanで見つけられないWordPress固有の観点の調査

今回は、WPScanの診断結果から修正対応するまでの流れについて理解を深めるため、検証してみようと思いました。

そこでこの記事では、
1. Dockerでローカル環境にWordPressをたてる
2. ローカル環境にたてたWordPressにWPScanをかける
3. WPScanの結果から、診断時に指摘する事項を確認する
4. 「指摘事項」について修正対応をする
5. 再度WPScanをかけて（再診断）修正できているか確認する
という流れを追います。

※[WPScan](https://wpscan.org/)はRubyで書かれたWordPressの脆弱性スキャンツールです。
※使い方次第では攻撃ツールになりえますので、自分が管理運営していないサイトへのWPScanのツールはやめときましょう。

## 用意するもの（わたしの環境）

- Docker for Mac
- ターミナル
- テキストエディタ（VSCode）

# 1. Dockerでローカル環境にWordPressをたてる
下記のymlファイルを用意します。
※パスワードを直接記載していますが、簡単に試せるようにこの形にしています。
※実際はenv_fileを利用するなど、ymlファイルとは別でパスワード等の管理を行うのが望ましいです。

```yml:docker-compose.yml
version: '3'
services:
  wordpress:
    image: wordpress:latest
    ports: 
      - "8080:80"
    environment:
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: mysql_user
      WORDPRESS_DB_PASSWORD: mysql_pw
    restart: always
    depends_on:
      - mysql
  mysql:
    image: mysql:5.7
    environment: 
      MYSQL_ROOT_PASSWORD: root_pw
      MYSQL_DATABASE: wordpress
      MYSQL_USER: mysql_user
      MYSQL_PASSWORD: mysql_pw
    restart: always
    volumes:
      - mysql_data:/var/lib/mysql
volumes:
    mysql_data:
```

ターミナルで下記コマンドを叩きます。

```
docker-compose up
```
ちょっと時間がかかりますが、wordpressのlatestイメージからあれこれいっぱい用意してくれていることがわかるので、眺めているとDockerイメージありがとう、という気持ちになります。

Webブラウザで `localhost:8080` にアクセスするとWordPressのインストール画面が表示されます。

WordPressのインストールとログインが完了すると、WordPressのダッシュボードに入ることができます。
せっかくなので、ダッシュボードから何か新規投稿して見てみましょう。

さっき `docker-compose up` したので、コンテナ実行中は他のコマンドを入力することができません。
バックグラウンド実行しておけばそんな事態には陥らないので、次からそうしたいと思います。

いったん今立ち上げているWordPressを `control+C` で止めます。

```
Stopping study_wordpress_1 ... done
Stopping study_mysql_1     ... done
```
コンテナを止めたので、WordPressを開いていたブラウザ更新すると何も表示されなくなりました。
もう一度、今度は `docker-compose up -d` でバックグラウンド実行します。
さっきと違って特に実行状況について表示されず、コマンド入力ができる状態になっていると思います。

また、 `docker-compose ps` でプロセス表示ができます。
下記のように、Statusが `Up` になっていれば実行中で、 `docker-compose stop` でコンテナを止めている時は `Exit 0` になります。

```
      Name                     Command               State          Ports
---------------------------------------------------------------------------------
study_mysql_1       docker-entrypoint.sh mysqld      Up      3306/tcp, 33060/tcp
study_wordpress_1   docker-entrypoint.sh apach ...   Up      0.0.0.0:8080->80/tcp
```
`docker-compose down` でコンテナを破棄した場合は下記のとおりですので、自分がコンテナをどうしたか忘れてしまうタイプの方（同志）は一度 `docker-compose ps` で確認してみるといいかもしれません。

```
Name   Command   State   Ports
------------------------------
```

# 2. ローカル環境にたてたWordPressにWPScanをかける

まず、`ifconfig en0` でローカルネットワークのIPを確認します。
`inet` の後ろに書かれているのが、今WordPressを立ち上げているローカル環境のIPになります。

下記コマンドでWPScanを開始します。

```
docker run -it --rm wpscanteam/wpscan --url http://<ifconfigで確認したIP>:8080 -e vp --wp-content-dir wp-content
```
下記のような結果が出ました。(IP部分は内部IPだけど適当に伏せてます)

```
_______________________________________________________________
        __          _______   _____
        \ \        / /  __ \ / ____|
         \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
          \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \
           \  /\  /  | |     ____) | (__| (_| | | | |
            \/  \/   |_|    |_____/ \___|\__,_|_| |_|

        WordPress Security Scanner by the WPScan Team
                       Version 3.4.0
          Sponsored by Sucuri - https://sucuri.net
      @_WPScan_, @ethicalhack3r, @erwan_lr, @_FireFart_
_______________________________________________________________

[+] URL: http://xxx.xx.xxx.xx:8080/
[+] Started: Wed Dec  5 08:46:06 2018

Interesting Finding(s):

[+] http://xxx.xx.xxx.xx:8080/
 | Interesting Entries:
 |  - Server: Apache/2.4.25 (Debian)
 |  - X-Powered-By: PHP/7.2.12
 | Found By: Headers (Passive Detection)
 | Confidence: 100%

[+] http://xxx.xx.xxx.xx:8080/xmlrpc.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%
 | References:
 |  - http://codex.wordpress.org/XML-RPC_Pingback_API
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner
 |  - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access

[+] http://xxx.xx.xxx.xx:8080/readme.html
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] WordPress version 4.9.8 identified (Latest, released on 2018-08-02).
 | Detected By: Emoji Settings (Passive Detection)
 |  - http://xxx.xx.xxx.xx:8080/, Match: 'wp-includes\/js\/wp-emoji-release.min.js?ver=4.9.8'
 | Confirmed By: Meta Generator (Passive Detection)
 |  - http://xxx.xx.xxx.xx:8080/, Match: 'WordPress 4.9.8'

[i] The main theme could not be detected.

[+] Enumerating Vulnerable Plugins

[i] No plugins Found.

[+] Finished: Wed Dec  5 08:46:07 2018
[+] Requests Done: 20
[+] Cached Requests: 3
[+] Data Sent: 3.436 KB
[+] Data Received: 325.479 KB
[+] Memory used: 56.004 MB
[+] Elapsed time: 00:00:01
```

# 3. WPScanの結果から、診断時に指摘する事項を確認する

結果をひとつひとつ見てみます。

```
[+] http://xxx.xx.xxx.xx:8080/
 | Interesting Entries:
 |  - Server: Apache/2.4.25 (Debian)
 |  - X-Powered-By: PHP/7.2.12
 | Found By: Headers (Passive Detection)
 | Confidence: 100%
```

これはどちらも、ヘッダから「サーバー情報が露出してますよ」「PHPのバージョン情報が露出してますよ」ということなので、指摘しています。
対策としては、どちらもバージョン情報は隠してください、というものになります。
また、どちらも脆弱性のあるバージョンじゃないかを確認し、もし脆弱性のあるバージョンを使用している場合は、最新バージョンに変更しないと脆弱性を付かれる可能性があるので修正したいところです。

```
[+] http://xxx.xx.xxx.xx:8080/xmlrpc.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%
 | References:
 |  - http://codex.wordpress.org/XML-RPC_Pingback_API
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner
 |  - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access

[+] http://xxx.xx.xxx.xx:8080/readme.html
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%
```

これはどちらも `xmlrpc.php` と `readme.html` にアクセスすることができています、という報告です。

xmlrpc.phpは、標準の管理画面以外からでもAPIを利用して記事の投稿を行うことができるようにする機能です。
参照：https://www.sasukepg.jp/wordpress/1917/

readme.htmlはWordPressのバージョンなどの基本情報が記載されているファイルです。
第三者からバージョン情報が丸見えになってしまい、万が一脆弱性のあるバージョンのWordPressを使用していると、バージョン情報が攻撃の手がかりになってしまいます。

どちらも必要のないファイルなので、削除しておくことを推奨しています。

```
[+] WordPress version 4.9.8 identified (Latest, released on 2018-08-02).
 | Detected By: Emoji Settings (Passive Detection)
 |  - http://xxx.xx.xxx.xx:8080/, Match: 'wp-includes\/js\/wp-emoji-release.min.js?ver=4.9.8'
 | Confirmed By: Meta Generator (Passive Detection)
 |  - http://xxx.xx.xxx.xx:8080/, Match: 'WordPress 4.9.8'
```
これは今使っているWordPressのバージョンが4.9.8で、2018年8月2日にリリースされた最新バージョンだ、ということが、絵文字設定およびメタジェネレータからわかりましたよ、という内容です。
これも使っているWordPressのバージョン情報は見えない方が、攻撃する側への手がかりを減らせるので、隠したいところです。

# 4. 「指摘事項」について修正対応をする

対応内容は下記の通り。

- PHPのバージョン情報を隠す
- xmlrpc.php と readme.html を削除する
- WordPressのバージョン情報を隠す
- Apacheのバージョン情報を隠す

このうち、

- WordPressのバージョン情報を隠す
- Apacheのバージョン情報を隠す

については修正方法の模索が間に合わなかったので、次回(12/15も担当しています)の記事で書けるようにしたいと思います…(´・ω・`)

## PHPのバージョン情報を隠す

下記Dockerfileを作ります。

```dockerfile:Dockerfile
FROM wordpress:latest

#phpのバージョン情報を隠す
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
RUN sed -i -e "s|expose_php = On|expose_php = Off|" "$PHP_INI_DIR/php.ini"
```

## xmlrpc.php と readme.html を削除する

xmlrpc.phpファイルとreadme.htmlファイルを削除するために下記をDockerfileに追記します。

```dockerfile:Dockerfile
 #不要ファイルを削除する
RUN rm -rf /usr/src/wordpress/readme.html /usr/src/wordpress/xmlrpc.php
```

ymlファイルを下記に書き換えます。

```yml:docker-compose.yml
version: '3'
services:
  wordpress:
    # image: wordpress:latest
    build: .
    ports: 
      - "8080:80"
    environment:
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_DB_USER: mysql_user
      WORDPRESS_DB_PASSWORD: mysql_pw
    restart: always
    depends_on:
      - mysql
  mysql:
    image: mysql:5.7
    environment: 
      MYSQL_ROOT_PASSWORD: root_pw
      MYSQL_DATABASE: wordpress
      MYSQL_USER: mysql_user
      MYSQL_PASSWORD: mysql_pw
    restart: always
    volumes:
      - mysql_data:/var/lib/mysql
volumes:
    mysql_data:
```

# 5. 再度WPScanをかけて（再診断）修正できているか確認する

## 一度立ち上げ直す

Dockerfileを作成してdocker-compose.ymlファイルでそれを読み込むように指示しているので、反映させるためにいったん立ち上げたDockerコンテナをボリュームごと破棄する必要があります。

`docker-compose down -v` でボリュームごと破棄します。

```
Stopping study_wordpress_1 ... done
Stopping study_mysql_1     ... done
Removing study_wordpress_1 ... done
Removing study_mysql_1     ... done
Removing network study_default
Removing volume study_mysql_data
```

`docker-compose up --build -d` でDockerfileの設定でコンテナをビルドします。
いくつかビルド内容が表示されますが、

```
Successfully built d32116641adf
Successfully tagged study_wordpress:latest
Creating study_mysql_1 ... done
Creating study_wordpress_1 ... done
```
と表示されたらビルド成功です。

改めて `localhost:8080`　にブラウザでアクセスして、WordPressのインストールを行います。

## WPScanをもう一度かける

意図したとおりに修正できているか、もう一度WPScanをかけてみます。

```
docker run -it --rm wpscanteam/wpscan --url http://<ifconfigで確認したIP>:8080 -e vp --wp-content-dir wp-content
```

結果は下記の通りでした。

```
_______________________________________________________________
        __          _______   _____
        \ \        / /  __ \ / ____|
         \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
          \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \
           \  /\  /  | |     ____) | (__| (_| | | | |
            \/  \/   |_|    |_____/ \___|\__,_|_| |_|

        WordPress Security Scanner by the WPScan Team
                       Version 3.4.0
          Sponsored by Sucuri - https://sucuri.net
      @_WPScan_, @ethicalhack3r, @erwan_lr, @_FireFart_
_______________________________________________________________

[+] URL: http://xxx.xx.xxx.xx:8080/
[+] Started: Wed Dec  5 10:41:36 2018

Interesting Finding(s):

[+] http://xxx.xx.xxx.xx:8080/
 | Interesting Entry: Server: Apache/2.4.25 (Debian)
 | Found By: Headers (Passive Detection)
 | Confidence: 100%

[+] WordPress version 4.9.8 identified (Latest, released on 2018-08-02).
 | Detected By: Emoji Settings (Passive Detection)
 |  - http://xxx.xx.xxx.xx:8080/, Match: 'wp-includes\/js\/wp-emoji-release.min.js?ver=4.9.8'
 | Confirmed By: Meta Generator (Passive Detection)
 |  - http://xxx.xx.xxx.xx:8080/, Match: 'WordPress 4.9.8'

[i] The main theme could not be detected.

[+] Enumerating Vulnerable Plugins

[i] No plugins Found.

[+] Finished: Wed Dec  5 10:41:37 2018
[+] Requests Done: 22
[+] Cached Requests: 2
[+] Data Sent: 3.535 KB
[+] Data Received: 113.031 KB
[+] Memory used: 53.281 MB
[+] Elapsed time: 00:00:01
```

意図したとおり、PHPのバージョン情報と　xmlrpc.phpおよびreadme.htmlを隠したり削除したりすることができました。

# おわりに

WordPressでのサイト構築にもいろいろな方法があると思うので、必ずしもこの記事で言及したような方法でのセキュリティ対応が可能とは限りません。
でも、この記事を読んだことで、「WordPressでサイト作成をしたらWPScanをかけてみて、対応の必要な結果が出ないか確認する、という選択肢が増えてくれると嬉しいです。

今回間に合わなかったApacheのバージョン情報と、WordPressのバージョン情報をjsとメタジェネレータから隠す方法については、12/15の記事の前半で言及したいと思います…検証が間に合えば…｡ﾟ(ﾟ´Д｀ﾟ)ﾟ｡

次回は、今回立てたDocker+WordPressの環境にBurp Suite ProでActiveScanをかけてどんなIssueが上がってくるか、という記事を書く予定です。