
Дифференциальный proof-of-concept для CVE-2026-40176, демонстрирующий внедрение команд ОС в драйвер Perforce в Composer через вредоносный URL-адрес репозитория, с автоматическим A/B-тестированием против уязвимых и исправленных версий.
Автономное, объектно-ориентированное PHP-подтверждение концепции, которое демонстрирует и дифференциально проверяет уязвимость внедрения команд в драйвере репозитория Perforce для Composer.
PoC запускает один и тот же вредоносный composer.json с двумя бинарными файлами Composer — уязвимой версией (2.9.5) и исправленной версией (2.9.6) — и доказывает наличие ошибки, наблюдая за побочным эффектом (файл-маркер, созданный внедренной командой оболочки), который срабатывает на уязвимой версии, но не на исправленной.
⚠️ Только для авторизованных исследований безопасности и защитного тестирования. См. Ответственное использование.
| CVE | CVE-2026-40176 |
| Компонент | Composer — драйвер репозитория/VCS Perforce (perforce) |
| Класс | Внедрение команд ОС через URL репозитория, контролируемый атакующим |
| Поверхность атаки | composer.json, содержащий сконструированную запись repositories с type: perforce |
| Уязвимая версия | Composer 2.9.5 |
| Исправленная версия | Composer 2.9.6 |
| Триггер | Разрешение/обновление зависимостей (composer update) на основе вредоносного манифеста |
| Воздействие | Произвольное выполнение команд на машине, запускающей Composer |
| Язык PoC | PHP (один файл, без внешних зависимостей) |
Composer может разрешать пакеты из нескольких систем контроля версий. Для Perforce репозиторий идентифицируется URL-адресом p4://, который кодирует хост, порт и пользователя/поток. Когда драйвер Perforce в Composer формирует базовую командную строку p4, поля, взятые из контролируемого атакующим URL, недостаточно очищаются перед передачей оболочке.
Поскольку автор манифеста полностью контролирует URL репозитория, атакующий, который может заставить жертву выполнить composer update/composer install на основе вредоносного composer.json (например, отравленная зависимость, враждебный репозиторий или задание CI, обрабатывающее непроверенные файлы проекта), может выйти за пределы предполагаемого вызова p4 и выполнить произвольные команды ОС с привилегиями процесса Composer.
Это относится к тому же семейству, что и исторические проблемы внедрения аргументов в драйверах VCS Composer, где значения URL/ветки/потока попадают в команды оболочки без экранирования. Composer 2.9.6 ужесточает драйвер Perforce, так что внедренная нагрузка больше не выполняется.
Авторитетным описанием поведения, демонстрируемого здесь, является исходный код PoC (
CVE202640176Test.php); за подробностями исправления обращайтесь к официальному уведомлению и журналу изменений Composer.
PoC представляет собой один класс CVE202640176Test, который выполняет контролируемый A/B (дифференциальный) эксперимент:
--version у обоих бинарных файлов Composer (уязвимой 2.9.5 и исправленной 2.9.6) и прерывает выполнение, если один из них не может быть вызван.composer.json, раздел repositories которого содержит запись perforce с вредоносным URL p4://, несущим внедренную команду оболочки.composer update в этой директории.finally всегда восстанавливает исходный composer.json в директории проекта.PASS только тогда, когда запуск на уязвимой версии показывает побочный эффект, а на исправленной — нет.Для каждого запуска validateRun() проверяет три условия:
| Проверка | Что доказывает |
|---|---|
| Файл-маркер существует и содержит идентификатор запуска | Внедренная нагрузка touch/echo действительно выполнилась — то есть внедрение команд удалось. |
Вывод Composer упоминает p4 | Был достигнут путь кода драйвера Perforce (нагрузка обработана правильным компонентом, а не каким-то другим шагом). |
| Распознанная версия Composer соответствует ожидаемой | Запущен правильный бинарный файл (2.9.5 или 2.9.6). |
Запуск считается "OK" только при выполнении всех трех условий. Общий тест проходит, когда запуск на уязвимой версии OK, а на исправленной — нет — точный признак реальной уязвимости, которая впоследствии была исправлена.
Вредоносный URL репозитория создается в writeComposerJson():
p4://127.0.0.1:1666:attacker_user;touch <маркер> && echo '<id_запуска>' > <маркер>:client_test
Разберем по частям:
p4://127.0.0.1:1666:attacker_user — корректно выглядящий URL Perforce (хост, порт 1666, пользователь).;touch <маркер> && echo '<id_запуска>' > <маркер> — внедренные команды оболочки. Ведущий ; завершает предполагаемую команду p4; touch создает файл-маркер, а echo '<id_запуска>' > <маркер> записывает в него уникальный идентификатор запуска, чтобы PoC мог подтвердить, что нагрузка (а не какой-то другой процесс) создала этот файл.:client_test — завершающий текст для сохранения правдоподобности разбора URL.В уязвимом драйвере метасимволы оболочки обрабатываются, и файл-маркер создается. В исправленном драйвере значение правильно экранируется/заключается в кавычки, поэтому та же строка рассматривается как инертные данные, и маркер не появляется.
Примечание: PoC использует уникальный идентификатор запуска с меткой времени и записывает свой маркер внутри изолированной временной директории, поэтому нагрузка является безвредной и самоочищающейся, а не разрушительной.
2.9.5 (уязвимая версия)2.9.6 (исправленная версия)exec() запускает cd … && php …). Разработано для Linux/macOS.composer.json в директории проекта (он считывается при запуске, копируется в каждый временный запуск и восстанавливается после).Вам обычно не нужен работающий сервер Perforce: уязвимость заключается в том, как Composer формирует командную строку
p4, и внедренная нагрузка выполняется до/вместо любого реального подключенияp4. Composer может выдать ошибку подключения Perforce — это ожидаемо и не влияет на доказательство через файл-маркер.
Клонируйте / разместите PoC в рабочей директории.
Предоставьте composer.json в той же директории, что и PoC. Минимального достаточно:
{
"name": "research/cve-2026-40176-poc",
"description": "Базовый манифест для дифференциального PoC CVE-2026-40176",
"require": {}
}
Получите два бинарных файла Composer и разместите их там, где ожидает PoC (по умолчанию показаны):