jueves, 4 de agosto de 2011

There is no place like 127.0.1.50: Una historia de error

Hace tiempo que no escribo en el blog... pido disculpas a mis lectores, pero estuve inmerso en una maraña de sucesos que han hecho interesante mi vida más allá de todas mis expectativas... evitando que pudiera usar la computadora para otra cosa que no fuera trabajar (sin contar pequeñas pausas para no perder contacto vía FB...).

--

Muchas veces sucede que cuando estamos trabajando en algo y empiezan a aparecer problemas, tendemos a tratar de resolverlos lo más rápidamente posible para poder seguir adelante. Ya antes he hablado del "síndrome del sysadmin", y no me enorgullezco de decir que a veces aflora gracias a la eventual simultaneidad de ciertos problemas informáticos que parecen ser causados por diversos factores como el viento solar, la diferencia horaria generada por el tsunami asiático, la caída de las bolsas en Wall Street o quién sabe por qué otras esotéricas razones exactamente... Así es que uno a veces busca respuestas o soluciones de la forma más complicada, talvéz también debido a la estructura mental que caracteriza a los idiotas que hacemos de la administración de sistemas nuestro modo de vida (no se sientan agredidos, colegas... sé que no todos son idiotas como yo).

Hace un par de días me llamó un cliente para decirme que tenía problemas con su correo electrónico. Siendo que este cliente es una persona inteligente, versada y acostumbrada a las cuestiones tecnológicas e informáticas, cuando me dijo que tenía dificultades con el servicio de envío, asumí que NO se trataba de un usual y mero problema de capa 8 (situación también conocida como "PEBKAC", "PIBCAK", "PICNIC", o según la jerga militar yanquee "One Delta Ten Tango problem"). Referencias y alusiones ilustrativas pueden buscarse en Google.

Pedí detalles sobre el problema y el cliente me dijo que no podía enviar mensajes desde ningún equipo, y que el servidor tardaba mucho en responder, en el mejor de los casos. Las descargas de mensajes y el webmail funcionaban bien, así que el servicio que fallaba parecía no afectar a los otros servicios en el sistema.

Entré al servidor y comencé el diagnóstico standard. Pude ver que había varias conexiones abiertas desde una IP externa a la red, enviando mensajes a través de una conexión debídamente autenticada (esto es, usando SMTP-AUTH y con credenciales de acceso válidas). Como sabía que no había nadie de viaje en la empresa del cliente, y como esta dirección de origen correspondía a algún lugar en la China, parecía evidente que este "usuario" conectado no era trigo limpio. Examinando los mensajes encolados en el servidor y que este usuario estaba enviando, veo que se trata de SPAM, así que desactivé la cuenta y limpié la cola de mensajes para evitar más daños (más tarde tendría que hacer los trámites de salida de las blacklists correspondientes y también tendría que implementar políticas más restrictivas al respecto de las credenciales de acceso al servicio). Habiéndome asegurado de que el spammer no podía volver a conectarse desde esa red, ni siquiera con nuevas credenciales de acceso, me puse a verificar otros parámetros del servicio, dado que aún no funcionaba como debía.

Así fué que ví que había una serie de conexiones abiertas desde y hacia la dirección IP 127.0.1.50...

En este punto estoy seguro de que muchos lectores experimentados en las artes del manejo de redes y servicios estarán sospechando cual es el problema en realidad... pero dejaré igual que sigan leyendo, esperando que de esta historia salga una moraleja útil para alguien.

Como la concurrencia de conexiones al servicio SMTP está limitada (como forma de controlar los recursos del servidor), una vez que se llegaba a la cantidad máxima de conexiones permitidas, el servicio simplemente dejaba de responder, hasta que las conexiones se resolvieran (se cerraran).

Mi primer impresión fué que algo o alguien había logrado ejecutar un claro spoofing de la dirección IP 127.0.1.50, inyectando paquetes desde la red e iniciando conexiones en el servicio SMTP. Lo primero que hice fué ejecutar una captura de tráfico en el enlace externo para verificar el verdadero origen de los paquetes. Me contrarió no encontrar nada en el tráfico externo, así que capturé paquetes en las otras interfaces para ver si el "ataque" provenía de la red lan. Tampoco vi nada. Razonando la situación, me dí cuenta de que es simplemente imposible que existan conexiones establecidas desde una dirección perteneciente a la red 127.0.0.0/8 "spoofed" sobre una interfaz externa, porque un paquete SYN con ese origen debería ser contestado entregando el SYN/ACK a través de la ruta local del sistema hacia la dirección origen de la conexión, con lo cual el destino de dichos paquetes debería ser invariablemente la interfaz loopback... evitando que las conexiones pudieran establecerse en primer momento. Si bien se puede instruir al kernel para que entregue respuestas a través de la misma interfaz que las recibe, bypasseando las rutas de la tabla de encaminamiento de paquetes, yo estaba seguro de que este NO era el caso. Ojalá hubiera seguido razonando sobre esa misma línea, en vez de echarme a perseguir fantasmas durante el resto del "evento", hubiera ahorrado tiempo y recursos valiosos. En cambio, asumí que el ataque tenía que provenir desde el "interior" del servidor entonces, mediante alguna técnica posíblemente menos sutil.

Todos sabemos que esa dirección forma parte del bloque de direcciones 127.0.0.0/8, el cual está asociado a la interfaz loopback (ver RFC3300), lo cual me hizo sospechar que se trataba de algún tipo de ataque de ejecución remota, el cual había logrado utilizar la dirección de la loopback como origen. Supuse que de alguna manera habían podido subir un script y lo habían ejecutado para hacer spam (que es el propósito más común de este tipo de ataques hoy día). Antes de cortar las conexiones, exploré el sistema en busca del script, pero para mi sorpresa, no encontré nada.

Revisé logs de servicios, sobre todo el del servicio web (Apache) y si bien ví intentos de acceso a software que no tengo ni uso (como el phpBB, el phpMyAdmin, etc.), no encontré nada que demostrara que alguien había subido un programa o script y lo había ejecutado. Verifiqué los ejecutables básicos del sistema mediante checksums y comparación con los de otros sistemas "sanos", revisé los módulos cargados en el kernel (en busca de rootkits de tipo LKM), listé los archivos abiertos en el sistema, las conexiones activas, examiné los subdirectorios de procesos en el directorio /proc en busca de ejecutables eliminados, procesos con nombres cambiados o indicios de invocaciones extrañas, revisé el filesystem para ver si había archivos con nombres extraños o en directorios donde no debería haberlos, el /tmp, el /dev, directorios o archivos que comienzan con "." o que se llaman "...*", etc... y seguí sin encontrar nada...

Descargué y ejecuté rkhunter y chkrootkit, y aunque ya había hecho manualmente los chequeos que estos dos softwares hacen, decidí intentarlo a ver si se me había escapado algo inadvertidamente. Nuévamente, NADA.

En este momento empecé a preocuparme seriamente... ya que si alguien había tomado posesión de ese servidor y estaba ejecutando algo, su método de ocultación era, para todo propósito, simplemente perfecto.

Opté por cerrar las conexiones y hacerle un seguimiento intenso con el fin de descubrir algo que me llevara a encontrar al atacante. Talvéz algún cracker había subido, ejecutado y eliminado toda traza de algún software en un movimiento único... lo cual era improbable, pero "posible". Tal atacante se merecería más que respeto de mi parte, así que decidí no dejar nada al azar y vigilar la actividad del servidor muy de cerca, más de lo que el tiempo del que dispongo para dicha tarea me lo permite.

Una hora después, el problema volvió a aparecer. Otra vez intenté dar con el causante sin poder hacerlo, lo cual me inquietó más aún. Esta vez terminé las conexiones y restauré el servicio en forma inmediata, dado que lo que fuera que pasaba estaba sucediendo tán rápido que evitaba que yo pudiera tomar acciones preventivas a tiempo. Evalué incorporar un bloqueo mediante iptables con el propósito de evitar nuevos intentos de acceso desde/hacia esa dirección, pero asumí que estaba frente a un atacante lo suficientemente habil como para darse cuenta del mismo y naturalmente que su paso siguiente sería probar otra dirección IP. Evalué también poner reglas sobre todo el bloque 127.0.0.1/8, exceptuando el 127.0.0.1 y el 127.0.0.2, que son las únicas dos direcciones necesarias (bueno, la primera al menos) para el correcto funcionamiento del servidor, pero eso daría por tierra con mi intento de encontrar al causante del problema, así que dejé todo como estaba, en espera de poder seguir mi búsqueda. Otros asuntos me llamaron al deber y tuve que dejar de monitorear este servicio para atenderlos, así que a pesar de mi renuencia, me separé de la terminal y fuí a seguir mi rutina diaria.

Horas después, verifiqué el estado del sistema y estaba normal, así que decidí poner manos a la obra y escribir un script que chequeara el sistema de forma constante (con una ventana de un segundo) y que sacara una "radiografía" del servidor en ese preciso instante, en caso de detectar actividad de esa dirección IP, con el propósito de tratar de encontrar el vector de ataque en caso de que la situación volviera a suceder. Esa sería una herramienta invaluable para descubrir al habil atacante. Terminé el script, lo probé y lo dejé en ejecución, esperando que las respuestas fuesen reveladas.

Seguí con otras cosas y en la noche recibí una notificación del script indicando que otra vez estaba sucediendo el "ataque". Con los archivos generados por el script, esperaba obtener una solución definitiva... y finalmente la obtuve, aunque no era lo que yo esperaba.



En los servidores de correo que instalo, utilizo una versión modificada de QMail. Entre las modificaciones y herramientas accesorias que utilizo, tengo un script que escribí y que hace de "proxy" entre los clientes externos y el software que dá el servicio. Dicho script chequea ciertos parámetros de las conexiónes antes de permitir el establecimiento de las mismas directamente con el sistema interno que gestiona el servicio SMTP. Entre los chequeos que hace, se encuentra la verificación del reverso DNS de la IP del cliente, la comparación de la IP y del reverso contra una blacklist interna, la pasada de la conexión por un proceso de "greylisting" (con el propósito también de asegurar que la misma se hace desde un servidor de correo legítimo, el cual volverá a reintentar la conexión más tarde en caso de haber sido rechazado la primera vez), el registro de la conexión y de la sesión, con puertos de origen y destino, con fines de monitoreo y diagnóstico, etc. Este script "escucha" en el puerto 25 del enlace externo (en caso de que haya más de un enlace de acceso al servidor) y decide si la conexión se establece o no dependiendo de los parámetros de la misma. Se supone que no puede haber en Internet un servidor de correo electrónico legítimo ejecutándose sobre una dirección IP dinámica, o sobre una dirección IP que no tiene reverso de DNS, o cuyo reverso DNS indique claramente que se trata de una dirección IP que no está asignada a ningún recurso en particular.

Como muchas veces sucede, algunos servidores de correo legítimos están activos sobre direcciones IP que no tienen reversos de DNS, ya sea porque los ISP a los que pertenecen no permiten o no usan dichos reversos, o porque los administradores de sistemas de dichos servidores ni siquiera saben que semejante cosa existe o se usa, o simplemente porque esos administradores no tienen ganas de asignarle un reverso de DNS a sus IP (hasta hace pocos años, los servidores del dominio "hsbc.com.ar", la sucursal argentina del conocido banco inglés, NO tenían reversos de DNS, por ejemplo).

Como mi script no permite conexiones desde direcciones IP sin reverso DNS, a veces rechazábamos correo emitido desde servidores legítimos pero mal administrados, con las consecuencias obvias para los clientes. Para evitar rechazar mensajes de correo electrónico de servidores legítimos pero cuyas direcciones IP no tienen reversos de DNS, se me ocurrió implementar un chequeo denominado "callback", el cual tiene como propósito verificar que el servidor que se conecta con nuestro servidor y que no tiene reverso de DNS, sí tiene un servicio SMTP activo, lo cual disminuye el riesgo de que se trate de una fuente de spam (en el mundo real, este chequeo no es tán efectivo, dado que cualquier spammer que vaya a usar una dirección IP sin reverso puede crear un servicio SMTP ilegítimo con la capacidad de burlar el método de callback, pero es preferible aceptar diez mensajes de spam que rechazar un mensaje legítimo).

El metodo de callback implica que el servidor que aún espera permitir la conexión desde la dirección IP sin reverso, genere una nueva conexión hacia el puerto 25 de dicha IP y ejecute ciertos comandos SMTP destinados a verificar la fuente. Una vez que la fuente es verificada (o sea que los comandos recibidos por la misma se ejecutan y generan una respuesta acorde con el protocolo), la conexión se permite. El caso contrario, la conexión se cierra y el script bloquea la dirección origen dejando una entrada en el registro del servicio. En este caso, el programa que hace el callback es un simple script en perl que escribí hace unos años para probar conexiones, modificado para agregar el envío de comandos SMTP y procesar la salida. ¿Porqué es importante que mencione esto? Bueno, resulta ser que el "ataque" en realidad se podría decir que fué "autoinflingido"...

En alguna parte del planeta, un malévolo sysadmin creyó que era una buena idea asignar la dirección IP 127.0.1.50 como registro MX (Mail Exchanger) de un dominio real en Internet. O bien es "malévolo" o "estúpido", o talvéz las dos... No está claro. Talvéz sea política standard de la empresa para la que trabaja este "sysadmin" hacer ese tipo de cosas, pero sincéramente no se me ocurre una buena razón para hacer esto (bueno, pensándolo bien, puede ser un retorcido mecanismo antispam...).

¿Qué pasó entonces en realidad? Simplícimo: nuestro servidor (de nuestro cliente en realidad) recibió un spam (enviado por nuestro "amigo chino") con dirección de remitente en un dominio que tenía como registro MX la dirección 127.0.1.50 (hubiera dado lo mismo que tuviera cualquier dirección dentro del rango 127.0.0.0/8), y al intentar enviar el mensaje correspondiente, nuestro servidor inició la conexión SMTP al puerto 25 de esa dirección IP, lo cual hizo que se conectara a sí mismo (dado que todo el bloque 127.0.0.0/8 "contesta" como si las 16.777.214 direcciones que forman parte del bloque estuvieran activas), lo que a su vez disparó el script que chequea las conexiones, el cual al ver que la dirección IP 127.0.1.50 no tenía reverso de DNS, inició el script de callback (para chequear que el origen de la conexión era un servidor de correo legítimo), que inició a su vez otra conexión hacia la dirección origen de la conexión inicial, que al haber sido generada desde la dirección IP 127.0.1.50, volvió a iniciar otra conexión al puerto 25 de la dirección 127.0.1.50, la cual fué atendida nuevamente por el script de chequeo de conexiones, el cual a ver que la dirección no tenía reverso DNS, inició el script de callback, el cual inició otra conexión a la IP de origen, la cual inició el script de chequeo de IP, el cual inició el proceso de callback... y así sucesivamente, entrando en un bucle infinito, el cual se enlenteció cuando se establecieron todas las conexiones posibles como valor máximo permitido. A medida que las conexiones expiraban (por timeout), iban saliendo de la lista de establecidas, permitiendo nuevas conexiones, lamentablemente idénticas.

Esto sucede tán rápido en realidad, que uno no se dá cuenta hasta que todas las conexiones están ocupadas. Es una avalancha de conexiones que parecen simultáneas, y solo se pueden detener matando todas las instancias de los procesos implicados al mismo tiempo.

El descubrimiento de esta situación fué agridulce... De alguna forma sentí haber tenido una epifanía, y al mismo tiempo sentí una terrible vergüenza al darme cuenta de lo torpe que fuí al no ver lo que en otras circunstancias me hubiera resultado obvio. De hecho, sí lo vi al hacer el análisis inicial del que les hablé arriba al respecto de las conexiones desde y hacia direcciones asignadas a la loopback, pero de alguna forma no llegué a hacer la asociación hasta mucho después.

Me metí en una cacería de fantasmas, enceguecido por las ánsias de atrapar a un atacante súmamente hábil y cauteloso, sin darme cuenta de que básicamente estaba persiguiendo mi propio trasero, tal y como lo hacen los perros.

Finalmente, luego de terminar mi análisis, modifiqué el programa de chequeo de conexiones para que esto no vuelva a suceder. Se podría decir entonces que algo bueno surgió de todo esto. Fué lo que se dice "una experiencia educativa", aunque en el estado en el que estoy estos días, lo que menos necesito es otra de estas experiencias. Lo que necesito realmente son vacaciones... y seguramente mi desempeño durante este evento fué un claro indicativo de ello.

:wq

 
Gustavo Castro

Crea tu insignia