the_open_door
café
ESEN
← Volver al índice

RCE via git hooks

Un git hook es un script que git ejecuta automáticamente alrededor de ciertas acciones (commit, push, merge, checkout...). Viven en .git/hooks/ dentro de cada repositorio y son locales a ese repo: no se versionan ni viajan con un clone, por eso se pasan por alto. Si el script tiene permiso de ejecución, git lo lanza; si no, lo ignora.

Los más útiles para escalar son los que corren antes de completar la acción:

El patrón de escalada. El hook se ejecuta con los privilegios del usuario que lanza la operación git, no del que escribió el hook. La combinación peligrosa aparece cuando:

  1. Un proceso privilegiado (un cron de root, un servicio) ejecuta periódicamente un git commit, git pull o similar sobre un repositorio.
  2. Nosotros podemos escribir en el directorio .git/hooks/ de ese repositorio (porque somos dueños del working tree, p. ej. un repo en nuestro $HOME).

Entonces basta con plantar el hook adecuado para ejecutar código como ese proceso privilegiado:

echo '#!/bin/bash' > /ruta/al/repo/.git/hooks/pre-commit
echo 'chmod u+s /bin/bash' >> /ruta/al/repo/.git/hooks/pre-commit
chmod +x /ruta/al/repo/.git/hooks/pre-commit

En el siguiente ciclo del proceso, el git commit de root dispara el pre-commit y /bin/bash queda con el bit SUID. Recuperar root es inmediato conservando el EUID:

bash -p     # -p no descarta el EUID 0 del binario SUID
id          # euid=0(root)

En lugar del SUID se puede usar cualquier payload: una reverse shell, copiar una clave en /root/.ssh/authorized_keys, añadir un usuario, etc. El SUID sobre bash es el más limpio y fiable.

Cómo detectarlo. La pista suele ser un proceso periódico. A falta de pspy sirve un bucle que difumine la salida de ps:

old=$(ps -eo command); while true; do new=$(ps -eo command); diff <(echo "$old") <(echo "$new"); old=$new; done

Ver un git commit/git-sync recurrente lanzado por root sobre un repo escribible es la señal. Encaja con el patrón general de cron sobre recurso escribible: lo abusable no es el cron en sí, sino que toca un recurso bajo nuestro control.

Defensa. No ejecutar operaciones git privilegiadas sobre repositorios cuyo working tree pertenezca a un usuario sin privilegios; usar core.hooksPath apuntando a un directorio controlado y de solo lectura; o git -c core.hooksPath=/dev/null para desactivar hooks en automatizaciones.

Herramientaspspy

Máquinas donde se aplica