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: [<ffffffff811a7200>] 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 http://codemonkey.org.uk/projects/trinity/
Linux kernel oops http://en.wikipedia.org/wiki/Linux_kernel_oops
Performance counter subsystem https://perf.wiki.kernel.org/index.php/Main_Page
CVE-2013-2094 kernel: perf_swevent_enabled array out-of-bound access https://bugzilla.redhat.com/show_bug.cgi?id=962792#c16
Commit del parche en el repositorio del kernel Linux http://bit.ly/1833rsM
Exploit http://fucksheep.org/~sd/warez/semtex.c
http://www.laflecha.net/canales/seguridad/noticias/vulnerabilidad-en-el-kernel-linux-permite-elevacion-de-privilegios