Herramienta forense Log2Pcap

A principios de septiembre tenía que escribir un artículo para presentarlo como propuesta para la certificación GOLD GCIA de GIAC (SANS). Este documento tenía que estar relacionado con el tráfico de red y la detección de intrusos, orientado al análisis forense.

Del conjunto de distintas ideas que tenía en mente, pensé en las veces que me había encontrado con un registro (log) de un servidor Web, en el que se requería emplear expresiones regulares usando listas blancas y negras de patrones que identificasen de entre todas las entradas del registro cuáles podían identificar un incidente de seguridad.

Adicionalmente a las listas negras y blancas, tenemos una serie de herramientas de detección de intrusos a nivel de sistema, donde dado un registro de un servidor es capaz de detectar alertas a partir de unas reglas previamente creadas. Un ejemplo de estas herramientas fue expuesto con anterioridad por José Luis Chica donde se empleó la herramienta OSSEC.

Teniendo en cuenta estos motores de reglas, uno de los entornos con mayor número de reglas son las herramientas de detección de intrusos a nivel de red, como por ejemplo Snort. El principal problema de las herramientas de detección de intrusos a nivel de red es que los datos de entrada requieren justamente eso: tráfico de red, pero el técnico dispone del registro del servidor y no del tráfico de red que lo generó.

Por tanto, para poder usar estos motores de reglas es necesario ser capaz de leer ese registro de entrada y a partir de éste, generar el tráfico de red oportuno, de forma que dicho tráfico de red pueda ser inyectado en una herramienta de detección de intrusos a nivel de red. Con esta idea programé una pequeña herramienta (prueba de concepto), la cual es capaz de leer el fichero de registro de un ISS, Apache, Nginx o IBM Web Seal y guardar en un fichero PCAP el tráfico de red que generó dicho registro.

Para entenderlo mejor vamos a ver un ejemplo de uso de la herramienta, donde se dispone de un registro de un servidor web Apache donde una IP atacante (y ficticia) ha realizado una auditoría Web con la herramienta Nikto:

Por ello vamos a proceder a usar la herramienta Log2Pcap para que recree el tráfico de red correspondiente al registro del servidor. Lo primero es indicar donde está el fichero del registro, que se trata de un servidor Apache, la IP y puerto del servidor Web que ha sido auditado:

Como resultado tenemos un fichero llamado “result.pcap” que ocupa 168 K. Dicho fichero ya puede ser leído por todas las herramientas de red que tengan la capacidad de leer el fichero PCAP, como por ejemplo Tcpdump o Tcpflow:

De la misma forma que puede ser inyectado en este tipo de herramientas, también puede ser inyectado en un Snort para que emplee el motor de reglas en búsqueda de posibles ataques:

Dando como resultado las siguientes alertas:

El código fuente de la herramienta lo podéis encontrar aquí mientras que la documentación la tenéis explicada aquí. Siento que el inglés no sea el mejor pero el documento fue escrito hace aproximadamente cinco meses.

Respecto a la evolución de la herramienta, la segunda versión está casi terminada. La herramienta ha sido prácticamente reescrita desde cero y se han añadido funcionalidades como poder inyectar el tráfico a un interfaz de red sin ser necesaria la creación del fichero PCAP, API orientada a objetos e integración con Plaso.

En lo concerniente a las limitaciones la más importante es obvia: se depende totalmente de la integridad y de la información almacenada en dicho registro. Durante el desarrollo de la herramienta caí en la cuenta que prácticamente la mayoría de servidores web no registran por defecto las variables enviadas por POST. Por lo tanto, aunque la herramienta está preparada para leer registros con parámetros POST, si el servidor no está configurado adecuadamente se está perdiendo mucha información. La segunda limitación viene dada al intentar establecer una línea de tiempo, puesto que en ningún momento la herramienta relaciona la alerta de Snort con la entrada del registro que generó la alerta.

Otro punto importante a entender es que la herramienta no pretende sustituir a las expresiones regulares, a los motores de detección de intrusos a nivel de host o cualquier otro método usado previamente. El objetivo de la herramienta es ofrecer otra opción más que pueda proporcionar más información al técnico que gestione el incidente.

Para evitar confusión quiero comentar que existe otra herramienta perteneciente al motor de Samba que usa el mismo nombre. La cual hace justamente lo que su nombre indica: coge un fichero samba y genera un fichero PCAP. He de reconocer que nunca he usado dicha herramienta y que no conocía la existencia de la misma. Ambas tienen propósitos totalmente distintos, protocolos distintos y nada en común. He mantenido el nombre Log2Pcap porque es la metodología de nombre empleado para este tipo de herramienta. Posiblemente en la segunda versión, y dada las nuevas funcionalidades, le cambiaré el nombre a LogWeb2Net.

Para finalizar agradecería que los lectores me enviasen el fichero log de cualquier otro servidor Web no soportado hasta la fecha, lógicamente anonimizado antes del envío. Con un par de líneas donde se traten diferentes métodos como GET y POST sería suficiente.

Snort: “byte_test” for dummies

Recientemente un usuario del blog pregunto por qué en las reglas de detección de Malware con Snort, cuando se quiere detectar la consulta DNS hacia ciertos dominios sospechosos, se emplean ciertos caracteres y condiciones como la comprobación “byte_test:1, !&, 0xF8, 2;“. Para explicarlo vamos a tomar como ejemplo la siguiente regla VRT para la detección del malware Gauss:

alert udp $HOME_NET any -> any 53 (msg:"BLACKLIST DNS request for known malware domain 
bestcomputeradvisor.com - Gauss"; flow:to_server; byte_test:1,!&,0xF8,2; 
content:"|13|bestcomputeradvisor|03|com|00|"; fast_pattern:only; metadata:impact_flag 
red, policy balanced-ips drop, policy security-ips drop, service dns; 
reference:url,gauss.crysys.hu/; reference:url,www.securelist.com/en/blog/208193767

[Read more…]

Auditando la pila TCP con Scapy

Recientemente he tenido que jugar con la biblioteca Scapy para Python. Ésta permite crear cualquier tipo de paquete de red con un par de simples comandos, incluso para protocolos no existentes mediante paquetes RAW.

En este caso pondré de ejemplo la necesidad de tener que enviar paquetes TCP a un puerto determinando usando cualquier combinación de flags TCP, con el objetivo de evaluar el comportamiento de la pila TCP al recibir cualquier combinación de flags.

Hay que tener en cuenta que no estamos hablando de permutaciones, puesto que es lo mismo enviar un paquete con el flags Syn y Ack que enviar un paquete con los flags Ack y Syn. Sí, seguramente habrán recordado la frase “el orden de los factores no altera el producto“. Por tanto es necesario generar cualquier combinación teniendo en cuenta que el orden no afecta y que no se desea enviar más paquetes de los estrictamente necesarios.

[Read more…]

Apple: Think Different y la importancia de controlar las actualizaciones

Hoy voy a contarles una historieta que me ha ocurrido con un dispositivo Thunderbolt, empleado en la mayoría de equipos de sobremesa o portatiles de la compañia Apple y cuyo diseño fue llevado a cabo por la compañía Intel. Este puerto fue presentado por Apple hace cosa de un año como alternativa al HDMI, USB y FireWire ya que emplea tecnología óptica que permite grandes tasas de transferencias de datos de entorno a 10Gbps empleando el puerto como multiuso: video de alta definición y transferencia de gran volumen de datos.

Como buen talibán, esto… aficionado de Apple que soy, adquirí el Mac Book Air nuevo (ahora ya viejo) con el nuevo puerto ThunderBolt, aunque no tuviera ningún dispositivo para este puerto todavía, el portátil y la propaganda lo valían. Para quien no conozca el portátil indicarle que no dispone de interfaz de red (RJ45) donde conectar un cable de red, pero Apple para subsanar este posible requisito vendía un adaptador USB para poder conectar el portátil a un cable de red, todo ello al módico precio de 29 euros. El problema de este dispositivo es que no permitía tasas de transferencia reales superiores a los 3mb/s, algo bastante lento si tenemos NAS en red y necesitamos copiar los videos de “nuestras vacaciones en alta definición” de 20GB por video (you known what I mean).

Por ello, Apple sacó recientemente un adaptador Thunderbolt a RJ45 donde se aseguraban unas transferencias de red de entorno a 60 MB/s, una gran mejora respecto al dispositivo USB. Como buen consumidor que soy, fui a la tienda de Apple Store a adquirir mi nuevo y flamante cacharrito al módico precio de ¿27 euros? ¿Cómo? Qué raro… hacía una semana habia visto que costaba 29 euros y no 27 euros. Pues mejor… supongo.

Fue entonces cuando llegó la sorpresa, llego a mi casa, conecto el dispositivo y… ¡no pasa nada! No me detecta nada, no hay nada; reviso las actualizaciones pendientes pero no hay actualizaciones pendientes. Es entonces cuando, después de santiguarme, busco en Google el motivo por el cual el dispositivo no funcionara, que claramente indica que necesito la versión software 1.2 del Thunderbolt desarrollaba exclusivamente para dar soporte al adaptador RJ45. El problema es que la actualización había desaparecido de la faz de la tierra. No se podia descargar de Apple Store y la página no estaba disponible.

Sigo buscando información y leo que la actualización estaba causando problemas a un gran número de usuarios, a los que les dañaba el arranque de Mac Os X. Esta es la razón de que Apple actuase de la mejor forma posible: eliminó la actualización, bajó 2 euros el precio del producto y dejó a los usuarios que habiamos comprado el dispositivo sin posibilidad de poder usarlo. Una semana después mi portátil me indica que hay una nueva actualización: “Actualización Thunderbolt 1.2”, que pasa de ocupar los 170K byte de su predecesora a 220Kbytes (aproximadamente, no recuerdo los valores de memoria). La gracia de todo esto es que Apple seguía llamandola igual que la que dañaba el arranque de algunos Mac OsX, es decir, ambas eran la 1.2 sin ninguna diferenciación entre ambas.

Resumiendo. Sacas un producto con un firmware que daña el arranque de algunos Mac OsX, de modo que en cierto modo tus clientes pasan a ser beta testers de tus productos, eliminas la actualización de tu producto sin notificarlo pero sigues vendiendo el producto en tu web aunque no pueda ser utilizado y para acabar de arreglarlo sacas el parche unas semanas después sin indicar nada en las release notes, como si aquí no hubiera pasado nada (si esto lo hace otro fabricante hubieramos visto la repercusión, pero en este caso era Apple: “Think different” con tu procesador Intel x86).

Como cualquier historia, esta también tiene moraleja. Vamos con ella.

Aunque en mi caso este problema no ha supuesto más que una pequeña molestia temporal que no era otra que no poder utilizar el adaptador comprado y seguir funcionando a la velocidad del caracol, cabe imaginar qué puede haber supuesto para una empresa cuya plataforma principal sea Apple, una actualización en masa del controlador de este dispositivo, dado que al parecer la solución planteada pasaba en general por la reinstalación del sistema operativo.

Aunque el personal de seguridad solemos hacer mucho énfasis en la necesidad de instalar los parches las actualizaciones que proporcionan los fabricantes, lo cierto es que esta práctica sólo hace frente, en general a una dimensión de la seguridad: la confidencialidad, mientras que puede, como en el ejemplo que comentaba, poner en riesgo otra como es la disponibilidad, probablemente mucho más importante cuando se trata de equipos de usuario final (donde la información está alojada en repositorios comunes y no en el propio equipo) de los cuales depende de manera directa la productividad.

Por tanto, la moraleja de la historia es que sin quitarle la importancia que se merecen a las actualizaciones de los fabricantes, su aplicación “descontrolada” puede dañar seriamente el funcionamiento de una organización. De ahí la necesidad de poner en marcha aplicaciones internas de distribución de actualizaciones (por ejemplo, tipo WSUS para Windows) y por supuesto, cuando nos vamos a entornos corporativos o servicios críticos, los parches deben ser aplicados siempre primero al entorno de preproducción y tras la verificación de funcionamiento, al de producción con su correspondiente gestión de cambios (como es natural deberá ser igual al de producción o de lo contrario estaremos haciendo el tonto).

No hay que olvidar nunca que, para bien o para mal, las combinaciones de hardware y software es tan grande que nuestro entorno puede tener características que el fabricante no haya tenido en cuenta o haya podido reproducir. Así que como siempre, mejor prevenir que curar.

Yo solo quería alquilar un piso

Hace un año decidí alquilar una vivienda y como el resto de gente de a pie, decidí publicarlo en una web de venta y alquiler de inmuebles. Para ello procedí a registrarme introduciendo información básica como nombre, DNI, dirección, teléfono, etc. Adicionalmente tuve que emplear mis datos bancarios para pagar mejoras como “siempre visible / destacado”, publicar vídeos, imágenes de mayor resolución, primero en la búsqueda, etc.

Después de estar un buen rato entre subir fotos, ordenarlas y redactar la descripción de la vivienda me di cuenta que me habían enviado un correo de la web en donde había dado de alta el inmueble. Era el típico correo de bienvenida donde se me indicaba entre otra información mi usuario y mi contraseña, en texto en claro por supuesto.

Entonces tuve una idea fugaz: si me han mandado la contraseña en texto en claro… ¿no será porque la están almacenando en texto en claro? Y que mejor forma que confirmarlo que emplear los métodos de recuperación de contraseña ante una pérdida de la misma, así que me da por recordar la contraseña donde se indica literalmente “enviadme mi contraseña!”. ¡Sorpresa! Me envían al correo mi contraseña en texto en claro.

Lanzado como soy yo, me pongo en contacto con la web mediante correo electrónico y amablemente les indico que por favor cifren las contraseñas en la base de datos, a lo que recibo una contestación donde se me agradece la información proporcionada y que lo tendrán en mente como mejora.

Un año después he vuelto hacer la prueba y como supondrán todo sigue exactamente igual:

la contraseña de acceso en lala.com para el usuario correo@dom.es es En_Claro

Lo siguiente era consultar con el Departamento de Consultoria de nuestra empresa, para preguntarles si era obligatorio cifrar las contraseñas, ya que aunque yo desde el punto de vista técnico lo tenía clarísimo, desde el punto de vista de la LOPD no lo tenía tan claro. La contestación fue que ese tipo de aspectos estaban regulados por el siguiente artículo:

[RDLOPD] Artículo 93. Identificación y autenticación.

1. El responsable del fichero o tratamiento deberá adoptar las medidas que garanticen la correcta identificación y autenticación de los usuarios.

2. El responsable del fichero o tratamiento establecerá un mecanismo que permita la identificación de forma inequívoca y personalizada de todo aquel usuario que intente acceder al sistema de información y la verificación de que está autorizado.

3. Cuando el mecanismo de autenticación se base en la existencia de contraseñas existirá un procedimiento de asignación, distribución y almacenamiento que garantice su confidencialidad e integridad.

4. El documento de seguridad establecerá la periodicidad, que en ningún caso será superior a un año, con la que tienen que ser cambiadas las contraseñas que, mientras estén vigentes, se almacenarán de forma ininteligible.

Mi pregunta ahora es para ustedes. En calidad de usuario preocupado por la seguridad de sus datos, ¿debería informar a la Agencia Española de Protección de Datos o les envío un segundo correo para que me vuelvan a indicar que lo introducirán en sus mejoras próximamente? De momento ya les he mandado un segundo correo haciendo alusión al artículo 93, al que estoy esperando respuesta.

Les tendré informados.

Bastionado de Apache Tomcat (II)

Tras la primera parte que vimos el otro día, en esta entrada vamos a ver el uso de Security Manager y la correcta configuración del protocolo SSL sobre un Apache Tomcat.

Respecto al primero, vamos a configurar Security Manager para que restrinja el uso de ciertos métodos y clases que puedan implicar un riesgo para el servidor de aplicaciones. Estas restricciones se definen en el fichero catalina.policy. Para activar Security Manager será necesario añadir las siguientes dos entradas en el arranque de Tomcat:

-Djava.security.manager 
-Djava.security.policy=$CATALINA_BASE/conf/catalina.policy  

Nota: si se usa el script por defecto de arranque en vez de JSVC se debe añadir el tag -security, que realmente hace lo mismo: añadir las dos entradas anteriores.

Un ejemplo práctico de Security Manager sería una aplicación con un fallo de seguridad donde un posible atacante ha conseguido subir una Web Shell para intentar ejecutar órdenes en el servidor mediante el método Runtime.getRuntime().exec(). Al intentar ejecutar dicho método Security Manager impedirá su ejecución devolviendo la siguiente excepción:

java.security.AccessControlException: access denied

Donde sí se ha tratado adecuadamente la excepción, tal como se documento en la anterior entrada, únicamente se mostrara la página error.html cuando se intente ejecutar órdenes en el servidor mediante la Web Shell del atacante. Otro punto importante a tener en cuenta es la correcta configuración del registro de sucesos. Para ello se recomienda seguir los siguientes consejos:

  • Aplicar una configuración adecuada para registrar la mayor información posible de los clientes que han realizado una solicitud a una aplicación del servidor Tomcat, añadiendo para ello la siguiente válvula al campo Host del fichero server.xml:
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
      prefix="localhost_access." suffix=".log"
      resolveHosts="false"
      pattern="%t %h %H %m %s "%r" cookie:%{SESSIONID}c User- Agent:%{User-Agent}i " />
    
  • Crear registros específicos para cada aplicación. Para realizar esta tarea es necesario especificar un fichero de logging.properties por cada aplicación desplegada en el directorio .../aplicacion/WEB-INF/clases/ donde aplicacion es el directorio de la aplicación. Dicho fichero tendrá la siguiente configuración:
    handlers = org.apache.juli.FileHandler, java.util.logging.ConsoleHandler
    org.apache.juli.FileHandler.level = FINEST
    org.apache.juli.FileHandler.directory = ${catalina.base}/logs
    org.apache.juli.FileHandler.prefix = aplicacion.
    org.apache.juli.FileHandler.suffix = .log
    org.apache.juli.FileHandler.rotatable = true
    java.util.logging.ConsoleHandler.level = FINEST
    java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
    
  • Emplear LOG4J para aquellas aplicaciones donde las librerías de registros por defectos de Tomcat no cumplan todos nuestros requisitos.

Para finalizar trataremos de configurar adecuadamente los conectores que empleen protocolos cifrados (conectores SSL) definidos en nuestro servidor de aplicaciones Tomcat siguiendo para ello los siguientes puntos:

  • Forzar a los conectores que no cifran las comunicaciones a usar SSL mediante los siguientes pasos, llamado también redirección a conector SSL:

    1. En los conectores definidos en el fichero server.xml que no se empleen protocolo SSL se debe aplicar una redireccionan hacia un conector que emplee el puerto SSL:

    <Connector port="80" protocol="HTTP/1.1"
                    redirectPort="443"
    ...
    />
    <Connector port="443" protocol="HTTP/1.1"
                    scheme="https"
    ...
    />
    
  • 2. Indicar que los métodos GET, POST y HEAD sean confidenciales añadiendo la siguiente entrada al fichero web.xml:

    <web-app xmlns="http://java.sun.com/xml/ns/javaee" …>
    ...
        <security-constraint>
                <web-resource-collection>
                        <web-resource-name>Servidor Aplicaciones</web-resource-name>
                        <url-pattern>/*</url-pattern>
                        <http-method>GET</http-method>
                        <http-method>POST</http-method>
                        <http-method>HEAD</http-method>
               </web-resource-collection>
                <user-data-constraint>
                        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
                </user-data-constraint>
        </security-constraint>
    </web-app>
    
  • Impedir el uso de algoritmos y protocolos débiles. Para ello en la configuración de los conectores cifrados, definidos en el fichero server.xml, se deberá indicar que se desea usar TLS mediante la variable sslProtocol="TLS". Hay que tener en cuenta que el valor “TLS” en las últimas versiones de Tomcat identifica a la versión 1.1, y por tanto, acepta tanto TLS como SSL v3 pero no SSL versión 2, tal y como se especifica en la documentación del protocolo TLS v1.1.

    Será necesario definir que ciphers suite de los soportados por Java (ver enlace) se deben emplear. Estos presentan la siguiente forma:

    Proto_AlgClaveAsimetrica_WITH_AlgClaveSimetrica_tamClaveSim_AlgCompendio
    
  • Para esta restricción se pueden aplicar listas negras: aceptamos todos los algoritmos menos unos cuantos, como se recomienda en la entrada de SecurityByDefault, o se pueden aplicar listas blancas: solo permito los estrictamente indicados. Para gustos colores, pero en mi opinión prefiero aplicar listas blancas, es decir, indicar solo los que puedo usar.

    En mi opinión, sin ser ni mucho menos un experto en algoritmos de cifrado, recomendaría usar TLS, algoritmos de curva elíptica o RSA como clave asimétrica, AES de 128 bits mínimo para clave simétrica y SHA como algoritmo de compendio:

    TLS_RSA_WITH_AES_128_CBC_SHA
    TLS_RSA_WITH_AES_256_CBC_SHA
    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
    

    Por ello se definirán los cipher suite indicados con anterioridad en cada conector SSL creado en el fichero server.xml mediante la variable ciphers separados por comas:

    <Connector port="443" protocol="HTTP/1.1"
    scheme="https"
    ...
    ciphers="TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,
    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA"
    ...
     />
    

    Hay que tener en cuenta que para que pueda emplear algoritmos de curvas elípticas y algoritmos simétricos de más de 128 bits será necesario sustituir las librerías por defecto del Java JDK local_policy.jar y US_export_policy.jar por las suministradas en el paquete Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files. Esto es debido a que por defecto el JDK está restringido a las leyes de EEUU donde no se permite más de 128 bits ni algoritmos de curva elíptica. Una vez sustituidas las librerías y reiniciado Tomcat ya se permitirá emplear dichos algoritmos, tal como se muestra a continuación:

    $ openssl s_client -host localhost -port  443 -tls1 -cipher AES256-SHA
    …
    New, TLSv1/SSLv3, Cipher is AES256-SHA
    Server public key is 1024 bit
    Secure Renegotiation IS supported
    Compression: NONE
    Expansion: NONE
    SSL-Session:
        Protocol  : TLSv1
        Cipher    : AES256-SHA
    ...
    

Con este par de pasos ya se dispondrá de un servidor Tomcat con una configuración de seguridad más o menos correcta ya que se han omitido una serie de configuraciones adicionales que se deberían aplicar para mejorar la seguridad del entorno, aspecto que queda fuera del contexto de esta entrada.

Bastionado de Apache Tomcat (I)

A raíz de la entrada de Guillermo Mir sobre el bastionado de Apache (parte I y parte II) y una entrada en el blog SecurityByDefault sobre cómo trabajar con SSL en Tomcat me he decidido a escribir una entrada sobre el bastionado de Tomcat que debía desde hace mucho tiempo a Manolo, quien no se olvidaba de recordármelo (N.d.E. como es mi obligación…).

En primer lugar indicar que la entrada se centrará en un correcto bastionado de Tomcat sobre un entorno Linux, puesto que sinceramente Tomcat en Windows no ofrece las mismas opciones, por muy Java que sea, que en un entorno Linux. Como documentar una guía de bastionado de Tomcat ocuparía muchas páginas, esta y la siguiente entrada tratará los principales puntos a tener en cuenta durante un bastionado adecuado. Para más información os remito a la documentación de Tomcat.

[Read more…]

Parcheando código con GDB

Hace un par de días, durante un reto de seguridad, nos encontramos con la situación de tener que modificar con GDB el código de un binario para que realizara las acciones que nos interesaban y no para las que había sido programado; esto suele usarse en los retos tipo “crack me” o “patch me”. Todo sea dicho, al final no era la solución al reto, pero como todo reto, se suelen probar distintas opciones.

De forma resumida tenemos un programa con la función principal y dos funciones adicionales declaradas. En la función principal se llama solo a una de las dos funciones, en este caso a la función que llamaremos “malo”, pero nosotros necesitábamos que en vez de llamar a esa función llame a la otra función, la que llamaremos “bueno”.

Un ejemplo del código en C sería el siguiente, al que denominaremos “prueba.c”:

#include <stdio.h>
#include <stdlib.h>
// Funciones auxiliares

void bueno(void) { printf("SIIII");}
void malo(void) { printf("Noooo");}

// Función principal que llama a la funcion malo
void main(void){malo();}

Nuestro objetivo era que el programa, en lugar de llamar a la función malo en el main, llamara a la función bueno. Para el reto teníamos únicamente GDB como debugger. Para la entrada usaremos el código anterior por estar más simplificado y resultar más claro.

Teniendo en cuenta que el código anterior se ha escrito en el fichero “prueba.c”, hay que seguir los siguientes pasos para compilar y cargar el binario en GDB:

$ gcc -o prueba prueba.c
$ gdb prueba

Una vez accedido a GDB le indicaremos que queremos usar el formato Intel en vez del AT&T seleccionado por defecto (me gusta más, para gustos colores o sabores):

$ set disassembly-flavor intel

El siguiente paso consistirá en ver el código del programa, teniendo encuenta que las pruebas se hacen sobre un entorno de 64 bits (por eso las direcciones son tan largas):

(gdb) disas main 
Dump of assembler code for function main: 
   0x0000000000400514 <+0>:	push   rbp 
   0x0000000000400515 <+1>:	mov    rbp,rsp
   0x0000000000400518 <+4>:	call   0x4004fc <malo>
   0x000000000040051d <+9>:	pop    rbp 
   0x000000000040051e <+10>:	ret    
End of assembler dump.
NOTA: “disas” viene de la orden “disassemble” pero se puede usar de esta forma porque no hay órdenes que empiecen por “disas” y no es necesario escribir la orden completa… es igual que ocurre en los cacharritos CISCO :)

Como vemos la llamada a la función malo, mediante CALL, está en posición “0x400518”, la cual apunta a la dirección “0x4004fc” donde se encuentra la primera instrucción de la función malo, tal como se muestra a continuación:

(gdb) disas malo 
Dump of assembler code for function malo: 
   0x00000000004004fc <+0>:	push   rbp 
   ...
   0x0000000000400512 <+22>:	pop    rbp 
   0x0000000000400513 <+23>:	ret    
End of assembler dump. 

El siguiente paso es analizar el tipo de CALL de la función principal (main), identificando el tipo de OP Code de la función; en nuestro caso se mostrarán los 8 bytes a partir del CALL:

(gdb) x/8xb 0x400518 
0x400518 >main +4<:	0xe8	0xdf	0xff	0xff	0xff	0x5d	0xc3	0x90

Vemos que el primer byte es 0xe8, que corresponde con el OP Code del CALL a dirección relativa Call32(). Dicha llamada consta de 5 bytes; el primero es el OPCode representado como “0xe8” identificativo de la llamada y los siguientes 4 bytes es la dirección donde se encuentra la función que se desea llamar. Por tanto para ver correctamente esta instrucción será necesario visualizar solo los 5 primeros bytes:

(gdb) x/5xb 0x400518
0x400518 <main +4>:	0xe8	0xdf	0xff	0xff	0xff

Donde tenemos el Op Code 0xe8 y la dirección “0xdf 0xff 0xf 0xff“. Como este ordenador es un LE hay que darle la vuelta: es decir, la dirección real es ” 0xff 0xff 0xff 0xdf”. Esto se puede realizar cambiando el carácter “b” de byte por el de word “w” e indicando la posición de memoria donde empieza la dirección que se quiere tratar, es decir, la dirección del CALL + 1 (quitamos el OP Code 0xe8):

(gdb) x/xw 0x400519
0x400519 <main +5>:	0xffffffdf

Y la pregunta que se estarán realizando: ¿de dónde sale ese dato? Pues ese dato es el valor negado de la diferencia (offset) entre el final de la llamada del CALL y la función que se quiere llamar:

Not (OFFSET) = pos pre CALL + tam instrucción call - F(x) a saltar

¿Lioso? Veámoslo por partes. Tenemos de offset este valor “0xffffffdf”, con lo que la operación NOT sería:

0xdf -> 1101 1111 (el not de esto) -> 0010 0000 -> 0x20

Por tanto el NOT de “0xffffffdf” es “0x00000020”. Si sabemos que el Call se llama en la posición “0x400518” y el tamaño de la instrucción CALL son 5 bytes, entonces sabemos que la f(x) termina en la posición “0x40051c”; cuidado porque el byte “518” ya es el primer byte del CALL:

F(x) a saltar = pos pre CALL + tam instrucción call - Not (OFFSET)

F(x) a saltar = 0x400518 + 0x4 - 0x20 = 0x40051c - 0x20 = 0x4004FC

Es decir, la posición donde está la función malo que hemos identificado con anterioridad. Ahora queremos modificar el CALL para que apunte a bueno. Para ello es necesario obtener la posición de la función bueno:

(gdb) disas bueno
Dump of assembler code for function bueno: 
   0x00000000004004e4 <+0>:	push   rbp
   ...

Ya sabemos que el último byte de CALL está en la posición “0x40051c”, por tanto:

0x40051c - 0x4004e4 = 0x38

Not de 0x38 = 0xFFFFFFC7, y como es necesario ponerlo en LE -> 0xC7 0xff 0xff 0xff

Si recordamos, en el CALL de la función principal teníamos la siguiente entrada “0xe8 0xdf 0xff 0xff 0xff” la cual apuntaba a malo, y ahora queremos sustituirla por 0xc7 0xff 0xff 0xff: solo necesitamos cambiar el segundo byte del call, es decir 0xdf por 0xc7. O sea, indicarle que el byte “0x400519” valga 0xC7:

(gdb) x/xb 0x400519
0x400519 
: 0xdf

Para realizar dicha operación es necesario usar “set” indicando que lo que se quiere sustituir es un byte en la posición indicada:

(gdb) set *(unsigned char*) 0x400519 = 0xc7 
Cannot access memory at address 0x400519

Como vemos ha fallado, no tenemos acceso a dicha posición de memoria, hagamos una triquimechuela poniendo un breakpoint en la instrucción previa al call y ejecutando el programa:

(gdb) break 0x0000000000400515 
Function "0x0000000000400515" not defined. 
Make breakpoint pending on future shared library load? (y or [n]) y 
Breakpoint 1 (0x0000000000400515) pending.

Ejecutamos ahora el programa esperando a que se detenga en el break indicado, una instrucción antes del call:

(gdb) start 
Temporary breakpoint 2 at 0x400518 
Starting program: /home/moxilo/prueba

Temporary breakpoint 2, 0x0000000000400518 in main ()
(gdb) disas main
Dump of assembler code for function main: 
   0x0000000000400514 <+0>:	push   rbp 
   0x0000000000400515 <+1>:	mov    rbp,rsp 
=> 0x0000000000400518 <+4>:	call   0x4004fc <malo>
   0x000000000040051d <+9>:	pop    rbp 
   0x000000000040051e <+10>:	ret    
End of assembler dump.

Ahora ya podemos aplicar el cambio y comprobar que la modificación se ha llevado a cabo al apuntar el call a la función bueno en vez de a la función malo:

(gdb) set *(unsigned char*) 0x400519 = 0xc7
(gdb) disas main 
Dump of assembler code for function main: 
   0x0000000000400514 <+0>:	push   rbp 
   0x0000000000400515 <+1>:	mov    rbp,rsp 
=> 0x0000000000400518 <+4>:	call   0x4004e4 <bueno> 
   0x000000000040051d <+9>:	pop    rbp 
   0x000000000040051e <+10>:	ret    

End of assembler dump.

Como vemos ya apuntamos a la función bueno, por lo que si le decimos que “continue” mostrará el texto de la función buena (“SIIII”) y no el de la función mala:

(gdb) continue 
Continuing. 
SIIII[Inferior 1 (process 3897) exited with code 05]

Por tanto hemos parcheado el programa en ejecución con GDB, de tal forma que hemos obtenido la dirección de memoria de otra función, sustituyendo posteriormente la dirección de la llamada CALL por la de la función que nos ha interesado. Esto también se puede usar para sustituir instrucciones de códio que realizan ciertas comprobaciones que no nos interesa por NOPs (0x90), es decir, por “nada”.

Presentamos Mail Malware Trap

A raíz de la entrada del correo electrónico donde se enlazaba a un troyano bancario, comencé a desarrollar un programita que tenía en mente desde hacía tiempo. Se trata de un recolector de Malware de correos electrónicos, encargado de acceder a distintas cuentas de correo, obtener el correo electrónico, almacenarlos en base de datos, y en caso de poseer adjuntos, ser analizados con la API de Virus total.

Veámoslo con más detenimiento. Primero será necesario disponer de una serie de sondas, correspondientes a cuentas de correo electrónico perteneciente en distintos servidores públicos como son Gmail, Yahoo o Hotmail, así como una serie de correos privados como por ejemplo el corporativo. Dichas cuentas de correo solo se usarán para recoger el correo que llegue a la bandeja de entrada y Spam. Para ello en ocasiones será necesario hacer filtros para trasladar el correo de la carpeta Spam a bandeja de entrada. Por ejemplo para Gmail sería necesario crearse un filtro de tipo texto con la siguiente entrada “is:spam” e indicar que debe enviarse como correo no leído a la bandeja de entrada. Con esto dispondremos de varias cuentas de correo distribuidas sobre distintos servidores de correo cuyo único objetivo es almacenar los correos de entrada.

Así, el recolector de malware lo que hará será conectarse a las cuentas para descargarse el correo nuevo y una vez obtenido insertará en una base de datos MySQL el correo descargado, indicando de qué servidor de correo lo ha descargado, a qué hora ha llegado, quién lo ha enviado, con qué asunto y con qué texto. Tanto el asunto como el texto se almacenarán en base64 en la base de datos.

El agente también comprobará si el correo electrónico tiene adjuntos. En caso de tener se descargará el adjunto conservando su nombre y lo almacenará en un árbol de sistema de ficheros compuesto por año, mes y identificador de correo de la base de datos. A su vez se insertará en la base de datos de que correo procede el adjunto, donde está almacenado, su comprobación MD5, si ha sido subido anteriormente a Virus Total y en caso afirmativo, se nos mostrará el resultado de los antivirus.

Veamos como funciona. En primer lugar será necesario disponer de una base de datos MySQL en el servidor donde instalemos el agente. Una vez dispongamos del MySQL será necesario crearnos el usuario y la base de datos mediante las siguientes órdenes:

$ mysql -u root -p
> CREATE database mailmalwaretrap;
> CREATE USER 'mailmalwaretrap'@'localhost' IDENTIFIED BY 'MiContrasenya';
> GRANT SELECT, INSERT, DELETE ON mailmalwaretrap.* TO 'mailmalwaretrap'@'localhost';

A continuación volcaremos el esquema de la base de datos, que podéis obtener de mailMalwareTrap.sql (los ficheros están al final del post):

$ mysql -u root -p < mailMalwareTrap.sql

Posteriormente configuraremos el agente, escribiendo en el fichero “mailMalwareTrap.conf” una línea por cada cuenta de correo que dispongamos, teniendo en cuenta que se deberá seguir el siguiente esquema “servidorCorreo|cuentaCorreo|contraseña”. Si por ejemplo nuestro servidor de correo es “pop.gmail.com”, la cuenta “lol@gmail.com” y contraseña “lolazo” el fichero será el siguiente:

# cat mailMalwareTrap.conf
pop.gmail.com|lol@gmail.com|lolazo

Para finalizar obtendremos el agente “mailMalwareTrap.py” donde será necesario tener instalado las dependencias de MySQL para Python.

Para el ejemplo que mostraremos usaremos una única cuenta de Gmail donde enviaremos desde otro servidor de correo adjuntando un fichero pdf que corresponde con el meterpreter. Una vez enviado ejecutaremos el script para que procese los correos nuevos. Se aconseja que se ponga el script en el crontab para que se ejecute cada hora:

$ mailMalwareTrap.py
Hay correos nuevos en "pop.gmail.com".
$

Si accedemos a la base de datos tabla “mail”, veremos que se ha insertado el nuevo correo con identificador “1”, correspondiente a la sonda “1”, que ha llegado a las 10:52 del 11 de Noviembre, cuyo asunto y texto están en base64 para evitar “sorpresas”:

Si analizamos la tabla malware veremos lo siguiente:

Malware con identificador 1, procedente del correo con ID 1, que se encuentra almacenado en “/tmp/result/2011_11/1/meterpreter.pdf”, que ya había subido con anterioridad a Virus Total, listado de los resultados de los distintos antivirus (la mayoría lo detectan), texto añadido por nosotros (en este caso está a NULL) y para finalizar la comprobación md5 del malware.

Si comprobamos a mano la comprobación md5 veremos que es la misma que hay almacenada en la base de datos:

$ md5sum /tmp/result/2011_11/1/meterpreter.pdf
062e7ecdc4a15f2f49cb5b2b09e5a4ea /tmp/result/2011_11/1/meterpreter.pdf
$

Como ven es una forma sencilla de tener un recolector de malware de las carpetas Spam de distintos servidores que permite analizar los riesgos que pueden acontecer en su infraestructura.

Como mejoras pendientes tengo dos puntos claramente identificados: el primero será buscar en el texto del correo posibles URLs y analizar con Virus Total si se tratan de URL con malas intenciones. Así mismo el segundo punto lo constituye una interfaz gráfica que permita gestionar los correos de forma más sencilla. Pero todo esto ya otro día ;)

Espero que les haya gustado la entrada, todo sea dicho, imagino que alguien habrá tenido la idea antes, pero sinceramente, yo creo que para aprender es necesario hacerlo uno mismo. Los ficheros pueden descargarlos directamente desde este enlace: mailMalwareTrap.rar.

Un correo ¿amigo?

Ayer domingo mientras pensaba qué excusa poner a mi compañero Raúl por lo del partido del Levante ante el Valencia —donde todo sea dicho de paso claramente nos robaron— me llegó un correo electrónico con una supuesta oferta de trabajo, tal como podemos ver en la siguiente imagen:

El enlace apuntaba a la siguiente dirección “http://anyhub.net/file/5r_v-oferta_838as32-pdf.exe”, donde como puede verse juega con la terminación “pdf” pero tiene extensión “exe”. Al analizar la cabecera del fichero vemos el “MZ” inicial típico de un ejecutable Windows; claramente todo apunta a Malware. Al buscar cadenas de texto en el binario mediante la herramienta “strings” vemos que el ejecutable contiene texto como el siguiente:

Microsoft Internet Security Banking Connections.

El c
digo de seguridad CVV/CVC, es un c
digo formado por tres cifras que est
situado en la parte trasera de su tarjeta de cr
dito.

A la vista de estos primeros indicios, lo que hice fue introducir el fichero en una máquina virtual VMWare para ver qué es lo que realmente hace. Lo primero que observamos es que al binario le han puesto el icono de un PDF de Acrobat Reader para hacer pasar al fichero por PDF a ojos de un usuario inexperto:

Al hacer click sobre el fichero no se ejecuta aparentemente nada, simulando de esta manera que el fichero no se ha abierto correctamente. No obstante, si analizamos los procesos en ejecución vemos que realmente sí que se está ejecutando el programa:

Como hemos visto con la orden strings, parece ser que se trata de un troyano con claros síntomas de intentar obtener datos bancarios, por lo que ejecutamos Internet Explorer y comenzamos a navegar por distintas Webs, sin detectar nada anómalo hasta que entramos en una web bancaria, donde el navegador se cierra y nos aparece la siguiente ventana de Windows, bastante elaborada a nivel gráfico para simular ser una pantalla auténtica del sistema de seguridad de los sistemas Windows:

Al pinchar sobre “Aceptar” se abre una pequeña aplicación con título “Microsoft Internet Security Banking Connections”, tal como vemos en la siguiente captura:

Si se pincha sobre el botón inferior derecho “Activar a mi equipo la conexión segura…” se abre una nueva ventana donde se invita a introducir los datos de nuestra tarjeta de crédito. En nuestro caso se introdujeron (obviamente) datos falsos:

Tras esto, capturamos el tráfico de red que se generaba al hacer click sobre “Activar”, obteniendo como resultado que el malware emplea el recurso “send.php” de la web “elequipodelbarrio.com.ar”, enviando los datos de la tarjeta de crédito en la variable “data” por POST mediante protocolo HTTP. Esto puede verse en la siguiente captura de pantalla:

Al acceder a la web “elequipodelbarrio.com” vemos que aparenta ser una Web legítima con comentarios de varias personas incluidos, tal como se puede ver en la siguiente imagen:

Lo que hace pensar en dos posibles opciones: que la web haya sido comprometida o que se trate de una falsa web para no generar alarma. En mi caso procedí a enviar la alerta al CSIRT-Cv para que gestionasen la alerta temprana.

Para finalizar indicar que se trata de un troyano “típico” sudamericano, más enfocado a intentar engañar al usuario inexperto con pantallas y un entorno gráfico bastante conseguido, que a emplear métodos realmente complejos como es el caso habitual de los troyanos rusos. Lo que me ha llamado la atención es que al subir el binario a VirusTotal no me indicó que hubiera sido subido con anterioridad por otro usuario, y a su vez, que solo 6 de los 42 antivirus hayan reconocido al Troyano como tal:

Me gustaría poder realizar un reversing del troyano para saber si reconoce la URL bancarias por dirección de URL, por ciertos caracteres de texto que identifican un banco o por cualquier otro motivo, pero por desgracia mañana me llega un libro muy esperado “El temor de un hombre sabio” y es una tarea que va a tener que esperar unos días.