flawopen.com/Teardowns/cve-2024-32002-git-submodule-rce
CVE-2024-32002: Удаленное выполнение кода через симлинки Git Submodule (RCE)
CVE-2024-32002 позволяла подготовленному Git-репозиторию во время рекурсивного клонирования записывать в .git через символическую ссылку, конфликтующую по регистру, и запускать hook до проверки файлов. В upstream-исправлении Git проверяет каталог субмодуля до и после клонирования и прерывает операцию при неожидимом содержимом; обновитесь до исправленной версии Git.
Представьте склад, где таблички считают A и a одной дверью. Если путь субмодуля проходит через табличку, ведущую в архив (.git), файлы для одного сектора могут попасть во внутренние данные Git. Исправление Git проверяет целевой каталог до и после клонирования и останавливается при неожиданном содержимом.
Ключевые понятия и термины
Подмодуль Git (Submodule)- Отдельный репозиторий Git, встроенный как подкаталог в родительский проект.
Символическая ссылка (Symlink)- Объект файловой системы, перенаправляющий путь на другой целевой файл или директорию.
Коллизия регистра (Case Collision)- Поведение NTFS и APFS, при котором пути с разным регистром символов ведут к одному объекту.
Git-хуки (Hooks)- Исполняемые скрипты в
.git/hooks/, автоматически запускаемые при выполненииpost-checkout.
Связи со стандартами
Анализ первопричины
В файловой системе без учета регистра, поддерживающей символические ссылки, подготовленный репозиторий мог задать путь субмодуля, который после свертки регистра конфликтовал с symlink. При рекурсивном клонировании Git мог заменить созданный для клона каталог этой ссылкой и записать файлы за пределами worktree субмодуля, в .git. Размещенный там hook мог выполниться до того, как пользователь проверит checkout. Upstream-патч запрещает клонирование в уже существующий непустой каталог субмодуля и после клонирования отклоняет каталог, если он содержит что-либо помимо метаданных .git.
Пошаговый сценарий атаки
Рекурсивное клонирование достигает метаданных под контролем репозитория
Пользователь рекурсивно клонирует недоверенный репозиторий в файловой системе без учета регистра, где Git поддерживает symlink. Репозиторий задает пути субмодулей и отслеживаемые записи, конфликтующие после свертки регистра.
Конфликт путей пересекает границу доверия
Git может разрешить конфликтующий путь субмодуля через symlink, а не внутри ожидаемого worktree. Уязвимая граница — обработка путей файловой системы при рекурсивном клонировании субмодулей.
Файлы попадают в метаданные Git
Перенаправленная запись может поместить hook в каталог .git репозитория. В уведомлении проекта Git подтверждено, что hook можно записать туда и выполнить, пока клонирование еще продолжается.
Клон может выполнить код до проверки
Git запускает hook в ходе клонирования, до того как пользователь сможет проверить файлы. Исправленные версии Git проверяют состояние целевого каталога и прерывают операцию при обнаружении неожиданных файлов.
Исходный код: Уязвимый vs Защищённый вариант
Это фрагмент upstream-изменения на C в clone_submodule(), а не самостоятельная функция. До исправления в показанном пути клонирования не было соответствующей проверки содержимого целевого каталога. Исправление отклоняет уже существующий непустой каталог checkout и после клонирования проверяет, что присутствуют только метаданные .git; вспомогательная функция проверяет записи каталога. Полная реализация приведена в upstream-коммите.
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);
}
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);
}
}
Чек-лист по защите системы для инженеров
- ✓Обновите Git до 2.45.1, 2.44.1, 2.43.4 или новее — в них есть защита от коллизий подмодулей.
- ✓Отключите автоматическое рекурсивное клонирование подмодулей для недоверенных репозиториев (git clone --no-recurse-submodules).
- ✓Введите проверки коллизий путей файлов в CI-раннерах и сборочных ботах.
- ✓Установите core.protectNTFS и core.protectHFS в true в кроссплатформенных средах разработки.
- ✓Проверяйте выгрузки репозиториев на подозрительные символьные ссылки, ведущие во внутренние каталоги .git.