
Analisi tecnica dettagliata e proof-of-concept exploit per le vulnerabilità RCE di WordPress CVE-2019-8942 e CVE-2019-8943, che dimostrano LFI-to-RCE tramite path traversal e manipolazione del caricamento delle immagini.
CVE-2019-8942 è una vulnerabilità che sfrutta il difetto LFI combinato con la funzionalità di caricamento file (File Upload) per eseguire RCE sul server web WordPress con privilegi di author. Le versioni di WordPress interessate includono le precedenti alla 4.9.9 e le 5.x precedenti alla 5.0.1; ciò consente l'esecuzione remota di codice perché il valore wp_attached_file del Post Meta può essere modificato in una stringa arbitraria, ad esempio: .jpg?file.php. Un attaccante con privilegi di author può eseguire codice arbitrario caricando file immagine contenenti codice PHP malevolo nei metadati Exif. L'exploit può sfruttare CVE-2019-8943.
In CVE-2019-8943, WordPress fino alla versione 5.0.3 è affetto da una vulnerabilità di Path traversal nel metodo wp_crop_image(). Un attaccante con il permesso di usare la funzione di ritaglio immagini (author) può scrivere un file immagine in qualsiasi directory basandosi su un nome file con due estensioni, come .jpg?/../../file.jpg.
author.La causa principale che consente a un utente di eseguire RCE risiede nell'errore per cui il Post meta può essere sovrascritto.
I metadati possono essere intesi come dati che descrivono altri dati; in questo caso specifico, i metadati sono le informazioni relative al blog come: titolo, data di pubblicazione, nome dell'autore, ecc.
Nel codice sorgente di WordPress versione 4.9.8, quando un'immagine viene aggiornata, viene chiamata la funzione edit_post(). È importante notare che questa funzione opera direttamente sull'array $_POST. wp_update_post prende direttamente $post_data come parametro senza verificare i campi dati che è consentito modificare.
function edit_post( $post_data = null ) {
global $wpdb;
if ( empty($post_data) )
$post_data = &$_POST;
...
if ( isset($post_data['meta']) && $post_data['meta'] ) {
foreach ( $post_data['meta'] as $key => $value ) {
if ( !$meta = get_post_meta_by_id( $key ) )
continue;
if ( $meta->post_id != $post_ID )
continue;
if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'edit_post_meta', $post_ID, $meta->meta_key ) )
continue;
if ( is_protected_meta( $value['key'], 'post' ) || ! current_user_can( 'edit_post_meta', $post_ID, $value['key'] ) )
continue;
update_meta( $key, $value['key'], $value['value'] );
}
}
...
update_post_meta( $post_ID, '_edit_last', get_current_user_id() );
$success = wp_update_post( $post_data );
if ( ! $success && is_callable( array( $wpdb, 'strip_invalid_text_for_column' ) ) ) {
$fields = array( 'post_title', 'post_content', 'post_excerpt' );
foreach ( $fields as $field ) {
if ( isset( $post_data[ $field ] ) ) {
$post_data[ $field ] = $wpdb->strip_invalid_text_for_column( $wpdb->posts, $field, $post_data[ $field ] );
}
}
wp_update_post( $post_data );
}
wp-admin/includes/post.php
Un utente con privilegi di pubblicazione può sovrascrivere i valori del Post Meta. Più precisamente, l'attaccante può modificare il valore del metadato _wp_attached_file. Questo non cambia il nome del file; cambia semplicemente il file su cui WordPress opera quando viene effettuata la modifica. Ciò porta allo sfruttamento del Path Traversal.

wp_postmetaprima di effettuare lo sfruttamento
Si può notare che il file caricato ha il formato YYYY/MM/name.jpg; WordPress salva il file nella directory YYYY/MM/, il che fa pensare allo sfruttamento del Path Traversal.
Nella funzione wp_crop_image(), quando un utente author effettua il ritaglio di un'immagine, WordPress verifica che l'immagine esista in due modi. Il primo consiste nel cercare l'immagine basandosi su _wp_attached_file nella directory wp-content/uploads.
function wp_crop_image( $src, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs = false, $dst_file = false ) {
$src_file = $src;
if ( is_numeric( $src ) ) { // Handle int as attachment ID
$src_file = get_attached_file( $src );
if ( ! file_exists( $src_file ) ) {
// If the file doesn't exist, attempt a URL fopen on the src link.
// This can occur with certain file replication plugins.
$src = _load_image_to_edit_path( $src, 'full' );
} else {
$src = $src_file;
}
}
$editor = wp_get_image_editor( $src );
...
function get_attached_file( $attachment_id, $unfiltered = false ) {
$file = get_post_meta( $attachment_id, '_wp_attached_file', true );
wp-admin/includes/image.php
Se il metodo precedente fallisce, WordPress tenta di scaricare l'immagine dal proprio server generando un URL contenente il percorso verso la directory wp-content/uploads e il nome file presente in _wp_attached_file. Viene tentato il download dell'immagine invece di prenderla direttamente dal locale perché in alcuni casi alcuni plugin generano l'immagine quando quell'URL viene richiesto.
Quando WordPress ha scaricato correttamente l'immagine tramite il metodo wp_get_image_editor(), viene eseguito il ritaglio. L'immagine ritagliata viene poi salvata nel file system. Il nome file è il valore della variabile $src restituita da get_post_meta(), controllata dall'attaccante. WordPress crea una directory con il metodo wp_mkdir_p() (riga 9) e vi salva l'immagine con save(). Si può notare che il metodo save() non controlla affatto lo sfruttamento del Path Traversal.
...
$src = $editor->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs );
if ( is_wp_error( $src ) )
return $src;
if ( ! $dst_file )
$dst_file = str_replace( basename( $src_file ), 'cropped-' . basename( $src_file ), $src_file );
wp_mkdir_p( dirname( $dst_file ) );
$dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), basename( $dst_file ) );
$result = $editor->save( $dst_file );
L'uso del payload inserito in _wp_attached_file contenente i caratteri #/../../ non produrrebbe un percorso esistente utilizzando il primo metodo di caricamento dell'immagine, quindi WordPress usa il secondo metodo per caricarla, ovvero genera un URL per scaricare l'immagine. Qui il percorso assume la forma http://localhost/wp-admin/wp-content/uploads/YYYY/MM/name.jpg#/name.jpg; trattandosi di un URL, i caratteri dopo # (che indicano il frammento) vengono ignorati. Il nome file trovato sarà name.jpg#/name.jpg, in cui name.jpg# è una directory e un nome valido. Successivamente, usando un payload come /name.jpg#/../../name.jpg, si sfrutta il Path Traversal.

Si può notare che i campi valore come
_wp_attach_filee_wp_page_templatesono stati modificati
Da Path Traversal a RCE
Ogni sito WordPress utilizza un tema e dispone di una cartella /wp-content/themes/theme_name/ contenente i file dei template. In alcuni casi è possibile scegliere il tema per un articolo. L'utente deve solo impostare _wp_page_template nella tabella Post Meta al nome file desiderato. Tuttavia, il limite è che funziona solo con i file presenti nella directory del tema. Normalmente non è possibile accedere a questa directory né caricarvi file. Possiamo però sfruttare il Path Traversal per scrivere un file in questa directory. Un attaccante con ruolo author creerà un articolo e sovrascriverà il file immagine nel valore del parametro _wp_page_template. Il file immagine conterrà codice PHP malevolo; quando l'immagine viene caricata, il codice PHP viene eseguito -> RCE.