the_open_door
café
ESEN
← Volver al índice

Symlink abuse in a sudo script

Un patrón frecuente de escalada en Linux: un usuario sin privilegios puede ejecutar por sudo (a menudo con NOPASSWD) un script propio del sistema que manipula ficheros como root. Si ese script sigue enlaces simbólicos sin resolver la ruta real, el usuario controla qué fichero acaba leyendo o tocando root.

El fallo típico. El script intenta protegerse comprobando el destino de un enlace y rechazando rutas sensibles:

LINK_TARGET=$(readlink "$LINK")           # solo el PRIMER salto
if echo "$LINK_TARGET" | grep -Eq '(etc|root)'; then
    unlink "$LINK"                        # lo borra
else
    cat "$QUAR_DIR/$LINK_NAME"            # lo lee como root
fi

El error está en usar readlink (un solo salto) en vez de readlink -f (resuelve la cadena completa). Solo se inspecciona el destino directo.

El bypass: doble enlace. Se encadenan dos symlinks. El primero apunta a un destino "inocente"; el segundo, al fichero sensible:

ln -s /root/root.txt  pwn.txt     # eslabón oculto (destino real)
ln -s pwn.txt         pwn.png     # lo que ve readlink: 'pwn.txt', pasa el filtro

readlink pwn.png devuelve pwn.txt (no contiene etc ni root, así que el filtro lo deja pasar). Pero cuando el script hace cat pwn.png, el kernel resuelve la cadena entera hasta /root/root.txt y root lee ese contenido por nosotros.

Variantes y matices. A veces el comportamiento del script depende de una variable de entorno (en LinkVortex, CHECK_CONTENT): si la regla de sudoers conserva esa variable con env_keep, basta exportarla (sudo CHECK_CONTENT=true ...) para activar la rama que imprime el contenido. El objetivo más rentable no es solo root.txt: leer /root/.ssh/id_rsa da una clave privada con la que entrar por SSH como root y obtener shell completa, no solo una flag.

Cómo detectarlo. sudo registra la invocación del script en /var/log/auth.log con su usuario y argumentos; la firma del abuso es el proceso privilegiado abriendo, a través de un enlace, un fichero fuera de su directorio de trabajo esperado (/root/.ssh/id_rsa, /etc/shadow): auditd sobre open/openat con ppid del script revela la ruta real resuelta por el kernel, distinta de la que el script creía validar. La creación de una cadena de symlinks en el directorio de quarantine/trabajo (ln -s encadenados) justo antes del sudo es el indicio previo. Un sudo <script> con una variable de entorno inesperada (CHECK_CONTENT=true) conservada por env_keep también delata la manipulación.

Defensa. En cualquier script privilegiado, resolver siempre la ruta canónica (realpath/readlink -f) antes de decidir, no seguir enlaces ajenos, y minimizar env_keep en /etc/sudoers. Relacionado con el abuso de SUID y, en general, con confiar en rutas que el usuario controla.

Máquinas donde se aplica