
En resumen
Los hooks viven en .git, y un directorio .git que te entregan en un zip o carpeta compartida los trae consigo. Un hook post-checkout se ejecuta en el momento en que cambias de rama, antes de que leas el código.
Un hook es un programa que Git ejecuta en un punto fijo de su flujo, y los hooks viven dentro de .git. Por eso los clones normales son seguros y las carpetas compartidas no: entrega a alguien un directorio de trabajo con su carpeta .git intacta y le entregas todo lo necesario para ejecutar código en cuanto cambie de rama.
Este ataque ya tiene nombre en el mundo real. A un desarrollador le enviaron un archivo de proyecto plausible, le dijeron que cambiara a una «rama NDA» y encontró un hook post-checkout real que descargaba un binario específico del sistema desde un endpoint de mando y control, lo marcaba como ejecutable, lo ejecutaba y se borraba. Auditar hooks y config cuesta unos treinta segundos.
01Cómo un hook post-checkout se convierte en ejecución de código
post-checkout se ejecuta cuando termina un checkout. Git lo llama con tres argumentos —el HEAD anterior, el nuevo HEAD y un flag que indica si se hizo checkout de rama o de archivo— y el código de salida del hook no bloquea la operación, así que se ejecuta en silencio.
Como se dispara en la transición, el atacante no necesita que ejecutes un script. Basta con decirte git checkout nda. El hook tiene entonces tus privilegios completos de usuario: puede leer ~/.ssh, ~/.aws, el llavero de macOS, variables de entorno y cualquier helper de credenciales de la máquina.
Un checkout no es una acción de solo lectura en un repositorio no confiable
Si el directorio de trabajo contiene una carpeta
.gitque no creaste, trata ejecutar cualquier comandogitcomo ejecutar código de su autor. Eso incluyegit status, porque otras claves de config también ejecutan programas.
02Por qué aparecen los hooks si no se clonan
La vía normal de clonado es genuinamente segura: git clone <url> no copia los hooks remotos, y el post-checkout que Git pueda ejecutar tras clonar viene de tu directorio plantilla, no del repositorio. El ataque rodea eso de tres formas:
- Un árbol de trabajo entregado a mano. Un archivo comprimido, una carpeta de Dropbox o un directorio copiado que incluye
.git. Esto es lo que ocurrió en el incidente reportado. - Un directorio de hooks rastreado vía
core.hooksPath. Los frameworks que gestionan hooks así convierten los scripts en archivos confirmados, de modo que instalar dependencias los activa en silencio. - Un driver
filtermás.gitattributes. El repositorio confirma un.gitattributesque referencia un filtro cuyo comando vive en.git/config, y el checkout ejecuta ese comando.
Solo la primera no requiere instalar dependencias. También es la más fácil de tragar, porque la carpeta parece un proyecto normal.
03Audita un repositorio no confiable antes de ejecutar git
Empieza por el directorio de hooks y la config local. Todos los comandos siguientes se verificaron con Git 2.50.1.
# ¿Dónde guarda los hooks este repo?
git rev-parse --git-path hooks/post-checkout
# .git/hooks/post-checkout
# Lista cualquier cosa que no sea un archivo .sample.
ls -la "$(git rev-parse --git-path hooks)"
Un directorio de hooks que solo contiene archivos *.sample es lo normal. Un archivo real que no sea sample es un programa que se ejecutará en su punto de disparo; léelo entero antes de ejecutar Git.
# Cualquiera de estos indica hooks, filtros o helpers que ejecutan código.
git config --local --list
git config --get core.hooksPath # código de salida 1 significa no definido
git config --get core.fsmonitor
git config --get core.sshCommand
Presta atención a filter.*, core.fsmonitor, core.sshCommand, core.pager, core.editor y cualquier alias cuyo valor empiece por !. Cada uno puede ejecutar un comando como efecto secundario de una operación normal.
# Inspecciona un hook buscando el patrón típico: descargar, chmod, ejecutar, borrar.
grep -nE 'curl|wget|chmod|base64 -d|/tmp/|rm -f|exec' .git/hooks/post-checkout
Leer un hook es seguro; el peligro es que un comando de Git lo dispare. Si hay un hook y solo necesitas ver los archivos rastreados, ábrelos con un editor o con tar, no con Git.
04Ejecuta Git con seguridad en un checkout sospechoso
Cuando debas ejecutar Git en un repositorio que no confías, desactiva los hooks para esa invocación y mantén la sesión inerte:
# Verificado: una ruta de hooks vacía desactiva todos los hooks solo para este comando.
git -c core.hooksPath=$(mktemp -d) status
git -c core.hooksPath=$(mktemp -d) checkout nda
# Nunca pidas credenciales desde un repo no confiable.
GIT_TERMINAL_PROMPT=0 git -c core.hooksPath=$(mktemp -d) log --oneline -5
Para algo más que un vistazo, copia los archivos rastreados a un directorio nuevo e inicializa un repositorio nuevo en lugar de usar el .git que te dieron. Todo el ataque depende de esa carpeta, así que descartarla elimina la vía.
Clona desde una URL, no desde una carpeta
Si un colaborador comparte un proyecto, pídele una URL de Git o un bundle desnudo, no un directorio de trabajo con
.git.git cloneno ejecuta los hooks remotos; copiar una carpeta sí.
05Si un hook ya se ejecutó
Asume que las credenciales de la máquina están comprometidas. Desconecta la red y luego inventaría lo que era alcanzable durante el checkout: claves SSH y sus passphrases, sesiones de CLI en la nube, sesiones del navegador, el llavero de macOS y cualquier token en tu entorno de shell o archivos .env. Rota esas credenciales desde otra máquina y revisa los registros de auditoría de Git y de la nube en busca de actividad desde IPs o user agents desconocidos. Borrar el hook no deshace la ejecución.
06Errores comunes y soluciones
| Error | Por qué falla | Solución |
|---|---|---|
Ejecutar git status en una carpeta que te enviaron | Hooks y config pueden ejecutar código | Inspecciona .git primero o usa core.hooksPath |
Confiar en un repo porque no tiene post-checkout | Otros hooks y claves de config también se disparan | Lee todo el directorio de hooks y la config local |
| Asumir que los clones siempre son seguros | Solo lo son si no hay hooks en el árbol | Clona desde una URL; nunca adoptes un .git ajeno |
Revisar solo core.hooksPath | filter.* y core.fsmonitor también ejecutan | Comprueba cada clave capaz de ejecutar |
| Borrar el hook después | La carga ya se ejecutó | Rota credenciales y revisa auditoría |
El hábito que importa: una carpeta .git que no creaste es contenido ejecutable. Mírala por dentro antes de teclear git, y prefiere un clon limpio desde una URL para que no haya nada hostil que mirar.
Construye el patrón que encuentra curl, chmod y decodificación base64 dentro de un hook.
Probador de expresiones regularesPreguntas frecuentes
¿Los hooks de Git se clonan desde un remoto?
No. Un `git clone` normal copia archivos rastreados, no el `.git/hooks` del remoto. Clonar incluso un repositorio hostil desde una URL no ejecuta los hooks que el atacante confirmó, porque los hooks no forman parte del árbol rastreado.
Entonces, ¿cómo llega un hook post-checkout a mi máquina?
Alguien te entrega un directorio de trabajo que ya contiene una carpeta `.git` —en un zip, una carpeta compartida o un archivo—. Ese `.git` incluye sus hooks, así que ejecutar `git checkout` ahí dispara el script del atacante.
¿Con qué se ejecuta un hook post-checkout?
Git ejecuta `post-checkout` tras `git checkout`, tras el checkout inicial de `git clone` y tras `git switch`. Recibe el HEAD anterior y el nuevo, más un flag que indica si fue checkout de rama o de archivo.
¿Puedo ejecutar git en un repositorio no confiable sin que se disparen los hooks?
Sí. Apunta `core.hooksPath` a un directorio vacío para ese comando: `git -c core.hooksPath=$(mktemp -d) status`. Verificado en local, desactiva los hooks solo para esa invocación sin cambiar la config del repositorio.
¿Qué otras claves de config de Git pueden ejecutar código?
`core.fsmonitor`, `core.sshCommand`, `core.pager`, `core.editor`, `filter.<driver>.*` combinado con un `.gitattributes` confirmado, y los alias `!command` pueden ejecutar programas. Inspecciona siempre `.git/config` en un repositorio que no clonaste tú.
Artículos relacionados
- Cómo migrar un repositorio Git de SHA-1 a SHA-256
Una migración también es buen momento para auditar hooks y config local.
- Compara dos textos con un comparador de diferencias
Compara un .git/config sospechoso con uno limpio.
Herramientas relacionadas
- Probador de expresiones regulares
Encuentra descargas, chmod y decodificaciones en un hook.
- Codificador / decodificador Base64
Decodifica una carga ofuscada antes de ejecutarla.
- Comparador de diferencias
Detecta líneas inyectadas en config o scripts de hook.