Icono del sitio Una al Día

Vulnerabilidad en el kernel Linux permite elevación de privilegios

El pasado mes de abril, un mensaje en la lista del kernel Linux alertaba de un fallo de seguridad en la función ‘sw_perf_event_destroy‘ de kernel/events/core.c. Su autor, Tommi Rantala, comentaba que lo había encontrado ejecutando una versión de Trinity modificada por él mismo. Se trata de una herramienta para emplear técnicas de fuzzing en las llamadas al sistema que proporciona el kernel Linux.
El fallo se encuentra en el subsistema ‘perf‘ que permite obtener métricas de rendimiento del hardware. Al ejecutar un conjunto de llamadas con parámetros aleatorios, Rantala consiguió provocar un «kernel Oops» (una especie de excepción en el kernel). En la salida de registro de Trinity podían observarse las líneas que contenían la sospecha de un problema de seguridad:
[114607.069257] BUG: unable to handle kernel paging request at
0000000383c35328
[114607.070003] IP: [] sw_perf_event_destroy+0x30/0x90

Básicamente, antes de la llamada a ‘sw_perf_event_destroy‘ existe una llamada a ‘perf_swevent_init‘, donde se inicializa un entero con signo al valor de event->attr.config, procedente del puntero a la estructura *event:
static int perf_swevent_init(struct perf_event *event)
{
    int event_id = event->attr.config;
    if (event->attr.type != PERF_TYPE_SOFTWARE)
        return ENOENT;
Posteriormente, se hace una llamada a ‘sw_perf_event_destroy‘ y aquí ese valor entero con signo es tratado como un entero sin signo de 64 bits:
static void sw_perf_event_destroy(struct perf_event *event)
{
        u64 event_id = event->attr.config;
        WARN_ON(event->parent);
        static_key_slow_dec(&perf_swevent_enabled[event_id]);
        swevent_hlist_put(event);
}
Como en ‘perf_swevent_init‘ el entero es tratado con signo, solo es comprobado en su límite superior, dejando abierta la posibilidad de que se introduzca un entero negativo que posteriormente en ‘sw_perf_event_destroy‘, al ser «convertido» en u64, nos dará un entero positivo.
Debido a que el valor de event->attr.config es controlable por el usuario, cuando se efectua la llamada al sistema correspondiente mediante la estructura ‘perf_event_attr‘, es posible mapear una zona de memoria y obtener el control de la misma haciendo que se apunte a esa dirección. Si se llega a ejecutar nuestra zona de memoria con un shellcode instalado allí, este se ejecutaría con permisos de root.
Petr Matousek da una explicación completa y de recomendable estudio en la lista de bugzilla de Red Hat.
El parche fue añadido al repositorio del kernel el mismo mes. Básicamente consiste en promover la variable ‘event_id‘ al tipo ‘u64‘, evitando así la falta de una comprobación apropiada:
static int perf_swevent_init(struct perf_event *event)
 {
   int event_id = event->attr.config;
+   u64 event_id = event->attr.config;
    if (event->attr.type != PERF_TYPE_SOFTWARE)
        return ENOENT;
El fallo fue reportado en abril, y pasó un poco de puntillas por la lista de desarrolladores, sin levantar demasiado revuelo. Pero parece que no pasó tan desapercibido para todo el mundo. Un poco más tarde, en mayo, un tercero hizo público un exploit para aprovechar la vulnerabilidad. Este hecho es el que realmente ha sacado a la luz que el fallo, es en realidad un grave problema de seguridad.
El CVE asignado es el CVE-2013-2094. Afectaría a todos los kernels (de 64 bits) desde la versión 2.6.37 a la 3.8.8 que hayan sido compilados con la opción CONFIG_PERF_EVENTS.
Más información:
Trinity
Linux kernel oops
Performance counter subsystem
CVE-2013-2094 kernel: perf_swevent_enabled array out-of-bound access
Commit del parche en el repositorio del kernel Linux
Exploit
David García

Acerca de Hispasec

Hispasec Ha escrito 7037 publicaciones.

Salir de la versión móvil