06 agosto 2012

XMPPloit, jugando con XMPP

Como ya sabréis muchos de los lectores de SbD, he tenido la oportunidad de presentar XMPPloit en BlackHat USA 2012 Arsenal, siendo una mejora sustancial del script que utilicé para las pruebas de concepto de mi charla en la RootedCON 2012 "XMPP, algo más que chat" (en la que podéis encontrar toda la información al respecto).

Como bien dice su descripción, XMPPloit es una aplicación que permite establecer una pasarela entre el cliente y el servidor, permitiendo monitorizar y manipular el tráfico XMPP entre ellos (aprovechándose de vulnerabilidades en las implementaciones cliente/servidor y del propio protocolo).

En esta versión de la aplicación se ha reescrito completamente todo el sistema de filtros, siendo ahora mucho más personalizable y externo a la aplicación (se configuran en un archivo XML, sin necesidad de modificar el código).

La aplicación se configura a través del archivo xmpp.properties, que tiene la siguiente estructura:

server = 173.194.78.125        #IP/Dominio del servidor de XMPP
debug = all                             #Establece el nivel de debug (none|min|all)
filters = filters.xml                 #Ruta del fichero con los filtros
global_output = output.txt    #Ruta del fichero en el que se guardan las salidas de la aplicación
debug_output = debug.txt     #Ruta del fichero en el que se guarda la comunicación (según el debug)

Por defecto la aplicación está configurada para trabajar con Google Talk por lo que, si se utiliza para otro sistema, habrá que especificar la IP o dominio del servidor XMPP. El nivel 'min' de debug muestra los streams ya manipulados por los filtros (lo que le llega al cliente y al servidor), mientras que con 'all' se muestra siempre la comunicación real y la manipulada (para poder ver los cambios que efectuan los filtros). El fichero 'global_output' se utiliza para guardar las salidas de la aplicación y filtros generales (p.e los credenciales obtenidos tras el ataque 'mechanism downgrade')

Con respecto a la estructura de los filtros (por defecto se encuentran en filters.xml):
<filter>
     <name>example</name>
     <type>internal|client|server|both</type>
     <contains>(string:not_regex)</contains>
     <waitfor>(string:regex)</waitfor> 
     <replace>
          <regex>(string)</regex>
          <replacement>(string)</replacement>
     </replace>
     <remove>(string:regex)</remove>
     <search>
          <regex>(string)</regex>
          <output>(string:path)</output>
     </search>
     <stanza></stanza>
     <repeat/>
     <enabled/>
</filter>

El tipo de filtro establece sobre qué sentido de la comunicación se ejecuta (sólo en la información que proviene del cliente, la del servidor o ambas). El tipo 'internal' indica que es un filtro interno de la aplicación (debido a que ejecuta acciones complejas, como por ejemplo hacer peticiones web para obtener información a adjuntar al mensaje), activándose con la etiqueta 'enabled'. La etiqueta 'contains' indica ante qué contenido actúa el filtro (queda a la espera hasta que se cumpla y la etiqueta 'repeat' indica que se ejecute más de una vez). La etiqueta 'waitfor' es similar a 'contains' pero admite expresiones regulares. Con 'replace' es posible modificar el contenido (y admite expresiones regulares), permitiendo p.e el cambiar todos los enlaces que aparezcan por uno bajo nuestro control (phising, spam, etc). La etiqueta 'search' busca información en los streams y guarda todo lo que encuentre en el archivo que se le especifique en 'output' (p.e el obtener todos los contactos, el feed del correo, etc). Y finalmente, la etiqueta 'stanza' que nos permite inyectar stanzas en la comunicación (que, junto con 'search', nos permitiría hacer peticiones al servidor y descargar datos de la cuenta, sin necesidad de esperar a que lo haga el cliente).

Algunos filtros que se han implementado a modo de ejemplo son:

Evitar el cifrado

<filter>
     <name>avoid_encryption</name>
     <type>server</type>
     <contains>&lt;stream:features&gt;</contains>
     <remove>(?=&lt;starttls).*(?&lt;=&lt;/starttls&gt;)</remove>
     <repeat/>
</filter>

Inyectar stanza para obtener el feed de emails

<filter>
     <name>google_inject_mail_feed</name>
     <type>client</type>
     <waitfor>(?=&lt;presence&gt;).*(?&lt;=&lt;/presence&gt;)</waitfor>
     <stanza>&lt;iq type="get" id="1000"&gt;&lt;query xmlns="google:mail:notify" q="in:anywhere"/&gt;&lt;/iq&gt;</stanza> 
</filter>

Queda a la espera de que el cliente haya finalizado todo el proceso de autenticación (empiece a recibir stanzas de tipo 'presence') e inyecta una que obtiene el feed de correo de la cuenta (pero no solo los no-leídos, sino todos, haciendo uso del parámetro 'q' que actúa igual que la búsqueda en la versión web).

Filtro interno

<filter>
     <name>google_plain_mechanism</name>
     <type>internal</type>
     <enabled/>
</filter>


Fuerza que se utilice PLAIN en vez de el GOOGLE-TOKEN (obteniendo las credenciales de la cuenta). Puesto que la comunicación con el servidor tiene que ser correcta y éste no admite PLAIN, la aplicación utiliza las credenciales para obtener un token válido (a través de ClientLogin) y lo inyecta en la comunicación con el servidor.

Me hubiera gustado haber portado la aplicación a Python (actualmente está desarrollada en Java) pero por falta de tiempo no pude hacerlo (aunque no descarto, puesto que no es una aplicación bastante sencilla).

En el siguiente video tenéis una demo de la aplicación (es la versión anterior que llevaba los filtros 'hardcodeados') :


Podéis acceder a la aplicación y su código fuente en: http://www.ldelgado.es/?xmpploit

---------------------------------------------------------------------
Contribución por Luis Delgado J.