社会人から始めるペネトレーションテスト

情報セキュリティ系の話が主ですが、全く関係ない話を投稿することもあります。

WordPress 4.7/4.7.1 の Content Injection の脆弱性を確認する

先日、WordPress 4.7 および 4.7.1 における Content Injection の脆弱性が世間を騒がせました。脆弱性が「すでに存在する投稿を上書きする」という内容であるため、脆弱性診断士としてはお客様の環境に試すことが憚られる脆弱性です。しかし、検証を実施した結果、この脆弱性は投稿を実際に上書きをしなくても検出できることがわかりました。そこで今回の投稿では、これから脆弱性診断士を目指す人のことを意識して、この脆弱性の検出手法の解説を通じて「脆弱性診断士として気をつけなければならないこと」や「脆弱性を確認する手法の作成手順」がざっくり伝えられればと考えております。

どういう脆弱性なの?

WordPress 4.7 系から導入された REST API という機能に不備があり、WordPress で作成された Web サイトに投稿されている内容を上書きできるという脆弱性です。攻撃の手軽さや影響範囲から世間では大分騒がれましたが、なぜか CVE 識別子は割り当てられていません。詳しい内容に関してはその他の Web サイトで、様々な人が解説しているので割愛します。中でも徳丸さんのブログの記事に詳しく解説されておりますので、詳しく知りたい方は以下のリンク先をご参照ください。


脆弱性診断士として気を付けなければならないこと

脆弱性の有無を調査する」と言っても、本当になんでもやっていいわけではありません。少なくとも日本では、プロの脆弱性診断士はお客様のシステムに障害を引き起こしてしまわないように、細心の注意を払う必要があります。例えばお客様の Web サイトで SQL Injection が検出されたとしても、SQL Injection で過剰な攻撃を行い Web サイトのデータベースが全て消去されてしまえば、お客様に多大な損害を与えてしまうことが考えられます。
今回の WordPress脆弱性に関して言えば、脆弱性の有無を確認した結果として、お客様が運用している Web サイトのコンテンツが書き換えられてしまい、元の状態に普及出来なければ、お客様に損害を与えてしまうことになります。よって、今回の WordPress脆弱性脆弱性診断業務で安全に検出するために、「コンテンツを書き換えることなく脆弱性を検出する手法があるか」ということを検証する必要があるわけです。

脆弱性の確認手法の方針

突くことにより損害を与えてしまう可能性が高い脆弱性である場合は、「脆弱性が含まれているバージョンに固有な反応」を把握して、脆弱性の確認手法を確立する必要があります。

今回の検証環境

今回は以下の環境で検証を実施します。

[対象ホスト]
OS: Ubuntu 15.10, 64bit
Software: WordPress 4.6.3, 4.7, 4.7.1, 4.7.2
IP Address: 172.16.214.132

脆弱性が存在する場合と存在しない場合の反応差を明確にするために、複数のバージョンの WordPress を検証します。WordPress はいずれも初期設定の状態で検証しています。

まずは脆弱性の影響を受けるバージョンの範囲を確認する

脆弱性の確認手法を確立するための第一歩として、まずは脆弱性の影響を受ける範囲を確認しましょう。調べていただければわかると思いますが、今回の脆弱性は前述の通り、「WordPress 4.7 系から導入された REST API という機能」に存在する脆弱性です。また、脆弱性WordPress 4.7.2 で解消されました。よって、WordPress のリリース情報を照らし合わせると、当該脆弱性の影響を受けるバージョンは以下の 2 つであるということがわかります。

脆弱性の悪用に用いられる URI を把握する

今回の脆弱性を悪用する場合、脆弱性診断対象となる WordPress の以下の URL が利用されることが分かっています。

http(s)://[WordPress の REST API のホーム URL]/wp/v2/posts/[存在する投稿の ID]

以上の URL に対して送信するデータにて、不正なパラメータを id 要素に設定すれば、脆弱性をつくことができるようになるわけです。例えば投稿 ID が 1 である場合は、以下のような URL にリクエストが送信されます。(存在する投稿の ID を 1 としています)

http(s)://[WordPress の REST API のホーム URL]/wp/v2/posts/1/?id=1AAA

URL 末尾の AAA の部分は、文字列型の値であれば何でも構いません。「?id=1AAA」の部位は、WordPress での Permalink の設定(URI の書式設定)によって変わります。Permalink が Plain と設定されている場合(投稿の URL が http(s)://[WordPress のホームの URL]/?p=123 のような書式である場合)は、「&id=1AAA」となります。今回の脆弱性では以上のように、URL にて指定されている「投稿 ID」に、余分な文字を追加した値を id 要素に設定することで、脆弱性が発現します。

なるべく GET リクエストを用いる

POST メソッドを用いると、脆弱性診断対象の Web サイトの情報を書き換えてしまう恐れがあります。対象ホストに悪影響が出ないことがしっかりと確認できていない限りは、GET リクエストを用いて脆弱性の存在が確認できるよう、検証を進めましょう。

WordPressREST API のホーム URL

脆弱性を悪用するには、REST API のホーム URL が分かっていなければならないため、どこから調べるかを先に解説します。WordPress のホームの URL にアクセスすると、ソースコードの以下のタグの href に記載されています。

<link rel='https://api.w.org/' href='http://172.16.214.132/index.php/wp-json/' />

初期設定である場合は、以上のように「http(s)://[WordPress のホーム URL]/index.php/wp-json/」が REST API のホーム URL です。因みにですが、Permalink が Plain に設定されている場合は「http(s)://[WordPress のホーム URL]/?rest_route=/」となります。

脆弱性が存在するバージョンの反応を確認する

まずは脆弱性が存在するバージョンである WordPress 4.7 または 4.7.1 を検証環境にインストールして、実際に脆弱性が悪用できることを確認した上で、脆弱性の悪用に用いられる URI に GET リクエストを送信した際の反応を確認しましょう。WordPress は初期状態であるため、存在する投稿の ID は 1 のみです。
それでは WordPress 4.7 および 4.7.1 に対して、GET リクエストを送信した際の反応を確認してみましょう。とりあえず対象ホストから返送されるステータスコードを確認したいので、HTTP ヘッダの部分に着目して反応差を調べます。ステータスコードで判断できなそうな場合は、対象ホストから送信されるデータの内容も確認することにします。
先に、末尾の余分な文字を削除した正規の URL に対するリクエストの応答も確認しておきましょう。この場合は以下のように「200 OK」が返送されることが確認できます。

root@kali:~# curl --head "http://172.16.214.132/index.php/wp-json/wp/v2/posts/1/?id=1"
HTTP/1.1 200 OK
Date: Mon, 13 Feb 2017 14:20:18 GMT
Server: Apache/2.4.12 (Ubuntu)
X-Robots-Tag: noindex
Link: <http://172.16.214.132/index.php/2017/02/05/hello-world/>; rel="alternate"; type=text/html
X-Content-Type-Options: nosniff
Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages
Access-Control-Allow-Headers: Authorization, Content-Type
Allow: GET
Content-Type: application/json; charset=UTF-8

root@kali:~# 

次に、脆弱性の悪用に用いられる不正な URL に GET リクエストを送信すると、以下のように「200 OK」が返送されることが確認できます。

root@kali:~# curl --head "http://172.16.214.132/index.php/wp-json/wp/v2/posts/1/?id=1AAA"
HTTP/1.1 200 OK
Date: Mon, 13 Feb 2017 14:18:32 GMT
Server: Apache/2.4.12 (Ubuntu)
X-Robots-Tag: noindex
Link: <http://172.16.214.132/index.php/2017/02/05/hello-world/>; rel="alternate"; type=text/html
X-Content-Type-Options: nosniff
Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages
Access-Control-Allow-Headers: Authorization, Content-Type
Allow: GET, POST, PUT, PATCH, DELETE
Content-Type: application/json; charset=UTF-8

root@kali:~# 

とりあえず、脆弱性が存在する WordPress 4.7 および 4.7.1 では、正規の URL でも不正な URL でも、GET リクエストに対して「200 OK」が返送されることが確認できました。

脆弱性に対応されたバージョンでの反応を調べる

次に、脆弱性が修正された WordPress 4.7.2 で同様に反応を調べてみましょう。正規の URL にリクエストを送信した場合は、当然「200 OK」が返送されることが確認できます。

root@kali:~# curl --head "http://172.16.214.132/index.php/wp-json/wp/v2/posts/1/?id=1"
HTTP/1.1 200 OK
Date: Mon, 13 Feb 2017 14:32:47 GMT
Server: Apache/2.4.12 (Ubuntu)
X-Robots-Tag: noindex
Link: <http://172.16.214.132/index.php/2017/02/06/hello-world/>; rel="alternate"; type=text/html
X-Content-Type-Options: nosniff
Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages
Access-Control-Allow-Headers: Authorization, Content-Type
Allow: GET
Content-Type: application/json; charset=UTF-8

root@kali:~#

では、脆弱性の悪用に用いられる不正な URL に GET リクエストを送信するとどうなるでしょうか。

root@kali:~# curl --head "http://172.16.214.132/index.php/wp-json/wp/v2/posts/1/?id=1AAA"
HTTP/1.1 400 Bad Request
Date: Mon, 13 Feb 2017 14:33:50 GMT
Server: Apache/2.4.12 (Ubuntu)
X-Robots-Tag: noindex
Link: <http://172.16.214.132/index.php/wp-json/>; rel="https://api.w.org/"
X-Content-Type-Options: nosniff
Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages
Access-Control-Allow-Headers: Authorization, Content-Type
Allow: GET
Connection: close
Content-Type: application/json; charset=UTF-8

root@kali:~# 

なんと、「400 Bad Request」が返送されます。これは良い結果と言えます。脆弱性が存在するバージョンの場合は「200 OK」、脆弱性に対応されたバージョンの場合は「400 Bad Request」という特徴的なステータスが返送されるため、脆弱性の有無が判別しやすいです。

脆弱性が発現する前のバージョンも調べる

当然と言えば当然なのですが、そもそも REST API が実装されていないため REST API のホームの URL が存在しません。よって以下のように、正規の URL であっても不正な URL であっても「404 Not Found」が返送されます。

root@kali:~# curl --head "http://172.16.214.132/index.php/wp-json/wp/v2/posts/1/?id=1"
HTTP/1.1 404 Not Found
Date: Mon, 13 Feb 2017 14:54:23 GMT
Server: Apache/2.4.12 (Ubuntu)
X-Robots-Tag: noindex
Link: <http://172.16.214.132/wp-json/>; rel="https://api.w.org/"
X-Content-Type-Options: nosniff
Access-Control-Expose-Headers: X-WP-Total, X-WP-TotalPages
Access-Control-Allow-Headers: Authorization
Content-Type: application/json; charset=UTF-8

root@kali:~# 

脆弱性の検出

以上の結果より、以下の URL に GET リクエストを送った結果、「200 OK」が返送されれば対象の WordPress に当該脆弱性が含まれていることを確認できます。

http(s)://[WordPress の REST API のホーム URL]/wp/v2/posts/[存在する投稿の ID]/?id=[存在する投稿の ID][適当な文字列]

ただし、前述の通り、以上の URL の「?id=」の部分は、Permalink が Plain に設定されている場合は「&id=」に変更する必要があります。自動化して脆弱性を検出するスクリプトを作成したい場合は、例として以下のアルゴリズムに沿ってプログラムを作成すると良いでしょう。

  1. WordPress のページにアクセスして REST API のホームの URL を抽出する
  2. http(s)://[WordPressREST API のホーム URL]/wp/v2/posts/ にアクセスして存在する投稿の ID を調べる
  3. http(s)://[WordPressREST API のホーム URL]/wp/v2/posts/[存在する投稿の ID]/?id=[存在する投稿の ID][適当な文字列] にアクセスしてステータスコードを調べる

まとめ

プロの脆弱性診断士として脆弱性の有無を調査する場合は、診断対象のホストに損害を与えない配慮が必要です。今後脆弱性診断士を目指す皆様、並びに現在脆弱性診断士として活躍している皆様にとっても、本投稿の内容が参考になれば幸いです。