Hoy vamos a hablar sobre Venom,
una vulnerabilidad descubierta por Jason Geffner de CrowdStrike y que afecta a todos los sistemas de virtualización
basados en QEMU, el popular emulador de fuente abierta.
Lo primero que nos va a llamar la atención es
donde se ha encontrado el fallo: La
emulación del controlador de discos flexibles (si, discos de 3.5”, por
ejemplo). Curioso porque resulta ser un componente emulado que rara vez se
utiliza, salvo en sistemas virtualizados que usen ambientes heredados con
cierta edad, aunque no es necesario que dicho dispositivo real esté presente
para explotar el fallo, es más ni siquiera desactivando la emulación de la
unidad virtual evitaría la explotación.
QEMU soporta la emulación de esta unidad
basándose en el chipset de Intel 82078, documentado aquí.
La lectura del documento nos proporcionará dos cosas: cómo funciona
internamente el controlador de disco flexible y la justificación más exacta de
la frase: "La abstracción es
ignorancia selectiva".
Para efectuar las distintas operaciones con
la unidad de discos se emiten comandos (desde el sistema virtualizado o
huésped) del tipo 'FD_CMD_READ'
lectura o 'FD_CMD_WRITE' escritura.
Los comandos que llegan al controlador del cliente son almacenados en un búfer
junto con la información de sus parámetros. Este búfer del tipo FIFO posee un
tamaño estático y está definido como miembro de la estructura FDCtrl. En dicha
estructura los miembros fifo (uint8_t*) y fifo_size (int32_t) definen el puntero
a memoria donde comienza el búfer y el tamaño asignado (512 hasta donde hemos
visto).
Como todo buen array en C que se precie, el
mayor problema viene con el índice. El cálculo para obtener el acceso al lugar
adecuado dentro del array puede llegar a complicarse tanto, que la propia
lógica que compone el índice puede ofuscarse en el código. Y llegar a
comprometer, como es el caso, la seguridad del proceso si estos valores son
controlables desde el exterior (entiéndase, a través de valores procedentes del
exterior del proceso).
La vulnerabilidad se asienta sobre este hecho
en particular. Tenemos unos comandos que va a procesar el controlador emulado,
unos parámetros asignables desde fuera (cliente) y un búfer de tamaño fijo con
un índice cuyo valor es "influenciable"
con ciertas llamadas.
El controlador se encarga de limpiar y
resetear el búfer cuando los comandos son procesados, dejándolo preparado para
la siguiente tanda de comandos. Esto se efectúa correctamente salvo cuando se
procesan dos comandos:
"FD_CMD_READ_ID" y "FD_CMD_DRIVE_SPECIFICATION_COMMAND".
En los casos de estos comandos el búfer no se deja en un estado apropiado, lo que permite que las siguientes operaciones de escritura puedan escribir fuera de los límites del tamaño asociado al búfer con el completo control del atacante sobre lo que se está escribiendo (shellcode por supuesto).
"FD_CMD_READ_ID" y "FD_CMD_DRIVE_SPECIFICATION_COMMAND".
En los casos de estos comandos el búfer no se deja en un estado apropiado, lo que permite que las siguientes operaciones de escritura puedan escribir fuera de los límites del tamaño asociado al búfer con el completo control del atacante sobre lo que se está escribiendo (shellcode por supuesto).
El parche que solventa el
problema está publicado aquí
Como podemos observar la variable
'pos' usada para almacenar la
posición del bloque de datos dentro del búfer ha sido cambiada del tipo 'int' al tipo 'uint32_t'.
El tipo 'int'
en la especificación del lenguaje C es del tipo "signed" y como mínimo de 16 bits para arriba (habitualmente 32
bits), mientras que el tipo que le es asignado está definido como entero SIN
SIGNO de 32 bits. De hecho, la operación donde se establece 'pos':
pos = fdctrl->data_pos;
Se hace una conversión implícita
de uint32_t a int, con la consiguiente malinterpretación: a partir de un fdctrl->data_pos
> (UINT_MAX/2) + 1. 'pos' es
negativo siempre que int = uint32_t = 32 bits.
Además se ha cambiado la forma en
la que se usa e interpreta 'fdctrl->data_pos'
como índice.
- fdctrl->fifo[fdctrl->data_pos++] = value;
+ pos = fdctrl->data_pos++;
+ pos %= FD_SECTOR_LEN;
+ fdctrl->fifo[pos] = value;
Ya no se usa directamente el
miembro de la estructura para indexar el búfer fifo. Ahora vemos como ese valor
se pasa a 'pos' (ya correctamente
definido como 'uint32_t') y su valor
filtrado al módulo de FD_SECTOR_LEN, por lo que no podrá ser indexado más allá
de 512, coincide con el tamaño del búfer.
Esta vulnerabilidad, con
CVE-2015-3456, permitiría "virtualmente"
causar el acceso al proceso de
virtualización en la máquina real, permitiendo de manera efectiva un escape
desde el sistema huésped al anfitrión. Teniendo en cuenta que los sistemas
virtualizados corren bajo cuentas con privilegios (Administrador, root…), su explotación puede derivar en problemas
serios.
No es la primera vulnerabilidad
que permite salir del sistema virtual al anfitrión. Pero, tal y como reseñan en
la web dedicada, esta no precisa de una
configuración especial para que la virtualización sea vulnerable.
QEMU es usado por varias
soluciones de virtualización, el propio QEMU, Xen, Citrix, etc. El problema es
independiente de la plataforma huésped, la vulnerabilidad afectará a Windows,
Linux u OSX de igual manera. Ya existe parche
disponible para solventar el problema. Por cierto, esto afecta a infraestructuras en nube, las cuales se caracterizan por
el uso extensivo de virtualización y acceso relativamente abierto a sus
clientes.
Por otro lado, VENOM sufre de la
nueva corriente de presentación estelar de vulnerabilidades: Nombre pegadizo
que es un acrónimo forzado, icono y web
propia. ¡Ah!, aquellos archivos de texto con sus asciiarts…
Más información:
VENOM
VENOM, don’t get
bitten
82078 44 PIN
CHMOS Single-Chip Floppy Disk Controller
David García

No hay comentarios:
Publicar un comentario