sábado, octubre 06, 2007

LDAP Injection & Blind LDAP Injection (Parte II de III)

********************************************************
Índice:
LDAP Injection & Blind LDAP Injection (Parte I de III)
LDAP Injection & Blind LDAP Injection (Parte II de III)
LDAP Injection & Blind LDAP Injection (Parte III de III)
********************************************************

Blind LDAP Injection

Una de las evoluciones de las inyecciones de comandos son las acciones a ciegas; los ataques “Blind”. Es común encontrarse ya documentados los ataques Blind SQL Injection, pero de los ataques Blind LDAP Injection sin embargo no se ha hablado nada aún. ¿Es posible realizar un ataque a ciegas para extraer información de un árbol LDAP? La respuesta, obviamente, es Sí.

El entorno para realizar un ataque a ciegas suele ser una aplicación web que cumple dos características:

- La aplicación no muestra los resultados obtenidos de la consulta realiza al árbol LDAP.

- El uso de los resultados produce cambios en la respuesta http que el cliente recibe.

Para realizar un ataque a ciegas necesitamos poder crear la lógica para extraer información cambiando los resultados obtenidos con el filtro LDAP y observando los cambios en las respuestas http.

Blind LDAP Injection en un entorno “AND”

En esta situación tenemos una consulta generada en el lado del servidor del siguiente tipo:

(&(atributo1=valor1)(atributo2=valor2))


Para realizar la inyección deberemos tener en cuenta que en un entrono AND tenemos un parámetro fijo que nos va a mediatizar. El entorno ideal es que el atributo1 sea lo más general posible, por ejemplo objectClass, o cn, con lo que podremos seleccionar casi cualquier objeto del árbol. Después deberemos aplicarle un valor que sea True, es decir, que siempre seleccione objetos para tenerlo fijado a valor true y utilizar como segundo atributo uno conocido que también nos garantice que sea True.

Supongamos la siguiente consulta LDAP:

(&(objectClass=Impresoras)(impresoraTipo=Epson*))

que se lanza para saber si en el almacén de una empresa hay impresoras Epson de tal manera que el programador, si recibe algún objeto, simplemente pinta en la página web un icono de una impresora Epson.

Está claro que lo más que conseguiremos tras la inyección es ver o no el icono de la impresora. Bien, pues a ello. Fijemos la respuesta positiva tal y como hemos dicho, realizando una inyección del siguiente tipo:

(&(objectClass=*)(objectClass=*))(&(objectClass=void)(impresoraTipo=Epson*))

Lo que está puesto en negrita sería la inyección que introduciría el atacante. Esta inyección debe devolver siempre algún objeto luego, en el ejemplo planteado, en la respuesta http veríamos un icono de la impresora. A partir de ahí podríamos extraer los tipos de objectClass que tenemos en nuestro árbol LDAP.

Reconocimiento de clases de objetos de un árbol LDAP

Sabiendo que el filtro (objectClass=*) siempre devuelve algún objeto deberíamos centrarnos en el segundo filtro realizando un recorrido de la siguiente forma:

(&(objectClass=*)(objectClass=users))(&(objectClass=foo)(impresoraTipo=Epson*))
(&(objectClass=*)(objectClass=personas))(&(objectClass=foo)(impresoraTipo=Epson*))
(&(objectClass=*)(objectClass=usuarios))(&(objectClass=foo)(impresoraTipo=Epson*))
(&(objectClass=*)(objectClass=logins))(&(objectClass=foo)(impresoraTipo=Epson*))
(&(objectClass=*)(objectClass=…))(&(objectClass=foo)(impresoraTipo=Epson*))


De tal forma que todas aquellas inyecciones que devolvieran el icono de la impresora en la página web serían respuestas afirmativas que nos indicarían la existencia de ese tipo de objetos.

Otra forma más eficiente sería realizando un barrido en el espectro del alfabeto de posibles valores utilizando una búsqueda con comodines, para ello realizaríamos un árbol que comenzaría por todas las letras del abecedario, de la siguiente forma:

(&(objectClass=*)(objectClass=a*))(&(objectClass=foo)(impresoraTipo=Epson*))
(&(objectClass=*)(objectClass=b*))(&(objectClass=foo)(impresoraTipo=Epson*))

(&(objectClass=*)(objectClass=z*))(&(objectClass=foo)(impresoraTipo=Epson*))


De tal manera que seguiríamos desplegando el árbol de aquellas que hubieran dado una respuesta positiva.

(&(objectClass=*)(objectClass=aa*))(&(objectClass=foo)(impresoraTipo=Epson*))
(&(objectClass=*)(objectClass=ab*))(&(objectClass=foo)(impresoraTipo=Epson*))

(&(objectClass=*)(objectClass=az*))(&(objectClass=foo)(impresoraTipo=Epson*))
(&(objectClass=*)(objectClass=ba*))(&(objectClass=foo)(impresoraTipo=Epson*))
(&(objectClass=*)(objectClass=bb*))(&(objectClass=foo)(impresoraTipo=Epson*))


Y así sucesivamente con lo que, desplegando siempre las positivas podríamos llegar a saber el nombre de todos los objetcClass de un sistema.

Imagen: Árbol de despliegue.Se profundizará en todas las respuestas positivas (color verde)

Este mecanismo no solo funciona para objectClass sino que sería válido para cualquier atributo que un objeto compartiera con el atributo fijo. Una vez sacados los objectClass, podríamos proceder a realizar el mismo recorrido para extraer la información de ellos, por ejemplo, si queremos extraer los nombres de todos los usuarios de un sistema, bastaría con realizar el barrido con la siguiente inyección:

(&(objectClass=user)(uid=a*))(&(objectClass=foo)(impresoraTipo=Epson*))
(&(objectClass=user)(uid=b*))(&(objectClass=foo)(impresoraTipo=Epson*))

(&(objectClass=user)(uid=z*))(&(objectClass=foo)(impresoraTipo=Epson*))


Este método obliga a hacer un recorrido en amplitud por un árbol cuya posibilidad de expandirse en cada nodo siempre es igual al alfabeto completo. Se puede reducir este alfabeto realizando un recorrido para averiguar que caracteres no están siendo utilizados en ninguna posición en ningún objeto utilizando un recorrido de la siguiente forma:

(&(objectClass=user)(uid=*a*))(&(objectClass=foo)(impresoraTipo=Epson*))
(&(objectClass=user)(uid=*b*))(&(objectClass=foo)(impresoraTipo=Epson*))

(&(objectClass=user)(uid=*z*))(&(objectClass=foo)(impresoraTipo=Epson*))


De esta forma se puede excluir del alfabeto a aquellos caracteres que no hayan devuelto un resultado positivo tras este primer recorrido. Si desearamos buscar un único valor, podríamos realizar una búsqueda binaria de cada uno de los caracteres utilizando los operadores realionales <= o >= para reducir el número de peticiones.

Blind LDAP Injection en un entorno “OR”

En esta situación tenemos una consulta generada en el lado del servidor del siguiente tipo:

(|(atributo1=valor1)(atributo2=valor2))

En este entorno la lógica que debemos utilizar es al contrario. En lugar de fijar el valor del primer filtro a un valor siempre cierto deberemos fijarlo a un valor siempre falso y a partir de ahí, extraer la información, es decir, realizaríamos una inyección de la siguiente forma:

(|(objectClass=void)(objectClass=void))(&(objectClass=void)(impresoraTipo=Epson*))

Con esta inyección obtendremos el valor negativo de referencia. En el ejemplo planteado de las impresoras disponibles deberemos obtener un resultado sin el icono de la impresora. Luego podremos inferir la información cuando sea verdadero el segundo filtro.

(|(objectClass=void)(objectClass=users))(&(objectClass=void)(impresoraTipo=Epson*))
(|(objectClass=void)(objectClass=personas))(&(objectClass=void)(impresoraTipo=Epson*))
(|(objectClass=void)(objectClass=usuarios))(&(objectClass=void)(impresoraTipo=Epson*))
(|(objectClass=void)(objectClass=logins))(&(objectClass=void)(impresoraTipo=Epson*))
(|(objectClass=void)(objectClass=…))(&(objectClass=void)(impresoraTipo=Epson*))


De igual forma que en el ejemplo con el operador AND podríamos extraer toda la información del árbol LDAP utilizando los comodines.

Conclusiones

LDAP es una tecnología en expansión cuya implantación en los sistemas de directorio y meta directorio la hacen cada día más popular. Los entornos Intranet realizan uso intensivo de esta tecnología para ofrecer sistemas de Single Sing-On e incluso es común encontrar entornos LDAP en los servicios de Internet.

Esta popularidad hace que sea necesario incluir las pruebas anteriores dentro de los procesos de auditoría de seguridad de las aplicaciones web. Las pruebas de inyecciones de auditoría que se estaban realizando hoy en día, siguiendo la documentación existente, son de poca ayuda pues en los entornos más comunes como ADAM u OpenLDAP no funcionan.

La solución para proteger las aplicaciones frente a este tipo de inyecciones es, como en otros entornos de explotación, filtrar los parámetros enviados desde el cliente. En este caso filtrar operadores, paréntesis y comodines para que nunca un atacante sea capaz de inyectar lógica dentro de nuestra aplicación.

********************************************************
Índice:
LDAP Injection & Blind LDAP Injection (Parte I de III)
LDAP Injection & Blind LDAP Injection (Parte II de III)
LDAP Injection & Blind LDAP Injection (Parte III de III)
********************************************************

Entradas populares