Categories
Seguridad XSS

XSS (Cross Site Scripting) en elpais.com

Hace cierto tiempo puse un quiz titulado "cuando los filtros no hacen lo que deberían", en el que preguntaba como explotar la casi nula validación de una variable. Pues bien, ese ejemplo hacía referencia al bug que existe en el sitio web del diario El País de España.

La URL afectada recibe como parámetro la variable backURL, que -supongo- sirve para redireccionar a esa URL una vez hecha la validación de los datos: http://www.elpais.com/clientes2/conectar1.html

Al parecer por la acción de un filtro, esta página envía un error (404) cuando backURL contiene en cualquier ubicación la palabra <script, pero por lo que se ve, los desarrolladores no se dieron cuenta de que existen otros vectores de ataque.

No tengo idea sobre el impacto de este bug en ese sitio, pero lo que está claro es que muchas empresas no le dan demasiada importancia a esto de la seguridad, ya que han pasado aproximadamente 3 meses desde que reporté este problema y hasta el momento ni lo han solucionado, ni he recibido respuesta alguna.

Categories
PHP Quiz Seguridad

Ejercicio de la Semana: Login de usuarios y actualización de datos

A partir de ahora, cada semana haré el intento de poner los quiz los días lunes o martes, así tendremos más tiempo para comentar estos pequeños ejercicios.

Esta vez, se trata de dos páginas: la primera que se encarga de realizar el login de los usuarios y la segunda se encarga de mostrar y actualizar el perfil del usuario que accede a la página.

php:

<?php
session_start();

/*
 session.php:
  Se encarga de validar los datos del usuario y
  que exista una sesión válida para acceder a user.php
*/

include_once './config.php';
include_once './db.php';

if ( !empty($_REQUEST['action']) ) {
        switch ($_REQUEST['action']) {
                case 'login':
                        if ( !empty($_SESSION['user']) ) {
                                header('Location: http://sitio/user.php');
                                exit();
                        }
                       
                        $username = $db->escape($_POST['user']);
                        $pass = md5($secret_key . $_POST['password']);
                       
                        if ( $user = $db->get_row("SELECT * FROM users WHERE user='$username' AND password='$pass'") ) {
                                session_regenerate_id();
                                $_SESSION['user'] = $user;
                                header('Location: http://sitio/user.php');
                                exit();
                        }
                        die('Usuario no válido');
                case 'logout':
                        // ...
        }
}

?>

php:

<?php
/*
 user.php:
   Se encarga de mostrar y actualizar el perfil de un usuario
*/

include_once './session.php';

if ( empty($_SESSION['user']) ) {
        header('Location: http://sitio/session.php');
        exit();
}

if ( !empty($_REQUEST['action']) ) {
        switch ($_REQUEST['action']) {
                case 'update-profile':
                       
                        // Limpiar los datos
                        $_POST['name'] = htmlspecialchars(strip_tags($_POST['name']), ENT_QUOTES, 'utf-8');
                        $_POST['email'] = preg_replace('/[^a-z0-9.@-]/i', '', $_POST['email']);   

                        // Escapar los valores
                        $user = $db->escape($_SESSION['user']->user);
                        $name = $db->escape($_POST['name']);
                        $email = $db->escape($_POST['email']);
                        $password = md5($secret_key . $_POST['password']);
                       
                        $sql = "UPDATE users SET name = '%s', email = '%s', password = '%s' WHERE user = '%s'";
                       
                        $db->query(sprintf($sql, $name, $email, $password, $user));
                       
                        break;
                case 'recover-password':
                        // ...
        }
}

?>

¿Existe algún bug en el código mostrado?

Actualización:

  1. Cambio de $_POST['user'] por $_SESSION['user']->user en user.php
  2. Cambio de ubicación de la comprobación de la variable user en la sesión

¿Todavía queda algún problema de seguridad?

Categories
Seguridad WordPress

Cuidado con la codificación de los datos

En el anterior quiz, había puesto un bug existente en versiones anteriores de WordPress que permitía hacer SQL Injection.

Como agusti comentó, el problema se presenta al aplicar la función mysql_real_escape_string antes de hacer la conversión de un juego de caracteres X a UTF-8.

mysql_real_escape_string: Escapa todos los caracteres especiales en la cadena_no_escapada, tomando en cuenta el juego de caracteres actual de la conexión, de tal modo que sea seguro usarla con mysql_query()

Pues bien, para hacer posible el ataque es necesario usar un juego de caracteres en los que ' o \ puedan ser expresados como una secuencia de caracteres seguros; uno de éstos es UTF-7, ya que ' puede ser representado como +ACc- y \ como +AFw-.

Para obtener esas secuencias, pueden hacerlo manualmente en base a la documentación sobre UTF-7 o usando esta simple función:

php:

<?php
$table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

function mb64($match) {
    global $table;
   
    return $table[bindec($match[0])];
}
function utf7_char($char) {
    $bin = str_pad(decbin(ord($char)), 16, 0, STR_PAD_LEFT) . '00';
   
    return '+' . preg_replace_callback('/\d{6}/', 'mb64', $bin) . '-';
}
echo utf7_char("'");
?>

Ahora con las herramientas necesarias, lo único que queda es empezar a jugar con la consulta para obtener los resultados deseados, por ejemplo con los siguientes valores obtenemos todas las entradas (notar que sólo se necesita evadir '):

  • title=+ACc- or 1=1--+ACc- (equivale a ' or 1=1--')
  • charset=utf-7

Se deja como tarea para el lector probar otras consultas 🙂

Categories
Seguridad WordPress

Exploit para versiones de WordPress menores a 2.0.5

Si todavía eres uno de los que no ha migrado a la versión 2.0.6 de WordPress, es mejor que lo hagas cuanto antes, puesto que ha sido liberado el exploit que aprovecha las vulnerabilidades existentes en versiones anteriores (<= 2.0.5).

Este exploit obtiene las cookies (usuario y password) necesarios para entrar en un sitio vulnerable -con extensión mbstring habilitada y wordpress <= 2.0.5.

Categories
Quiz Seguridad Web WordPress

Un bug interesante en WordPress

El presente quiz está basado en un reciente bug de WordPress que fue corregido en la versión 2.0.6 de este CMS

php:

<?php

header('Content-type: text/plain; charset=utf-8;');

if ( empty($_POST['title']) ) {
        die('Parámetros no válidos');
}

// Conexión a la base de datos test
mysql_connect('localhost', 'root', '1234');
mysql_select_db('test');

// Escapar los valores
$title = mysql_real_escape_string($_POST['title']);

// Hacer la conversión si se envía el parámetro charset
if ( !empty($_POST['charset']) ) {
        // Verificar si la extensión mb_string está habilitada
        if ( function_exists('mb_convert_string') ) {
                $title = @mb_convert_encoding($title, 'UTF-8', $_POST['charset']);
        }
        // Verificar si la extensión iconv está habilitada
        elseif ( function_exists('iconv') ) {
                $title = @iconv($_POST['charset'], 'UTF-8', $title);
        }       
}

$sql = "SELECT * FROM wp_posts WHERE post_title = '$title'";

if ( $result = mysql_query($sql) ) {
        while ($row = mysql_fetch_assoc($result)) {
                print_r($row);
        }
       
        mysql_free_result($result);
}

/*
        Muestra el error intencionalmente
*/

if ( $error = mysql_error() ) {
        echo "MySQL: $error\nConsulta: $sql";
}

mysql_close();
?>

¿Dónde se encuentra el error? y ¿cómo explotarlo? (Si desean puedo colgar una página para que hagan las pruebas)