Bug Remoto: Demostración Ilustrada de Uno de los Beneficios de un Microkernel vs Kernel Monolítico
Día a día, la mayoría de nosotros los mortales usamos sistemas operativos con núcleos monolíticos, es decir, la mayoría de las operaciones de interacción directa con el hardware radican en modo privilegiado, es decir en modo kernel, a diferencia del modelo de micronúcleo, donde su nombre lo dice, el núcleo es muy pequeño. En este esquema, solamente las operaciones más básicas trabajan en modo kernel, mientras las demás, incluyendo la pila TCP/IP, residen en modo usuario:
Tomada de: http://en.wikipedia.org/wiki/Monolithic_kernel
Ahora bien, ¿cuál es mejor?, pues esta interminable discusión, como la interminable de VIM vs Emacs, ya ha sido abordada y discutida por muchos investigadores y expertos en ciencias de la computación, entre ellos, Linus Torvalds y Andrew S. Tanenbaum [1] [2]. Sin embargo, entre los varios pros y contras, el motivo de este artículo es para sencillamente demostrar uno de los beneficios de un microkernel ante un fallo de seguridad en su pila de red, la cual como he mencionado antes, trabaja en modo usuario y por lo tanto, un fallo grave en esta, no dejaría al sistema operativo inservible por completo (kernel panic) a diferencia de los otros con núcleo monolítico (Linux, Free/Net/OpenBSD, AIX, HP-UX, Solaris). Fallos famosos en núcleos monolíticos van desde el infame Ping de la Muerte que afectaba a sistemas Windows, hasta encabezados IP malformados que tiran al sistema operativo más seguro del Mundo (OpenBSD).
Para dicha demostración, es importante recalcar y observar a nivel conceptual, que la pila de red trabaja en modo no privilegiado, y regularmente, es posible interactuar con ella a través de IPC (comunicación entre procesos). Como se observa en la siguiente figura, si esta falla, las demás aplicaciones y servicios del sistema operativo residentes en modo usuario continuarán con su operación normal sin afectar a otras partes del núcleo:
Tomada de: http://www.eecs.harvard.edu/~mdw/course/cs161/notes/osstructure.pdf
Recientemente he estado ‘jugando’ con MINIX 3, creado por Andrew S. Tanenbaum, el cual está basado en un micronúcleo y es el sistema operativo por excelencia para fines educativos, aunque últimamente el mismo Andrew ha apostado por el mercado de dispositivos embebidos [3]. Hace un par de días estaba leyendo partes del código de fuente, y viendo una cabecera en específico, minix/include/net/gen/tcp_hdr.h, me di cuenta de algunas macros definidas con el nombre TCP_OPT_* referentes a opciones TCP [4], las cuales no son muy comunes en comunicaciones normales y por lo tanto, algunos programas fallan al procesar dichos bytes extra, como en este caso. Para ello, decidí fuzzear dichos bytes al final de la cabecera TCP para finalmente descubrir un bug en la pila TCP/IP de MINIX 3 que a continuación describo.
[box]Paréntesis importante: hoy en día existen muchísimos “expertos en seguridad” que jamás han creado/analizado paquetes TCP/IP artesanalmente, es decir, campo por campo; byte a byte; calculando checksums; y la mayoría de los que lo ha hecho es con la ayuda de herramientas como hping o nemesis, lo cual no es malo, al contrario, para qué rehacer la rueda si estos programas nos facilitan la vida, sin embargo, considero que es sumamente importante tener conocimientos del penúltimo nivel de abstracción (diría que el último es a nivel bits y hardware) para mayor entendimiento de protocolos más arriba, y menos abstractos, de la capa OSI. Para esto, recomiendo muchísimo leer tutoriales de creación de raw sockets como el bien conocido “Beej's Guide to Network Programming - Using Internet Sockets” [5] o el tutorial de Mixter [6], reconocido hacker creador de famosas herramientas como "Tribe Flood Network" y "Six/Four System" de cDc (Cult of the Dead Cow).[/box]
Volviendo al tema. El fallo descubierto fue en el código en minix/net/inet/generic/tcp_lib.c que analiza las opciones TCP, el cual entra en un loop infinito al enviar un cero en el segundo byte de los últimos cuatro después del encabezado TCP, el cual corresponde a la longitud de las opciones TCP, por ejemplo: [IP][TCP]["\xff\x00\x00\x00"]. Para que la pila TCP/IP no descartara paquetes sin checksum, o checksum erróneo, y considerara estos bytes extra, fue necesario decirle que la longitud del encabezado son 24 bytes, en vez de 20 como normalmente es, y además, calcular el checksum correcto [7] con estos cuatro bytes incluidos. Después de crear el código de concepto y poder reproducir dicho bug, decidí reportarlo a los programadores de MINIX, quienes en menos de 24 horas corrigieron el fallo (aplausos!): “MINIX 3.3.0 Remote TCP/IP Stack Denial of Service (malformed TCP options)”.
Mucha boca y poco arte… Vamos directo a la prueba de concepto utilizando el siguiente código para explotar este fallo: http://www.exploit-db.com/exploits/35302/. Las partes más importantes, además del payload (“\xff\x00\x00\x00”), son TCP->th_off y los checksums calculados con la función _checksum(), ya que si estos valores son incorrectos, al llegar a la pila TCP/IP estos paquetes son descartados por el sistema operativo automáticamente sin continuar con su análisis:
Desde un Linux es muy sencillo compilar y ejecutar dicho código:
En otra ventana es posible observar claramente que los pings ya no son respondidos justo después de que el paquete malformado ha sido recibido y procesado por MINIX 3:
Finalmente podemos constatar en la consola de MINIX 3 que a pesar de que la pila de red quedó en un ciclo infinito sin poder procesar ninguna petición de red más, es posible seguir realizando otras tareas y usar otras aplicaciones, excepto todas las que tengan qué interactuar con la red (como ifconfig):
Y bueno, “esa”, es una de las GRANDES ventajas de un microkernel.
Así como MINIX, hay muchos sistemas operativos allá afuera que están realizando operaciones críticas, recibiendo y enviando paquetes TCP, y los cuales posiblemente no han llegado a las manos de hackers que descubran sus vulnerabilidades para remediarlas y por ende hacer de ellos mejores sistemas operativos. También, qué decir de los muchos sistemas operativos en sistemas embebidos y de los más caros y avanzados RTOS (Real Time Operating System) que no están a disposición de cualquiera con 20 dólares en la bolsa, como por ejemplo, VxWorks, que está ahora en robots y naves espaciales a miles de kilómetros del Punto Azul Pálido. Con su kernel monolítico, ¿quién puede asegurarnos que ha sido probado ampliamente para minimizar al máximo el número de posibles fallas? ¿y si no? ¿y si alguien (autorizado) desde la Tierra manda paquetes malformados a un robot con este SO al espacio?.
Para concluir, si ustedes tienen acceso a PLCs (comúnmente usados en plantas industriales e infraestructura crítica [ICS / SCADA]) u otros sistemas operativos en dispositivos embebidos o RTOS, no sería mala idea fuzzearlos en un entorno controlado y autorizado para descubrir vulnerabilidades y remediarlas antes de que los malos lo hagan. Pueden hacerlo utilizando el código de concepto mostrado arriba, haciendo combinaciones de los últimos cuatro bytes de forma aleatoria quizás. Si encuentran algo, no duden en compartírnoslo por aquí.
Gracias por su lectura.
Artículo escrito por Alejandro Hernández {@nitr0usmx} BrainOverflow.org para La Comunidad DragonJAR
Referencias:
[1] LINUX is obsolete
https://groups.google.com/forum/#!topic/comp.os.minix/wlhw16QWltI[1-25-false]
[2] Tanenbaum-Torvalds Debate: Part II
http://www.cs.vu.nl/~ast/reliable-os/
[3] MINIX 3 at the Embedded World Exhibition in Nuremberg
https://www.youtube.com/watch?feature=player_detailpage&v=vlOsy0PZZyc#t=38
[4] TCP Option Kind Numbers
http://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml#tcp-parameters-1
[5] Beej's Guide to Network Programming - Using Internet Sockets
http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html
[6] A brief programming tutorial in C for raw sockets
http://gonullyourself.org/library/A%20brief%20programming%20tutorial%20in%20C%20for%20raw%20sockets.txt
[7] TCP Checksum Calculation and the TCP "Pseudo Header"
http://www.tcpipguide.com/free/t_TCPChecksumCalculationandtheTCPPseudoHeader-2.htm