cookie経由のXSSの話 脆弱性"&'<<>\ Advent Calendar 2016 (4日目)
この記事は脆弱性"&'<<>\ Advent Calendar 2016の4日目の記事です。
前日の記事はHack Patch!: CyVDB-1118関連です。
今回の記事は、eval(document.cookie)
になるようなことは避けましょうという内容です。
cookie 経由でのXSSについて。
cookieに値を入れるにはサーバサイドからのレスポンスヘッダーかjavascriptからの操作になります。XSSなどがない限り攻撃者が値を入れることはできなそうですが、以下のような条件により可能です。
特にHTTP通信から攻撃する方法は徳丸先生の記事によるものが詳しく(HTTPSを使ってもCookieの改変は防げないことを実験で試してみた | 徳丸浩の日記)、これを防ぐためにはHSTSをサイト全体に設定する他ない状況となっています。
eval(document.cookie)
と等価になるような実装がされていることはあまり多くないのですが、たまーに見かけるのが現状です。
別サブドメインからのXSS事例
レアケースかと思われますが、数年前に別サブドメインからcookieを経由してXSSが可能だったケースを見つけたことがありました。
某植物系レンタルサーバさんのあるサーバ管理画面にてこんなコードが有りました。(変数名などは念のため変えています。)
var param = $.cookie('cookie_name'); ... ... $('.class_'+param).attr('checked', true);
一見すると問題があるようには見えませんが、XSSが可能です。というのもjQueryの古いバージョンでは$(.class_<img onerror=alert(1) src=.>)
で、DOM Based XSSが発火するためです。(jQuery Migrate Plugin なんてなかった件 - ほむらちゃほむほむ)
ここ数年はjQuery 1.10以上が使われることが多いためjQuery由来の問題は減ってきていますが、画面の作成時期によっては古いjQueryがそのまま使われています。
さて、このままでもMITMによって攻撃可能ですが他の攻撃方法がないかを探してみました。その結果、サーバ管理画面のドメインxxx.jp
の別サブドメイン、blog.xxx.jp
にてDOM Based XSSを見つけました。こちらもjQuery由来の問題でprettyPhoto
というjQuery pluginが脆弱性がある状態で使用されており、細工したURLにアクセスするとXSSが可能な状態でした。
組み合わせると、以下の手順でサーバの管理画面に攻撃することができました。
blog.xxx.jp
にXSSを含むURLをターゲットにアクセスさせる- javascriptから
cookie_name=<img onerror=alert(location.href) src=.>
のようなクッキーを発行する xxx.jp
にリダイレクトさせるxxx.jp
で$(.class_<img onerror=alert(1) src=.>)
が評価されてXSSが発火
eval(document.cookie)
と等価な部分があると別ドメインのXSSにひきずられることがあり、普通のXSSよりも気づにくい状態となります。cookieのセキュリティーポリシーがscheme+domain+portではないことに由来する問題ですね。
幸いサーバ管理画面の方は報告後すぐに修正されていました。
攻撃を受けにくくするために
この攻撃を発見して以降、念のため重要度が高いドメインではそのときだけシークレットウィンドウを開いて操作することを推奨しています。cookieやlocalStorageによる攻撃、CSRF、ブラウザ拡張機能による影響を受けにくくなります。
evernote.comにあったDOM Based XSS
先日evernote.comのコードを見ていたら変わった箇所を見つけまして
url中にstage
が含まれているときだけ実行される処理です。試しに実際に実行してみるとステージング環境であることを示す警告文が表示されます。
コード中で変数j
にdocument.URL
の値が代入されていますが、この値がエスケープされずにhtmlが組み立てられています。document.URL
(やlocation.href
)の値がパーセントエンコードされているかはブラウザによって違いまして、chromeからアクセスしてやるとこうなります。
XSSですね。
evernoteは脆弱性情報窓口が設けられいる( セキュリティ問題の報告 | Evernote)のでそちらから報告しました。次の日には返答があって、数日後には修正がリリースされていました。
WordPress pluginのCSRF脆弱性(CVE-2015-0895) "&'<<>\ Advent Calendar 2015 (14日目)
この記事は脆弱性"&'<<>\ Advent Calendar 2015の14日目の記事です。
前日の記事はWordPress のsecurity系plugin の Blind SQL injection 脆弱性(CVE-2015-0894) "&'<<>\ Advent Calendar 2015 (13日目) - ooooooo_qの日記です。今日は連続してCVE-2015-0895です。
脆弱性内容
違うCVEが割り振られていますが、報告したのはCVE-2015-0894と同じpluginで、WordPress › All In One WP Security & Firewall « WordPress Pluginsです。
昨日書いた記事の中で、クエリパラメータを受け取ってDELETE文が発行されている箇所がありますが、その際リクエスト元のチェックなどはされていません。
GETでアクセス時にDELETEが発行されるようなURLが存在していると検索エンジンのbotに拾われて、勝手に削除されてしまうなんて話もありますが、この場合はログインが必要なのでそうはなりません。しかし、ログインユーザが以下の様なURLにアクセスしてしまうとテーブルの中身が全部消えてしまいます。
/wp-admin/admin.php?page=aiowpsec_firewall&tab=tab6&action=delete&item[0]=) or 1=1
対象はplugin内で使っているテーブルのみなので、それほど影響は大きくないのですがあまり良くはないですね。
WordPress のsecurity系plugin の Blind SQL injection 脆弱性(CVE-2015-0894) "&'<<>\ Advent Calendar 2015 (13日目)
この記事は脆弱性"&'<<>\ Advent Calendar 2015の13日目の記事です。
前日の記事は mage ctf writeup: CVE-2015-0893です。今日は連続してCVE-2015-0894です。
脆弱性内容
JVN#30832515: WordPress 用プラグイン All In One WP Security & Firewall における SQL インジェクションの脆弱性
WordPressのpluginをいろいろ見て回っていた時期に見つけたやつで、IPAに昨年末に届け出をし、今年の3月に修正版が出ました。Wordpressのpluginはsecurity系pluginにも脆弱性が、という話をTLで見たことがありまして実際に探したら見つけてしまいました。
実際のコードです。
$events_table = AIOWPSEC_TBL_EVENTS;
if (is_array($entries)) {
//Delete multiple records
$id_list = "(" . implode(",", $entries) . ")"; //Create comma separate list for DB operation
$delete_command = "DELETE FROM " . $events_table . " WHERE id IN " . $id_list;
$result = $wpdb->query($delete_command);
クエリ文字列を動的に組み立ているあたりで嫌な感じはするのですが、$entriesの中身がどこでエスケープされているのかはパッと見ではよくわかりません。仕方なく$entries
を追っていくとリクエストのクエリパラメータの値がそのまま入っていましたので、SQLiできました。
そしてもう一種。
$orderby = !empty($orderby) ? mysql_real_escape_string($orderby) : 'login_date'; $order = !empty($order) ? mysql_real_escape_string($order) : 'DESC'; $data = $wpdb->get_results("SELECT * FROM $login_activity_table ORDER BY $orderby $order LIMIT 5", ARRAY_A); //Get the last 50 records
orderbyとorderにつかう文字列をクエリパラメータから受け取っています。一見mysql_real_escape_string
でエスケープされているように見えますが、この関数はPHP5.5.0から非推奨な上に数種の特殊文字をエスケープしてくれるだけで、空白や()
などはエスケープされません。
どちらの箇所もログインして管理者画面から操作しなければ実行されません。しかし、リクエストのクエリパラメータを受け取るため、ログイン中のユーザをうまく誘導してやれば任意のSQLを実行させることができます。 実行はできても、攻撃者は直接レスポンスを受け取れませんが、Blind SQL injection、つまりsleep関数などをうまく使ってやれば時間はかかりますがDB中のデータを抜き出すことが可能でした。
/wp-admin/admin.php?page=aiowpsec&tab=tab1&orderby=IF ( (select sleep(4) FROM wp_users where user_pass like concat( char(37),char(84),char(37))), id, id)
Time-based SQL Injectionは意外に実用的だった | 徳丸浩の日記
危険性について
Blind SQL Injectionであるのでなかなか時間がかかりますが、攻撃の際に被害者に対して気づかれずにDB上の情報を盗めます。設定を変えていない場合はsaltが同じようなので、パスワードのハッシュなどを抽出した後にパスワードをオフラインアタックで推測すること理論的には可能であるように見えます。
とはいえ、特定のpluginを入れているサイトの管理者に対してピンポイントに攻撃を仕掛けることになりますので、ソーシャルハックなどを組み合わせないとなかなか難しいです。ログインパスワードを総当り攻撃する方が楽なような気もしますが、アカウントロック機構やアクセス制限をすり抜けるなどの利点はあります。
余談
修正の発表後に、同じpluginに対してまた別の報告でSQL injectionが上がっていました。
ORDER BY句の列名をエスケープするという間違った対策に起因するSQLインジェクションの例。なんでもエスケープすればよいというものではない / “WordPress All In One WP Security & Firew…” http://t.co/lUU1fqSpWk
— 徳丸 浩 (@ockeghem) April 23, 2015
しかし、さっきの脆弱性は「All In One WP Security & Firewall」というセキュリティソフトウェアの脆弱性で、脆弱性があること自体は仕方ないと思うけど、セキュリティソフトウェアを作っているのに「開発者が分かってない」ことに呆然としてしまいますね
— 徳丸 浩 (@ockeghem) April 23, 2015
あれっと思って見てみると、mysql_real_escape_string
の部分がesc_sql
に変わっていたようですが、結局十分にエスケープできていなかったようです。
plugin周りの脆弱性への対応はやはりあまり良くないのですが、最近ではWordpress 周りの脆弱性を集約するサイト(https://wpvulndb.com/)があったり、pluginのコードを静的解析して脆弱性探すサイト php-grinder もあって、以前よりもちょっと環境が整ってきているようです。(けど使わなくていいなら使いたくはない。)