IDS en la máquina

Tras leer la sección anterior seguramente habrá quedado claro que un correcto esquema de detección de intrusos basado en red es vital para proteger cualquier sistema; con frecuencia suele ser el punto más importante, que más ataques detecta, y donde se suelen emplazar la mayoría de sistemas de detección que existen instalados en entornos reales hoy en día. No obstante, esta enorme importancia suele degenerar en un error bastante grave: en muchos entornos los responsables de seguridad, a la hora de trabajar con IDSes, se limitan a instalar diversos sensores de detección basados en red en cada segmento a proteger, creyendo que así son capaces de detectar la mayoría de ataques. Y eso suele generar una falsa sensación de seguridad grave, ya que a la hora de lanzar ciertos ataques un pirata puede eludir fácilmente a estos sensores; los sensores de detección en nuestros segmentos de red son importantes, pero no son la panacea. Para comprobarlo, volvamos a nuestro ejemplo anterior, en el que un atacante trata de descubrir vulnerabilidades en nuestros servidores HTTP: si recordamos dónde nos habíamos quedado, el pirata estaba lanzando un escaneador de vulnerabilidades web contra el puerto 80 de nuestro servidor corporativo; el esquema de detección implantado en el cortafuegos no percibirá nada extraño, pero el sensor ubicado en el segmento donde se encuentra el servidor sin duda verá patrones que denotan un ataque. Por ejemplo, en el momento en que el escáner compruebe la existencia en el servidor de un CGI denominado phf, SNORT generará una alerta similar a esta:
[**] IDS128 - CVE-1999-0067 - CGI phf attempt [**]
03/10-03:29:59.834996 192.168.0.3:1032 -> 192.168.0.1:80
TCP TTL:56 TOS:0x0 ID:5040 IpLen:20 DgmLen:58 DF
***AP*** Seq: 0x91FA846  Ack: 0x5AD9A72  Win: 0x7D78  TcpLen: 20
Como veremos en el punto siguiente, es posible que al generar este aviso, el propio sensor decida lanzar una respuesta automática contra la dirección atacante (por ejemplo, bloquearla en el cortafuegos corporativo). Pero SNORT trabaja basándose en su base de datos de patrones de ataques; en dicha base de datos habrá una regla como la siguiente:
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS 80 (msg:"WEB-CGI phf access";\
flags: A+; content:"/phf";flags: A+; nocase; reference:arachnids,128; \
reference:cve,CVE-1999-0067; )
A grandes rasgos, esta regla viene a decir que cuando el sensor detecte una petición al puerto 80 de nuestro servidor web en cuyo contenido se encuentre la cadena `/phf' levante la alerta correspondiente; de esta forma, cuando el atacante solicite el archivo /cgi-bin/phf del servidor, SNORT `verá' dicha cadena en la petición y generará una alarma. >Pero qué sucede si el atacante solicita ese mismo fichero pero utilizando una petición `ofuscada', formada por los códigos ASCII de cada carácter? Es decir, si en lugar de lanzar una petición como `GET /cgi-bin/phf' hace una similar a `GET %2f%63%67%69%2d%62%69%6e%2f%70%68%66'. La respuesta es sencilla: la petición anterior se `decodifica' en el propio servidor web, o como mucho en un proxy intermedio. Algunos detectores de intrusos, como RealSecure, son en teoría capaces de procesar este tipo de tráfico y analizarlo normalmente en busca de patrones sospechosos, pero otros muchos (SNORT en este caso) no; así, estos últimos no verán en ningún momento la cadena `/phf' circulando por la red, y por tanto no generarán ninguna alarma. Parece entonces evidente que es necesario un nivel adicional en nuestro esquema de detección: justamente el compuesto por los sistemas basados en la propia máquina a proteger.

Antes hemos hablado de los tres modelos básicos de IDSes basados en máquina: verificadores de integridad, analizadores de registros y honeypots. Parece claro que un verificador de integridad en nuestro caso no va a resultar muy útil para detectar a ese atacante (esto no significa que no se trate de modelos necesarios y útiles en otras muchas situaciones). Tendremos por tanto que recurrir a analizadores de logs o a tarros de miel instalados en la máquina. Si optamos por los primeros, es sencillo construir un shellscript que procese los archivos generados por el servidor web - en nuestro caso, aunque igualmente sencillo que procesar registros del sistema o de otras aplicaciones - en busca de patrones que puedan denotar un ataque, como el acceso a determinados archivos bajo el DocumentRoot. Si analizamos cualquier CGI Scanner nos podremos hacer una idea de a qué patrones debemos estar atentos: por ejemplo, intentos de acceso a CGIs como phf o printenv, a archivos passwd, a ficheros fuera del DocumentRoot, etc.

Aunque analizar los registros generados por una aplicación en busca de ciertos patrones sospechosos es una tarea trivial, no lo es tanto el integrar ese análisis en un esquema de detección de intrusos. Seguramente procesar la salida de un `tail -f' del archivo de log correspondiente, enviando un correo electrónico al responsable de seguridad cuando se detecte una entrada sospechosa es fácil, pero por poner un simple ejemplo, >qué sucede cuando la máquina se reinicia o el fichero de registros se rota? >Es necesario volver a entrar y lanzar de nuevo la orden correspondiente para analizar los logs? Seguramente alguien planteará que se puede planificar una tarea para que periódicamente compruebe que se está procesando el archivo, y lance el análisis en caso de que no sea así...>pero hasta qué punto es esto cómodo? >qué sucede con las entradas duplicadas? >y con los registros perdidos que no se llegan a procesar? Evidentemente, no todo es tan sencillo como el simple `tail -f' anterior.

El análisis de registros generados por el sistema o por ciertas aplicaciones suele ser una excelente fuente de información de cara a la detección de actividades sospechosas en nuestros entornos, pero no es habitual aplicar dicho análisis en un esquema de respuesta automática ante ataques. Es mucho más común automatizar la revisión de logs para que periódicamente (por ejemplo, cada noche) se analicen esos registros y se envíe un mensaje de alerta por correo electrónico a los responsables de seguridad en caso de que algo anormal se detecte; evidentemente no es un modelo que trabaje en tiempo real, pero no por ello deja de ser un esquema útil en la detección de intrusos; el único problema que se presenta a la hora de realizar el análisis suele ser la decisión de qué patrones buscar en los registros, algo que depende por completo del tipo de log que estemos analizando.

Aparte de los sistemas de detección basados en el análisis de registros, otro esquema que podemos - y debemos - implantar en cada máquina son los honeypots o tarros de miel, mecanismo que nos ha de permitir detectar ciertos ataques aunque el resto de sensores de nuestro modelo falle. Como antes hemos comentado, hay diferentes modelos de tarros de miel, desde los simples detectores de pruebas hasta los complejos sistemas dedicados por completo a esta tarea; para detectar ataques rutinarios estos últimos suelen ser algo excesivo e incluso en ocasiones difícil no sólo desde un punto de vista estrictamente técnico, ya que no siempre podemos dedicar una máquina entera a `engañar' atacantes, aparte del tiempo necesario para configurarla correctamente, actualizarla, revisar sus registros, etc. Esquemas teóricamente más simples pueden incluso resultar más efectivos en la práctica.

En nuestro caso no vamos a complicarnos mucho la existencia; si recordamos a nuestro atacante, estaba lanzando un escaneo de vulnerabilidades contra nuestro servidor web; nuestro IDS basado en red detectará el ataque y en consecuencia dará la voz de alarma y, adicionalmente, ejecutará una respuesta automática contra el pirata, bloqueándolo en el cortafuegos corporativo. Ese atacante ya puede más que sospechar que tenemos un detector de intrusos basado en red que le impide completar su escaneo, ya que en el momento que se detecta tráfico sospechoso bloquea por completo a la dirección origen; por tanto, un paso lógico para él es intentar un análisis de vulnerabilidades `camuflado', bien mediante una simple codificación de peticiones bien mediante un complejo stealth scan. De nuevo, buscará la existencia de CGIs vulnerables, como /cgi-bin/phf o /cgi-bin/printenv, y en este caso SNORT será incapaz de detectar el ataque. Pero forzosamente a nuestro servidor web han de llegar estas peticiones, por lo que >qué mejor forma de detectar al pirata que en la propia máquina? No tenemos más que situar en el directorio donde se ubican nuestros CGIs un programa que nos informe si alguien intenta acceder a él, tan simple como el siguiente shellscript:
anita:~# cat /web/cgi-bin/phf
#!/bin/sh
/bin/echo "Pirata desde "$REMOTE_ADDR""|mailx -s "Ataque PHF" root 
/bin/echo "Content-type: text/html"
/bin/echo ""
/bin/echo "<HTML><CENTER>Sonrie a la camara oculta...</CENTER></HTML>"
anita:~#
Evidentemente la `decepción' del sistema anterior en un atacante durará unos pocos segundos, ya que es inmediato para éste detectar que se trata de una simple trampa; si queremos algo más elaborado, tampoco es difícil conseguirlo: podemos simular el comportamiento de diferentes CGIs con vulnerabilidades conocidas de una forma muy sencilla. Por ejemplo, existen diferentes `imitaciones' de CGIs vulnerables que aparentan un problema de seguridad pero que en realidad se trata de sistemas de decepción más o menos eficaces; en http://www.eng.auburn.edu/users/rayh/software/phf.html podemos encontrar una versión muy interesante del CGI phf, capaz de simular los ataques más habituales contra el programa original de una forma bastante creíble - aunque no perfecta -. Además en este caso el código en Perl es fácilmente modificable, con lo que podemos desde adecuarlo a nuestros sistemas hasta mejorar su simulación; si lo hacemos, es muy posible que mantengamos `entretenidos' incluso a piratas con conocimientos por encima de la media de atacantes (esto lo digo por experiencia). También podemos construirnos nuestros propios CGIs que aparenten ser vulnerables, en muchos casos simplemente con unos conocimientos básicos de programación en shell o en Perl; por ejemplo, el código siguiente simula el CGI printenv, proporcionando información falsa sobre el sistema que parece útil para un atacante, y avisando por correo electrónico a los responsables de seguridad del sistema cuando alguien accede al programa:
anita:/# cat /web/cgi-bin/printenv
#!/usr/bin/perl

$SECUREADDRESS="root";
$mailprog = '/usr/lib/sendmail';

print "Content-type: text/html\n\n";
print "SERVER_SOFTWARE = Apache/1.3.12<br>";
print "GATEWAY_INTERFACE = CGI/1.2<br>";
print "DOCUMENT_ROOT = /usr/local/httpd/htdocs<br>";
print "REMOTE_ADDR = $ENV{'REMOTE_ADDR'}<br>";
print "SERVER_PROTOCOL = $ENV{'SERVER_PROTOCOL'}<br>";
print "SERVER_SIGNATURE = <br><i>Apache/1.3.9 Server at \
       $ENV{'SERVER_NAME'}</i><br><br>";
print "REQUEST_METHOD = GET<br>";
print "QUERY_STRING = $ENV{'QUERY_STRING'}<br>";
print "HTTP_USER_AGENT = $ENV{'HTTP_USER_AGENT'}<br>";
print "PATH = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:\
       /usr/local/bin<br>";
print "HTTP_ACCEPT = image/gif, image/x-xbitmap, image/jpeg<br>";
print "HTTP_CONNECTION = Keep-Alive<br>";
print "REMOTE_PORT = $ENV{'REMOTE_PORT'}<br>";
print "SERVER_ADDR = $ENV{'SERVER_ADDR'}<br>";
print "HTTP_ACCEPT_LANGUAGE = en<br>";
print "SCRIPT_NAME = /cgi-bin/printenv<br>";
print "HTTP_ACCEPT_ENCODING = gzip<br>";
print "SCRIPT_FILENAME = /usr/local/httpd/cgi-bin/printenv<br>";
print "SERVER_NAME = $ENV{'SERVER_NAME'}<br>";
print "REQUEST_URI = /cgi-bin/printenv<br>";
print "HTTP_ACCEPT_CHARSET = iso-8859-1, utf-8<br>";
print "SERVER_PORT = $ENV{'SERVER_PORT'}<br>";
print "HTTP_HOST = $ENV{'HTTP_HOST'}<br>";
print "SERVER_ADMIN = webmaster<br>";

# Enviamos correo
open (MAIL, "|$mailprog $SECUREADDRESS") or die "Can't open $mailprog!\n";
print MAIL "To: $SECUREADDRESS\n";
print MAIL "From: PRINTENV Watcher <$SECUREADDRESS>\n";
print MAIL "Subject: [/CGI-BIN/PRINTENV] $ENV{'REMOTE_HOST'} $action\n\n";
print MAIL "\n";
print MAIL  "--------------------------------------------------------\n";
print MAIL "Remote host: $ENV{'REMOTE_ADDR'}\n";
print MAIL "Server: $ENV{'SERVER_NAME'}\n";
print MAIL "Remote IP address: $ENV{'REMOTE_ADDR'}\n";
print MAIL "HTTP Referer: $ENV{'HTTP_REFERER'}\n";
print MAIL "Query String: $ENV{'QUERY_STRING'}\n";
print MAIL "\n--------------------------------------------------------\n";
close(MAIL);
exit;
anita:/#
© 2002 Antonio Villalón Huerta