FlawOpen

flawopen.com/Teardowns/cve-2024-32002-git-submodule-rce

● CVE-2024-32002 · CVSS 9.0 · Crítica
Investigación · FlawOpen

CVE-2024-32002: RCE mediante Enlaces Simbólicos en Submódulos Git

CVE-2024-32002 permitía que un repositorio Git preparado escribiera dentro de .git mediante un symlink con colisión de mayúsculas y minúsculas durante un clone recursivo, lo que podía ejecutar un hook antes de la inspección. La corrección upstream de Git comprueba el destino del submódulo antes y después del clone y aborta ante contenido inesperado; actualice a una versión corregida.

💡 Explicación en Lenguaje Sencillo (ELI5)

Imagina un almacén donde los letreros tratan A y a como la misma puerta. Si la ruta de un submódulo pasa por un letrero que redirige a la sala de registros (.git), los archivos destinados a una zona pueden acabar en los registros internos de Git. La corrección comprueba el destino antes y después del clone y se detiene si encuentra contenido inesperado.

Conceptos Clave y Términos

Submódulo Git
Un repositorio Git independiente anidado como subdirectorio dentro de un repositorio principal.
Enlace Simbólico
Un elemento del sistema de archivos que redirige las rutas hacia otro archivo o carpeta.
Colisión de Mayúsculas/Minúsculas
Comportamiento en NTFS y APFS donde rutas que varían solo en mayúsculas apuntan al mismo destino.
Ganchos Git (Git Hooks)
Scripts ejecutables en .git/hooks/ que Git lanza automáticamente tras clonar.

Mapeos de marcos

Análisis de Causa Raíz

En un sistema de archivos que no distingue entre mayúsculas y minúsculas y admite symlinks, un repositorio preparado podía asignar a un submódulo una ruta que colisionaba por diferencia de caja con un symlink. Durante el clone recursivo, Git podía sustituir por ese symlink un directorio creado para el clone y escribir archivos fuera del worktree del submódulo, dentro de .git. Un hook escrito allí podía ejecutarse antes de que el usuario inspeccionara el checkout. El parche upstream rechaza clonar en un destino de submódulo preexistente y no vacío y, después del clone, rechaza los destinos cuyo contenido no se limite a los metadatos .git.

Flujo de Ataque Paso a Paso

Paso 1

El clone recursivo alcanza metadatos controlados por el repositorio

Una persona clona recursivamente un repositorio no confiable en un sistema de archivos que no distingue mayúsculas y minúsculas y donde Git admite symlinks. El repositorio define rutas de submódulos y entradas versionadas que colisionan al plegar las mayúsculas.

Paso 2

La colisión de rutas cruza un límite de confianza

Git puede resolver la ruta colisionante del submódulo a través de un symlink, en vez de dentro del worktree previsto. El límite afectado es el manejo de rutas del sistema de archivos durante el clone recursivo de submódulos.

Paso 3

Los archivos llegan a los metadatos de Git

La escritura redirigida puede colocar un hook en el área .git del repositorio. El aviso del proyecto Git confirma que allí se puede escribir un hook y ejecutarlo mientras el clone sigue en curso.

Paso 4

El clone puede ejecutar código antes de la revisión

Git ejecuta el hook como parte del clone, antes de que el usuario pueda inspeccionar los archivos. Las versiones corregidas comprueban el estado del destino y abortan si encuentran archivos inesperados.

Código Fuente: Vulnerable vs. Seguro

Este es un fragmento del cambio upstream en C dentro de clone_submodule(); no es una función independiente. Antes del arreglo, la ruta de clone mostrada no tenía una comprobación equivalente del contenido del destino. La corrección rechaza un directorio de checkout preexistente que no esté vacío y, tras el clone, verifica que solo haya metadatos .git; la función auxiliar comprueba las entradas del directorio. El commit upstream contiene la implementación completa.

IMPLEMENTACIÓN VULNERABLE
if (!file_exists(sm_gitdir)) {
    if (safe_create_leading_directories_const(sm_gitdir) < 0)
        die(_("could not create directory '%s'"), sm_gitdir);

    if (run_command(&cp))
        die(_("clone of '%s' into submodule path '%s' failed"),
            clone_data->url, clone_data_path);
}
PARCHE SEGURO Y ROBUSTO
struct stat st;

if (!file_exists(sm_gitdir)) {
    if (clone_data->require_init && !stat(clone_data_path, &st) &&
        !is_empty_dir(clone_data_path))
        die(_("directory not empty: '%s'"), clone_data_path);

    if (safe_create_leading_directories_const(sm_gitdir) < 0)
        die(_("could not create directory '%s'"), sm_gitdir);

    if (run_command(&cp))
        die(_("clone of '%s' into submodule path '%s' failed"),
            clone_data->url, clone_data_path);

    if (clone_data->require_init && !stat(clone_data_path, &st) &&
        !dir_contains_only_dotgit(clone_data_path)) {
        char *dot_git = xstrfmt("%s/.git", clone_data_path);
        unlink(dot_git);
        free(dot_git);
        die(_("directory not empty: '%s'"), clone_data_path);
    }
}

Lista de Verificación de Seguridad para Ingeniería

Fuentes