Introducción a la Vulnerabilidad
En versiones del kernel de Linux anteriores a la 4.19.8, se identificó una falla en la función hso_probe() del controlador USB para dispositivos móviles de alta velocidad. Esta función lee el valor if_num desde un dispositivo USB como un entero de 8 bits sin realizar una verificación de límites en el array correspondiente. Esto puede ocasionar una lectura fuera de los límites de la memoria en las funciones hso_probe() o hso_get_config_data(). Un atacante con acceso físico al sistema y un dispositivo USB malicioso podría provocar un bloqueo del sistema y una danegación de servicio.
Análisis del Parche Correctivo
El parche que corrige esta vulnerabilidad se encuentra en el commit del kernel con ID 5146f95df782b0ac61abde36567e718692725c89. A continuación se muestra un extracto de los cambios realizados en el archivo drivers/net/usb/hso.c:
diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c
index 184c24b..d6916f7 100644
--- a/drivers/net/usb/hso.c
+++ b/drivers/net/usb/hso.c
@@ -2807,6 +2807,12 @@ static int hso_get_config_data(struct usb_interface *interface)
return -EIO;
}
+ /* Verificar si la interfaz es válida */
+ if (if_num > 16) {
+ kfree(config_data);
+ return -EINVAL;
+ }
+
switch (config_data[if_num]) {
case 0x0:
result = 0;
@@ -2877,10 +2883,18 @@ static int hso_probe(struct usb_interface *interface,
/* Obtener la especificación de interfaz/puerto desde driver_info o del dispositivo */
- if (id->driver_info)
+ if (id->driver_info) {
+ /* if_num es controlado por el dispositivo, driver_info es un array terminado en 0.
+ * Asegurar que el acceso esté dentro de los límites */
+ for (i = 0; i <= if_num; ++i)
+ if (((u32 *)(id->driver_info))[i] == 0)
+ goto exit;
port_spec = ((u32 *)(id->driver_info))[if_num];
- else
+ } else {
port_spec = hso_get_config_data(interface);
+ if (port_spec < 0)
+ goto exit;
+ }
/* Verificar si se necesita cambiar a interfaces alternativas antes de la configuración del puerto */
El parche agrega una validación de límites para if_num en ambas funciones. En hso_get_config_data(), se comprueba que if_num no exceda 16 antes de usarlo como índice. En hso_probe(), se implementa un bucle para recorrer el array driver_info y evitar accesos fuera de rango.
Análisis del Código Fuente
El archivo drivers/net/usb/hso.c implementa el controlador para dispositivos móviles de alta velocidad vía USB. Está compilado con la opción CONFIG_USB_HSO y contiene funciones de inicialización y limpieza del módulo.
module_init(hso_init);
module_exit(hso_exit);
MODULE_AUTHOR(MOD_AUTHOR);
MODULE_DESCRIPTION(MOD_DESCRIPTION);
MODULE_LICENSE(MOD_LICENSE);
La función de inicialización hso_init registra controladores USB y TTY, mientras que hso_exit los desregistra:
static void __exit hso_exit(void)
{
printk(KERN_INFO "hso: unloaded\n");
tty_unregister_driver(tty_drv);
usb_deregister(&hso_driver);
}
El controlador actúa como un puente entre la capa USB y TTY, proporcionando operaciones de serie como apertura, escritura y control de flujo. Estas operaciones se definen en una estructura tty_operations:
static const struct tty_operations hso_serial_ops = {
.open = hso_serial_open,
.close = hso_serial_close,
.write = hso_serial_write,
.write_room = hso_serial_write_room,
.ioctl = hso_serial_ioctl,
.set_termios = hso_serial_set_termios,
.chars_in_buffer = hso_serial_chars_in_buffer,
.tiocmget = hso_serial_tiocmget,
.tiocmset = hso_serial_tiocmset,
.get_icount = hso_get_count,
.unthrottle = hso_unthrottle
};
La función hso_probe es el punto de entrada cuando se conecta un dispositivo USB. En ella, se lee el valor if_num desde el descriptor de la interfaz USB:
if_num = interface->altsetting->desc.bInterfaceNumber;
Este valor proviene del dispositivo y puede ser manipulado por un atacante. Sin validación, se usa directamente como índice en ((u32 *)(id->driver_info))[if_num] o en config_data[if_num], lo que lleva a la vulnerabilidad. En hso_get_config_data(), se emplea un buffer local config_data de 17 elementos, y el acceso sin comprobación de límites permite lecturas fuera de rango.