
Auditor de funciones libc en tiempo de ejecución que detecta condiciones de carrera en el acceso a archivos y vulnerabilidades de enlaces simbólicos, interceptando llamadas al sistema de archivos mediante LD_PRELOAD y registrando posibles rutas de escalada de privilegios.
PathAuditor es una herramienta diseñada para encontrar vulnerabilidades relacionadas con el acceso a archivos mediante la auditoría de funciones de libc.
La idea es aproximadamente la siguiente:
Estamos usando LD_PRELOAD para interceptar todas las llamadas a la biblioteca relacionadas con el sistema de archivos y registrar cualquier violación encontrada en syslog.
Este no es un producto oficialmente respaldado por Google.
Veamos un ejemplo del tipo de vulnerabilidad que esta herramienta puede detectar. CVE-2019-3461 fue un error en tmpreaper, una herramienta que recorre /tmp y elimina archivos antiguos. Normalmente se ejecuta como un trabajo cron como root. Como no quiere eliminar archivos fuera de tmp, usaba el siguiente código para verificar si un directorio es un punto de montaje:
if (S_ISDIR (sb.st_mode)) {
char *dst;
if ((dst = malloc(strlen(ent->d_name) + 3)) == NULL)
message (LOG_FATAL, "malloc failed.\n");
strcpy(dst, ent->d_name);
strcat(dst, "/X");
rename(ent->d_name, dst);
if (errno == EXDEV) {
free(dst);
message (LOG_VERBOSE,
"File on different device skipped: `%s/%s'\n",
dirname, ent->d_name);
continue;
}
// [...]
En resumen, este código llama a rename("/tmp/foo", "/tmp/foo/x") que devolverá EXDEV si "/tmp/foo" es un punto de montaje.
PathAuditor marcaría esta llamada como una posible vulnerabilidad si "/tmp/foo" es propiedad de cualquier usuario excepto root.
Para entender por qué, debemos pensar en lo que sucede en el kernel cuando se ejecuta la syscall rename (simplificado):
"/tmp/foo" para el primer argumento."/tmp/foo/x" para el segundo argumento.Aquí hay una condición de carrera ya que "/tmp/foo" se resolverá dos veces. Si está controlado por el usuario, este puede reemplazarlo con un archivo diferente en cualquier momento.
En particular, queremos que "/tmp/foo" sea un directorio al principio para pasar la verificación if(S_ISDIR) en el código de tmpreaper. Luego lo reemplazamos con un archivo justo antes de que el código entre en la syscall. Cuando el kernel resuelve el primer argumento, verá un archivo con contenido controlado por el usuario.
Ahora lo reemplazamos de nuevo, esta vez con un enlace simbólico a un directorio arbitrario en el mismo sistema de archivos. El kernel resolverá la ruta por segunda vez, seguirá el enlace simbólico y moverá el archivo controlado a un directorio elegido.
La restricción del mismo sistema de archivos se debe a que rename no funciona entre sistemas de archivos. Pero en algunas distribuciones de Linux, /tmp es solo una carpeta en rootfs por defecto y se podría usar este error para mover un archivo a /etc/cron, que se ejecutará como root.
Para probarlo, necesitas compilar libpath_auditor.so con bazel y cargarlo en un binario usando LD_PRELOAD. Cualquier violación se registrará en syslog, así que asegúrate de tenerlo en ejecución.
bazel build //pathauditor/libc:libpath_auditor.so
LD_PRELOAD=/path/to/bazel-bin/pathauditor/libc/libpath_auditor.so cat /tmp/foo/bar
tail /var/log/syslog
También es posible ejecutar esto en todos los procesos del sistema agregándolo a /etc/ld.so.preload. Aunque se advierte que esto solo se recomienda en sistemas de prueba, ya que puede provocar inestabilidad.
Como inicio rápido, puedes probar el contenedor docker incluido con este proyecto:
docker build -t pathauditor-example .
docker run -it pathauditor-example
# LD_PRELOAD=/pathauditor/bazel-bin/pathauditor/libc/libpath_auditor.so cat /tmp/foo/bar
# cat /var/log/syslog