А вы знали, что git clone может выполнить код на вашем сервере?
Не через хуки, не через запуск чужого make — а самой операцией клонирования. Я знал это в теории, а на практике столкнулся, когда мой пет-проект пару дней подрабатывал дорвеем.
Матчасть. Гит поддерживает несколько транспортных протоколов для взаимодействия с репозиториями: https://, ssh://, git://. По началу адреса гит понимает, какой протокол использовать. Среди них есть особый — ext::, который вместо «сходи по сети» означает «выполни вот эту команду и считай её вывод репозиторием». То есть ext::sh -c "..." — это уже не адрес, а инструкция по запуску произвольной программы. Также гит имеет целый набор конфиг-ключей (вроде core.fsmonitor, filter.clean, diff.textconv), которые заставляют git выполнить произвольную внешнюю команду. Именно поэтому обёртки над гитом такие вещи фильтруют — simple-git (js-либа для работы с гитом) банил опасные опции ещё с 2022 года. Проблема (CVE-2026-6951, CVSS 9.8, все версии до 3.36.0): блок-лист оказался дырявым. Заблокировали короткую форму аргумента -c, но не длинную --config; не учли установку конфига через переменные окружения и ещё пачку ключей, которые могут запускать исполнение команды. Если злоумышленник добирается до аргументов git-команды — можно включить исполнение чужого кода прямо во время клонирования. Классический code injection (CWE-94) через безобидное «скачать репозиторий». У меня стояла версия 3.28 — в самой середине уязвимого диапазона.
Как меня взломали. У меня был Next.js-сервис: принимал от пользователя ссылку на репозиторий, клонировал его и анализировал код внутри. И вот первая архитектурная ошибка, о которой легко забыть: файл app/api/git/route.ts в Next.js автоматически становится публичным HTTP-эндпоинтом. То есть POST на /api/git мог слать кто угодно из интернета, не только моя форма в браузере. Вторая ошибка — единственной проверкой ссылки была регулярка вида ^https?://.*\.git$, где .* пропускала почти что угодно. Бот перебором нашёл эндпоинт по IP и начал отправлять ему специально сформированные ссылки на «репозитории».
Как я обнаружил. Захожу на свой домен — редирект на сторонний сайт. Лезу разбираться: nginx чистый, никакого редиректа в конфигах; крон чистый, левых задач нет. Пересобрал контейнер — починилось. А через некоторое время редирект вернулся, иногда уже на другой адрес. Вот это и сбивало с толку двое суток: снаружи всё корректно, следов на диске и в логах ноль, а лечится только рестартом. Разгадка оказалась логичной: вредоносный процесс жил в памяти работающего процесса (потому и не оставлял следов в файлах и конфигах), рестарт его убивал вместе со старым процессом — но публичный скопроментированный эндпоинт никуда не исчез, и следующий заход бота заражал хост снова.
Решение. Обновил simple-git до 3.36.0 — но это только половина решения. Настоящая дыра была в логике приложения: открытый в интернет эндпоинт, принимающий сырой пользовательский URL по регулярке .* и отправляющий его в git-операцию. Даже с пропатченной либой запускать клонирование неизвестного ввода — плохая идея, так что вторым делом прикрутил нормальную валидацию источника.
Выводы. Первое: git clone/git pull — это не безобидное «скачать файлики»; если URL приходит извне, относитесь к нему как к пользовательскому вводу в eval, а не как к строке. Второе: route.ts в Next.js — это дверь, открытая в интернет по умолчанию, и про её публичность легко забыть. Третье, моё любимое: зелёный npm audit — не 100% гарантия. Свежие CVE попадают в базы не мгновенно, и пока обновление не раскатится, аудит их не показывает. Апдейт зависимостей по расписанию — всё ещё ваша обязанность, про которую не стоит забывать.
Комментариев пока нет.