lunes, 16 de mayo de 2011

Vulnerabilidad en Nintendo.com (Double Encoding Directory Transversal)



Hoy por la mañana leyendo unos Twitts, me entero que la web de la conocida consola de video juegos Nintendo tiene una vulnerabilidad.

Verificando un poco de que se trataba (ya lo corrigieron) veo fácilmente un Directory Transversal. Este fallo consiste en escalar directorios para acceder a información que no está comprendida dentro del directorio publicado en el servidor web.

Ejemplo:

1) Tenemos un URL que apunta a un archivo PHP, el cual descarga el archivo indicado en la recepción de su variable "archivo". URL ==> www.victima.com/descargar.php?archivo=documento.pdf

2) El archivo descargar.php no contiene siquiera validación o filtro alguno de caracteres para evitar la inyección de saltos de directorio.

3) Teniendo en cuenta que el SO del servidor es Linux procedemos a leer el archivo /etc/passwd inyectando los saltos de directorio de esta manera:

www.victima.com/descargar.php?archivo=../../etc/passwd

Y añadimos tantos ../ como carpetas haya que escalar. Si no sabemos la ruta exacta, comenzamos a probar añadiendo saltos.

www.victima.com/descargar.php?archivo=../../../etc/passwd
www.victima.com/descargar.php?archivo=../../../../etc/passwd
www.victima.com/descargar.php?archivo=../../../../../etc/passwd
...
...
...

Hasta que finalmente damos con el archivo /etc/passwd y voilá.

Pero este no es el escenario de la web de Nintento, ya que esta si tiene ciertas validaciones de caracteres como "/" o "." por lo que procedo a explicar un ataque más avanzado del Directory Transversal común.

El nombre de este ataque es: Directory Transversal Attack with Double Encoding y consiste en lo siguiente:

Codificar los saltos de directorio en hexadecimal dos veces para hacer el bypass de los controles de seguridad y/o causar un comportamiento inesperado en la aplicación. Con esto se vulneran los controles de seguridad que sólo decodifican una sola vez la entrada de usuario. El segundo proceso de decodificación lo realiza la plataforma del backend o los módulos que controlan correctamente la data pero no tienen controles de seguridad correspondientes en su lugar.

En su codificación simple, los caracteres ../ codificados a hexadecimal arrojan este resultado %2e%2e%2f siendo:

Caracter        Hexadecimal
--------------------------------------------
.                    %2e
/                    %2f

Pero si queremos codificar nuevamente (Double Encoding), codificamos en hexadecimal también el símbolo "%" (porcentaje), por lo que quedaría así:

Símbolo porcentaje en codificación simple: %
Símbolo porcentaje en codificación doble: %25

Y reemplazando en %2E%2E%2f el valor del símbolo porcentaje obtenido luego de codificarlo, quedarían así los saltos:

Codificado Doble      Decodificado
-------------------------------------------------------
%252e%252e/                 ../

Aplicándose así en la URL vulnerable:

www.victima.com/descargar.php?archivo=%252e%252e/%252e%252e/%252e%252e/%252e%252e/etc/passwd

En el caso de Nintendo, la URL vulnerable era directa y no por un manejador de descargas:

http://www.nintendo.com/css/pages//%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e/%252e%252e/etc/passwd

viernes, 12 de marzo de 2010

Enlace y Autenticación PHP con LDAP (LDAPhp.class.php)


Es un clásico escuchar los intentos de realizar este enlace (bind) y que no funcione porque no sabemos como configurar la cadena de conexión del usuario enlace o el usuario a autenticar. A esta cadena de conexión la llamamos Distinguished Name/Nombre distinguido y un ejemplo de DN es:

DN: cn=Andres Perez Albela, dc=empresa, dc=com

Siendo su RDN (Relative Distinguished Name/Nombre Distinguido Relativo) la siguiente:

RDN: cn=Andres Perez Albela

Esto quiere decir que el usuario es Andres Perez Albela y que el dominio es empresa.com, y me parece bueno resaltar que el Directorio Activo (AD) separa mediante la variable DC (Domain Controller//Controlador de Dominio) cada parte del dominio explotada por el símbolo "." Es decir, si hacemos explode('.', "empresa.com.pe") obtendremos tres partes en el arreglo, y cada parte del arreglo es un DC, siendo en este caso algo así:

DC=empresa, DC=com, DC=pe

Ahora podrán ver un ejemplo de Active Directory que hice para poder explicarles el enlace.


Imagen de Estructura de Active Directory


Como podemos ver, el dominio es: labserver.com y las unidades organizacionales (OU) son:

-Casa
--Conexiones
--Cuarto 1
--Cuarto 2

Siendo Casa el OU Principal y Conexiones, Cuarto 1, Cuarto 2 las sub-unidades organizacionales de Casa (Ojo que no dejan de ser OU).


Podemos apreciar el "samAccountName" de un usuario ingresando a esa opción


Dentro de Cuarto 1 tenemos un usuario con los siguientes datos:

CN : Andres Perez Albela H.
samAccountName : aperezalbela

La historia es que el desarrollador de un sistema creado en PHP quiere autenticar a sus usuarios con LDAP. Entonces crea una clase para realizar el enlace, pero este enlace no funcionará si no se crea un usuario enlace en el Directorio Activo, que en este caso es PHP y se encuentra en CASA/Conexiones, entonces hay que crear una cadena de conexión para ese usuario:

CN=PHP,OU=Conexiones,OU=Casa,DC=labserver,DC=com

Luego de haber autenticado al usuario PHP, se procede a buscar al usuario aperezalbela en el AD, entonces tenemos que crear una cadena para ubicarnos en la OU principal y poder hacer búsquedas con ldap_search que busca en la OU principal y bajo ella (sub-OU). La cadena para este caso es:

OU=Casa, DC=labserver, DC=com

De esta manera al desarrollador solo le quedará crear un filtro de búsqueda para comprobar la existencia del usuario aperezalbela. El filtro es:

(&(objectClass=user)(samAccountName=$usuario))

Como podemos ver, tenemos la variable $usuario que es el usuario a buscar y que recibiéndola por un formulario y habiéndose interpretado por php pues quedaría así:

(&(objectClass=user)(samAccountName=aperezalbela))

Para finalmente intentar realizar el enlace (autenticación de ese usuario en LDAP) con la contraseña enviada por el usuario en el formulario web.

Ahora presento el código PHP para la autenticación (LDAPhp.php).

<?php

//Importamos el manejador LDAP
require_once 'LDAPhp.class.php';

//Datos del Usuario a Autentificar con tu aplicacion en PHP
//La mayoría de veces estos datos no son una constante, sino valores recibidos por $_POST[]
$usuario = "aperezalbela";
$clave = "prueba";

//Datos de Configuración del Servidor
$servidor="192.168.1.2";
$puerto="389";

//Datos de Configuración para la conexión al LDAP (DN == "Distinguished Name")
$cadenaConexionUsuario = "CN=PHP,OU=Conexiones,OU=Casa,DC=labserver,DC=com";

//Datos de Configuración de usuario enlace LDAP-PHP, en este caso PHP (CN=PHP)
$password = "pruebaLdap";

//Datos de Configuración para el enlace al LDAP (DN == "Distinguished Name")
//En este caso nos situamos en el OU (Unidad Organizacional) principal
$dn = "OU=Casa, DC=labserver, DC=com";

//Datos de Configuración de Filtro para la búsqueda de un Usuario
$filtro = "(&(objectClass=user)(samAccountName=$usuario))";

//Conectamos al servidor LDAP con los => "Datos de Configuración del Servidor"
$PHPLdap = new PHPLdap($servidor, $puerto);

//Luego de habernos conectado al servidor, enlazamos la conexion identificandonos con un password
//para el usuario PHP (CN=PHP)
$PHPLdap->enlazarPHPLdap($cadenaConexionUsuario, $password);

//Luedo de haber realizado el enlace principal con el usuario PHP (CN=PHP) creado para este fin
//Procedemos a realizar la búsqueda del usuario a autenticar con la aplicación en PHP.
//PD: No se envía el usuario como parámetro ya que se encuentra interpretado en la variable: $filtro
$autenticar = $PHPLdap->autenticarUsuario($dn, $filtro, $clave);

//Verificamos el valor de retorno de la autenticación
if($autenticar===true){
//Autenticado
//Aca es donde generas la $_SESSION, COOKIES, o como manejes la autenticación
//Luego redirecciónas, a la página privada, etc.
echo "- A buena hora! Lograste autentificar al usuario $usuario";
}
else{
//Acceso Denegado
echo "- Contraseña Incorrecta para el usuario $usuario";
}

//Cerramos la conexion a LDAP
$PHPLdap->cerrarConexion();
?>

Y también el código de la clase (LDAPhp.class.php)

/*** Título: Enlace PHP con LDAP para autenticar usuarios en aplicaciónes web
**** Autor: Andrés Pérez-Albela H.** Web: http://www.seguridadinformatica.org/
**** Blog: http://develsec.blogspot.com
**** Versión: 1.0
**** Lenguaje: PHP
**** Licencia: Copyright © SeguridadInformatica.Org
**** Creative Commons Atribución-Sin Obras Derivadas 3.0 Unported License
****/

class PHPLdap{
private $ds;
private $bind;

public function PHPLdap($host, $puerto){

if(!isset($this->ds)){
if($this->ds = (ldap_connect($host, $puerto))){
echo "- LDAP conectado a: ".$host."";
}
else
{
die("- No me puedo conectar con servidor LDAP");
}
}
}

public function enlazarPHPLdap($cadenaConexionUsuario, $password){
$this->bind = @ldap_bind($this->ds, $cadenaConexionUsuario, $password);

if($this->bind){
echo "- Se realizó el enlace principal satisfactoriamente con el usuario PHP";
}
else
{
die("- El enlace principal no se pudo llevar a cabo");
}
}

public function autenticarUsuario($dn, $filtro, $clave){
$busqueda=ldap_search($this->ds, $dn, $filtro);
$resultados = ldap_get_entries($this->ds, $busqueda);

//Habiendo buscado el usuario a autenticar con "ldap_search" y obteniendo resultados en
//un arreglo $resultados, con "ldap_get_entries", pasamos a condicionar la existencia
//de resultados positivos.

if($resultados["count"]>0){
//Ingreso a la condicional, verificando con el "count" que el usuario exista
//Quiere decir que si encontró al usuario
//Luego obtenemos el DN (Nombre Distinguido) del usuario

$dnUsuario = trim($resultados[0]["distinguishedname"][0]);

//Para poder finalmente realizar el enlace final, siendo "enlace final" el login correcto.
//Si y solo si... la clave es correcta

if(@ldap_bind($this->ds, $dnUsuario, $clave)){
//Se realiza el bind, siendo la clave correcta, y se retorna un valor VERDADERO (true)
return true;
}
else
{
return false;
}
}
}

public function cerrarConexion(){
//Cerramos la conexion a LDAP
ldap_close($this->ds);
}
}
?>

Bueno, y eso es todo. Espero que les sirva la información, el post es completamente propio y cualquier consulta no duden en comentarla acá en el post.

Saludos, hasta la próxima entrada.

jueves, 11 de marzo de 2010

Bienvenidos a DevelSec

Bueno, creo que ya era hora de crear mi propio blog para poder expresar mis ideas y exponer las cosas que sé y aprendo.

Bienvenidos a todos los interesados en estos dos rubros en los que enfocaré el contenido del blog: Seguridad Informática y Programación.

Espero poder ayudar a las personas que me escriban/comenten y agradeceré sugerencias/recomendaciones para que este blog siga su trayecto y continúe con vida.

Así que con este post comienza el Blog DevelSec.

Saludos entonces para mis lectores!