Liberada una nueva actualización de WordPress la 2.8.6 que soluciona dos problemas de seguridad.
Entradas en la categoría: XSS
7 nuevos problemas de seguridad en WordPress
Actualización: g30rg3_x me comenta que ya están disponibles los parches oficiales para estos problemas de seguridad, pueden ver los archivos modificados para las versiones 2.2.x (zip) y 2.0.x (zip).
Hace algunas horas acaban de reportar 7 problemas de seguridad en WordPress:
- Wordpress Persistant XSS Vulnerability in the Default Theme (v.2.2): Los parámetros para la imagen y color de la cabecera en el tema por omisión de WordPress (Kubrick) son vulnerables. Aunque no lo probé, me parece que es parecido al que reporté hace tiempo.
- Wordpress /options.php SQL Injection Vulnerability: El parámetro
page_optionsno está correctamente validado enwp-admin/options.php. Sólo los usuarios con nivel Administrador pueden explotar esta vulnerabilidad. - Wordpress /options.php Information Disclosure: Esta vulnerabilidad se deriva de la anterior, porque en
wp-admin/options.phpse asume que el nombre de las opciones son seguras. - Wordpress /edit-comments.php Database Error (Bug): Simplemente muestra un error en la consulta si el valor de la página es negativo.
- Wordpress /link-import.php XSS Vulnerability: El parámetro
cat_idno es filtrado adecuadamente, para explotarlo requiere tener un valor adecuado para el parámetro_wpnonce. - Wordpress /upload.php XSS Vulnerability: En este caso el parámetro
stylees inseguro.
La misma persona que reportó estos bugs se tomó el trabajo de realizar un gusano que se encarga de parchar los blogs vulnerables, siempre y cuando se el servidor web tenga permisos de escritura en los archivos afectados.
En cada uno de los tickets abiertos ya existen parches oficiales disponibles (4689, 4690, 4691 y 4692). Mi parche para corregir esos problemas es ligeramente diferente:
===================================================================
--- wp-admin/edit-comments.php (revision 5825)
+++ wp-admin/edit-comments.php (working copy)
@@ -76,7 +76,7 @@
endif;
if ( isset( $_GET['apage'] ) )
- $page = (int) $_GET['apage'];
+ $page = (int) abs($_GET['apage']);
else
$page = 1;
Index: wp-admin/link-import.php
===================================================================
--- wp-admin/link-import.php (revision 5825)
+++ wp-admin/link-import.php (working copy)
@@ -73,7 +73,7 @@
<h2><?php _e('Importing...') ?></h2>
<?php
- $cat_id = $_POST['cat_id'];
+ $cat_id = (int) $_POST['cat_id'];
if ( $cat_id == '' || $cat_id == 0 )
$cat_id = 1;
Index: wp-admin/options.php
===================================================================
--- wp-admin/options.php (revision 5825)
+++ wp-admin/options.php (working copy)
@@ -143,6 +143,7 @@
$options_to_update[] = $option->option_name;
$class = 'all-options';
}
+ $option->option_name = attribute_escape($option->option_name);
echo "
<tr>
<th scope='row'><label for='$option->option_name'>$option->option_name</label></th>
Index: wp-admin/upload-functions.php
===================================================================
--- wp-admin/upload-functions.php (revision 5825)
+++ wp-admin/upload-functions.php (working copy)
@@ -104,6 +104,8 @@
function wp_upload_form() {
$id = get_the_ID();
global $post_id, $tab, $style;
+ $style = attribute_escape($style);
+ $post_id = (int) $post_id;
$enctype = $id ? '' : ' enctype="multipart/form-data"';
?>
<form<?php echo $enctype; ?> id="upload-file" method="post" action="<?php echo get_option('siteurl') . "/wp-admin/upload.php?style=$style&tab=upload&post_id=$post_id"; ?>">
Index: wp-includes/functions.php
===================================================================
--- wp-includes/functions.php (revision 5825)
+++ wp-includes/functions.php (working copy)
@@ -206,6 +206,7 @@
function get_option($setting) {
global $wpdb;
+ $setting = $wpdb->escape(stripslashes($setting));
// Allow plugins to short-circuit options.
$pre = apply_filters( 'pre_option_' . $setting, false );
if ( $pre )
@@ -305,6 +306,7 @@
function update_option($option_name, $newvalue) {
global $wpdb;
+ $name = preg_replace('/[^a-z\d_-]/i', '', trim($name));
wp_protect_special_option($option_name);
if ( is_string($newvalue) )
@@ -352,6 +354,7 @@
function add_option($name, $value = '', $description = '', $autoload = 'yes') {
global $wpdb;
+ $name = preg_replace('/[^a-z\d_-]/i', '', trim($name));
wp_protect_special_option($name);
// Make sure the option doesn't already exist. We can check the 'notoptions' cache before we ask for a db query
@@ -391,6 +394,7 @@
wp_protect_special_option($name);
+ $name = $wpdb->escape(stripslashes($name));
// Get the ID, if no ID then return
$option = $wpdb->get_row("SELECT option_id, autoload FROM $wpdb->options WHERE option_name = '$name'");
if ( !$option->option_id ) return false;
Los que no sepan -- o no quieran -- aplicar manualmente este parche no oficial, pueden descargar el conjunto de archivos modificados para la versión 2.2.1, úsenlo bajo vuestra propia responsabilidad.
Actualización (03/08/2007): Miquel encontró un bug en el parche que publiqué.
Firefox 2.0.0.5 finalmente implementa las cookies HttpOnly
Un poco antes de lo esperado, esta última actualización de Firefox (2.0.0.5), finalmente incluye el soporte para cookies HttpOnly.
Ejemplos de uso en ASP.NET y PHP:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
Response.Cookies["Demo"].Value = "Prueba";
Response.Cookies["Demo"].Secure = true;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>HttpOnly Firefox 2.0.0.5</title>
<script type="text/javascript">
alert(document.cookie);
</script>
</head>
<body>
</body>
</html>
// PHP 4
setcookie('foo', 'test', null, '/;HttpOnly');
/*
PHP 5
setcookie('foo', 'test', null, null, null, true);
// o
ini_set("session.cookie_httponly", 1);
// o
session_set_cookie_params(0, NULL, NULL, NULL, TRUE);
*/
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>HttpOnly Firefox 2.0.0.5</title>
<script type="text/javascript">
//<![CDATA[
alert(document.cookie)
//]]>
</script>
</head>
</html>
Como mencioné anteriormente, esta característica no es la panacea para evitar el robo de cookies a través de XSS, pero por lo menos reducirá un poco los vectores de ataque.
XSS, SQL Injection, Arbitrary File Upload en Wordpress
A los dos problemas de seguridad que reporté previamente en Wordpress 2.2, se suman otros cuatro que en su mayoría afectan a aquellos blogs que dejan registrar a usuarios (como colaboradores). En vista de que en realidad muchos blogs -- al menos de los que conozco -- tienen esta opción deshabilitada, el radio de acción no es tan grande; sin embargo, estos mismos bugs en páginas que ofrecen blogs gratis basados en Wordpress MU, corren mucho riesgo.
XSS: Wordpress permite subir sólo cierto tipo de archivos, los cuales son validados en base a la extensión del archivo (ver función
wp_check_filetypeenwp-includes/functions.php), como no existe ningún proceso que valide el contenido de esos archivos, es posible que éstos puedan contener código javascript incrustado en éste. Por ejemplo, si se hace la siguiente petición a xmlrpc.php:code:POST /xmlrpc.php HTTP/1.1
Content-Type: text/plain
Host: vulnerable.com
Content-Length: 458
<methodCall>
<methodName>wp.uploadFile</methodName>
<params>
<param><value>1</value></param>
<param><value>user</value></param>
<param><value>password</value></param>
<struct>
<member><name>name</name><value>demo.gif</value></member>
<member><name>type</name><value>image/gif</value></member>
<member><name>bits</name><value><![CDATA[<script>alert(document.cookie)</script>]]></value></member>
</struct>
</params>
</methodCall>Puesto que a Internet Explorer le gusta facilitar los ataques XSS, si alguien accede con este navegador a
http://victima.com/wp-content/uploads/año/mes/demo.gif, se mostrará un mensaje mostrando las cookies de ese usuario.SQL Injection: Este problema se presenta en la función
mw_editPostdexmlrpc.php, debido a que no escapa los parámetros en el orden adecuado, si revisan el código de esa función, van a encontrar algo parecido a:php:postdata = wp_get_single_post($post_ID, ARRAY_A);
// If there is no post data for the give post id, stop
// now and return an error. Other wise a new post will be
// created (which was the old behavior).
if(empty($postdata["ID"])) {
return(new IXR_Error(404, __("Invalid post id.")));
}
$this->escape($postdata);Gracias a este pequeño error, es posible realizar un ataque de inyección de SQL usando el campo
post_password(20 caracteres). La siguiente prueba de concepto actualizará todas las entradas con el títulofoo:- Crear una nueva entrada y asignarle como contraseña:
'/* - Realizar la siguiente petición a xmlrpc.php.
code:POST /wp/xmlrpc.php HTTP/1.1
User-Agent: Fiddler
Host: localhost
Content-Length: 390
<methodCall>
<methodName>blogger.newPost</methodName>
<params>
<param><value>0</value></param>
<param><value>0</value></param>
<param><value>alex</value></param>
<param><value>1234</value></param>
<struct>
<member><name>title</name><value>foo</value></member>
</struct>
<param><value>0</value></param>
</params>
</methodCall>
Don't try it at home.
- Crear una nueva entrada y asignarle como contraseña:
Arbitrary File Upload: Este sin duda es el más grave de todos porque permite a un atacante subir cualquier tipo de archivos, más adelante publicaré los detalles.
XSS: Este bug se basa en lo que comentábamos el otro día sobre XSS y las peculiaridades de los navegadores, están afectadas casi todas las páginas que hacen uso de la función
js_escape. Por ejemplo, pueden reproducir el problema enwp-admin/edit-comments.php:- Hacer un comentario en alguna entrada y poner como nombre
')||alert(document.cookie)// - Intentar eliminar o marcar como spam ese comentario.
- Hacer un comentario en alguna entrada y poner como nombre
Según la respuesta de uno de los desarrolladores de Wordpress, la solución para el primer problema se va a postergar hasta la salida de la versión 2.2.2.
Dado que son varios los cambios que hay que hacer para estar un poco más seguros, les recomiendo que actualicen por lo menos a la versión 2.2.1* ó 2.0.11 para los que todavía sigan en la rama 2.0.
*: durante la edición de esta entrada se reportó otros pequeños problemas de SQL Injection.
Disculpen por el título amarillista, no se me ocurrió otra cosa.