10日以上経ってそろそろ WP4.7の脆弱性の話もあまり聞かなくなってきました。が、前回書ききれてなかったJSやPHPコードが実行される心配はなかった理由について。ちょっとだけ。
今北産業
- WP 4.7ではコンテツインジェクションの脆弱性が存在した
- これを利用した攻撃は世界中で行われ、いまだに放置されているサイトも有る
wp_kses
のおかげで任意コード実行は免れた
脆弱性は脆弱性だけど
先週話題になったWordPresssの脆弱性。だれもが簡単なコード(HTTPリクエスト)でサイト上のコンテンツを変更できてしまうため大変問題になったものの、いまでも被害を受けたまま更新されずに放置されているサイトがまだまだ多数見つかります。
もちろんコンテンツが書き換えられればそれだけでいくらでも悪用出来てしまうので(フィッシングだとか、SEOリンクだとか……)十分問題なのですが、あくまでも対象はコンテンツのみであり、悪意のある攻撃者が任意のPHPコード(Exec-PHPのようなプラグイン利用時)やJSのコードを埋め込むという危惧されていた問題は普通は起こらないようになっていました。
もしこれらが発生していれば完全に無法地帯、なんでもできてしまうといっても過言ではありません。もう終了のお知らせなんてレベルでもないです。
防げた仕組み
権限 (has_cap)
WordPressでは権限グループ(role)ごとに機能権限を持っています。管理者(admin)はほぼすべての権限を持ち、非ログインユーザーは一切の権限を持たないと言った具合に。
ユーザーの種類と権限 – WordPress Codex 日本語版
今回の脆弱性ではユーザーがログインしていなくても「項目を編集する権限があるか」の検証をすり抜けてしまうというものでしたが、もちろんユーザー認証はおこなっています。「非ログインユーザーでもAPIを利用できる」だけです。
たとえば不正なX-WP-Nonce
ヘッダーを送ってリクエストすると403が返されますし、非ログインでAPIが使えなければGET系のAPIやコメント投稿のAPIを閲覧者が使うことが出来ません。穴が存在していたことはともかく、設計自体は然るべきものです。
で、デフォルトで編集者以上のユーザーはunfiltered_html
というcapを持ちます。
wp_kses
WPを触っている人ならご存知でしょうが、WPにはwp_kses
という指定のHTML以外を除去するサニタイズ機能が用意されています。例えばコメントに入力できるHTMLって制限されますよね。(使えないタグは除去されたり)これのおかげです。1
wp_ksesが実行されるのはもちろんコメントだけではありません。unfiltered_html
権限を持たないユーザーが投稿などのコンテンツを保存する時にwp_kses
を行うフィルターがWPの初期化時や内部でのユーザー切替時にセットされます。
ログインしていないユーザーは権限を持たないので当然このフィルターが適用されるわけですね。当然ながら名前の通りsanitize evli scriptsなのでscript
タグが存在していれば除去されます。2PHPコードがリクエストに含まれていた場合、それが閉じているコード(<?php ~ ?>
だったら許可されていないなタグとして除去され、閉じていなければ<php
のようにエスケープされるはずなので結果としてこれらのコードが実行されることはありません。
手元で試しにいくつかの有り得そうなパターンをいくつか試してみたところ見事にすべてサニタイズされ無力でした。あっぱれ。
ところでインラインCSSもサニタイズされる
コンテンツインジェクションの問題で上がった話題として、特定サイトを狙った攻撃の場合、サイトにすっかり馴染んだスタイルのコンテンツがこっそり埋め込まれていたりして管理者すら気付かないことがあるんじゃないか、という話題が出てきました。
例えばサイトで使われているCSSのクラスを使われればそれはどうしようもないかもしれません……たとえばBootstrapを使ってたらこれだけでボタンが置けてしまいますね。
1 |
<button class="btn">ぼたん</button> |
class
属性はデフォルトではサニタイズ対象外のようなのでなんともいえなかった……んですがインラインスタイルはフィルタがかけられ、使用できる項目は絞られます。
たとえばボタンっぽいCSSのリンクを書いてみます。
1 |
<a href="#" style="display:inline-block;padding: 5px; border-radius: 3px;background: black; color: white;width: 100%;text-align:center">ボタンっぽい</a> |
これを普通に貼ると以下のようになります:
が、ksesがかかっているとそうはいかず、こうなります。
いくつかのCSSが欠けたのが分かりますね。HTMLをみてみましょう。
1 |
<a href="#" style="padding: 5px;background: black;color: white;width: 100%;text-align:center">ボタンっぽい</a> |
6つ指定したうちのdisplay
, border-radius
, が今回は取り払われました。このように、許可されたスタイル以外はインラインで指定できないようです。(許可するプロパティはsafe_style_css
フィルターで変更可能)
div
やfloat
, clear
などは使えるのでサイトレイアウトを崩すぐらいは簡単にできてしまう気もしますが、ある程度制限されるようですね。
まとめ
WordPressでは信頼できないユーザーの投稿にデフォルトで然るべきサニタイズが行われるようになっていたため、予期せぬトラブルの盾となって攻撃によって起きかねない悪夢から今回はなんとか守ってくれました。
が、もちろんこれは「デフォルトの場合」であって、もし何らかの理由でサイト管理者がこのサニタイズを無効化していたりし、そこに本文中のPHPを実行するプラグインが入っていたりすると、それはすべての終わりです。
また、今回はたまたまサニタイズにより守られましたが、必ずしも通用するわけではありません。もし次にコンテンツインジェクションが可能な問題が発生した際はサニタイズが行われないレイヤーかもしれませんし、そうなればJSもPHPも実行され放題になりえません。
PHPが任意実行されればそれはサーバーが乗っ取られたようなもの。Exec-PHPのようなプラグインを使っている人はそれを理解しておきましょう。というか使わないのが最善です。その仕組自体が脆弱です。
あと、毎回言ってるけど自動更新はちゃんと動くようにしておきましょうね。コケているようならサーバーの乗り換えどきかもしれません。
ではまた。
- ところでksesというのは既存のUlf Harnhammarによるものをベースにしていて、ksesが何の略かというと kses strips evil scripts…… あぁ、再帰している…… ↩
- デフォルトで実際に許可されているタグ一覧はこの辺りを読むと確認できます。 ↩