WordPress 4.7 のREST API脆弱性がコンテンツインジェクションで済んだわけ

10日以上経ってそろそろ WP4.7の脆弱性の話もあまり聞かなくなってきました。が、前回書ききれてなかったJSやPHPコードが実行される心配はなかった理由について。ちょっとだけ。

WP4.7 のREST APIの深刻なバグについて ~検証環境~


今北産業

  • WP 4.7ではコンテツインジェクションの脆弱性が存在した
  • これを利用した攻撃は世界中で行われ、いまだに放置されているサイトも有る
  • wp_ksesのおかげで任意コード実行は免れた

脆弱性は脆弱性だけど

先週話題になったWordPresssの脆弱性。だれもが簡単なコード(HTTPリクエスト)でサイト上のコンテンツを変更できてしまうため大変問題になったものの、いまでも被害を受けたまま更新されずに放置されているサイトがまだまだ多数見つかります。

もちろんコンテンツが書き換えられればそれだけでいくらでも悪用出来てしまうので(フィッシングだとか、SEOリンクだとか……)十分問題なのですが、あくまでも対象はコンテンツのみであり、悪意のある攻撃者が任意のPHPコード(Exec-PHPのようなプラグイン利用時)やJSのコードを埋め込むという危惧されていた問題は普通は起こらないようになっていました。

WordPress4.7と4.7.1の脆弱性を悪用した攻撃が多発。今後はSEOスパムに注意が必要

もしこれらが発生していれば完全に無法地帯、なんでもできてしまうといっても過言ではありません。もう終了のお知らせなんてレベルでもないです。

防げた仕組み

権限 (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()

wp_ksesが実行されるのはもちろんコメントだけではありません。unfiltered_html権限を持たないユーザーが投稿などのコンテンツを保存する時にwp_ksesを行うフィルターがWPの初期化時や内部でのユーザー切替時にセットされます。

kses_init_filters()

ログインしていないユーザーは権限を持たないので当然このフィルターが適用されるわけですね。当然ながら名前の通りsanitize evli scriptsなのでscriptタグが存在していれば除去されます。2PHPコードがリクエストに含まれていた場合、それが閉じているコード(<?php ~ ?>だったら許可されていないなタグとして除去され、閉じていなければ&lt;phpのようにエスケープされるはずなので結果としてこれらのコードが実行されることはありません

手元で試しにいくつかの有り得そうなパターンをいくつか試してみたところ見事にすべてサニタイズされ無力でした。あっぱれ。

ところでインラインCSSもサニタイズされる

コンテンツインジェクションの問題で上がった話題として、特定サイトを狙った攻撃の場合、サイトにすっかり馴染んだスタイルのコンテンツがこっそり埋め込まれていたりして管理者すら気付かないことがあるんじゃないか、という話題が出てきました。

例えばサイトで使われているCSSのクラスを使われればそれはどうしようもないかもしれません……たとえばBootstrapを使ってたらこれだけでボタンが置けてしまいますね。

class属性はデフォルトではサニタイズ対象外のようなのでなんともいえなかった……んですがインラインスタイルはフィルタがかけられ、使用できる項目は絞られます。

safecss_filter_attr()

たとえばボタンっぽいCSSのリンクを書いてみます。

これを普通に貼ると以下のようになります:

ボタンっぽい

が、ksesがかかっているとそうはいかず、こうなります。

ボタンっぽい

いくつかのCSSが欠けたのが分かりますね。HTMLをみてみましょう。

6つ指定したうちのdisplay, border-radius,  が今回は取り払われました。このように、許可されたスタイル以外はインラインで指定できないようです。(許可するプロパティはsafe_style_cssフィルターで変更可能)

divfloat, clearなどは使えるのでサイトレイアウトを崩すぐらいは簡単にできてしまう気もしますが、ある程度制限されるようですね。


まとめ

WordPressでは信頼できないユーザーの投稿にデフォルトで然るべきサニタイズが行われるようになっていたため、予期せぬトラブルの盾となって攻撃によって起きかねない悪夢から今回はなんとか守ってくれました。

が、もちろんこれは「デフォルトの場合」であって、もし何らかの理由でサイト管理者がこのサニタイズを無効化していたりし、そこに本文中のPHPを実行するプラグインが入っていたりすると、それはすべての終わりです。

また、今回はたまたまサニタイズにより守られましたが、必ずしも通用するわけではありません。もし次にコンテンツインジェクションが可能な問題が発生した際はサニタイズが行われないレイヤーかもしれませんし、そうなればJSもPHPも実行され放題になりえません。

PHPが任意実行されればそれはサーバーが乗っ取られたようなもの。Exec-PHPのようなプラグインを使っている人はそれを理解しておきましょう。というか使わないのが最善です。その仕組自体が脆弱です。

あと、毎回言ってるけど自動更新はちゃんと動くようにしておきましょうね。コケているようならサーバーの乗り換えどきかもしれません。

ではまた。


  1. ところでksesというのは既存のUlf Harnhammarによるものをベースにしていて、ksesが何の略かというと kses strips evil scripts…… あぁ、再帰している…… 
  2. デフォルトで実際に許可されているタグ一覧はこの辺りを読むと確認できます。 

コメントを残す