
# Laboratoire Docker local démontrant la RCE non authentifiée CVE-2026-34234 dans l'installateur web CtrlPanel Comprend des conteneurs vulnérables et corrigés, des scripts PoC et une analyse de cause racine pour la recherche en sécurité et la validation défensive.
Lab Docker local pour démontrer la CVE-2026-34234 dans CtrlPanel.
Ce dépôt compare :
vuln : CtrlPanel 1.1.1 épinglé par digestpatched : CtrlPanel 1.2.0 épinglé par digestLe lab est exclusivement local et lie les services à 127.0.0.1.
La CVE-2026-34234 est une RCE non authentifiée dans l'installateur web de CtrlPanel.
Le problème est causé par deux bogues enchaînés :
install.lock.Dans ce lab, le conteneur vulnérable exécute une commande de preuve inoffensive et écrit sa sortie dans le conteneur. Le conteneur corrigé reçoit la même requête mais ne crée pas le fichier de preuve.
Résultat attendu :
vulnerable => proof file created
patched => no proof file
1.1.1Fichier vulnérable d'origine :
public/installer/src/functions/shell.php
Code amont pertinent dans 1.1.1 :
function run_console(string $command, ...) {
$path = dirname(__DIR__, 4);
$handle = proc_open("cd '$path' && bash -c 'exec -a ServerCPP $command'", ...);
}
Problème :
run_console() accepte une chaîne de commande shell.bash -c.Fichier vulnérable d'origine :
public/installer/src/forms/pterodactyl.php
Comportement amont pertinent dans 1.1.1 :
run_console("php artisan settings:set 'PterodactylSettings' 'panel_url' '$url'", ...);
run_console("php artisan settings:set 'PterodactylSettings' 'admin_token' '$key'", ...);
run_console("php artisan settings:set 'PterodactylSettings' 'user_token' '$clientkey'", ...);
Problème :
url, key et clientkey proviennent des données POST de l'installateur.L'avis de sécurité indique que public/installer/index.php ne vérifiait install.lock qu'après le chargement/exécution de la logique des formulaires de l'installateur. Cela rendait les gestionnaires de l'installateur accessibles même sur des instances déjà installées.
Le correctif déplace la vérification de install.lock avant le chargement des gestionnaires de formulaires.
Comportement corrigé :
if (file_exists('../../install.lock')) {
exit("The installation has been completed already. Please delete the File 'install.lock' to re-run");
}
Fichier corrigé d'origine :
public/installer/src/functions/shell.php
Code amont pertinent dans 1.2.0 :
function run_console(array $command, ...): string {
$cwd = $cwd ?? $path;
$handle = proc_open($command, $descriptors, $pipes, $cwd, null, $options);
}
Pourquoi cela corrige le problème :
run_console() accepte désormais un tableau de type argv.$() reste une entrée littérale au lieu d'une syntaxe shell.Le comportement corrigé des formulaires dans 1.2.0 utilise une exécution de commande de type tableau :
run_console(['php', 'artisan', 'settings:set', 'PterodactylSettings', 'panel_url', $url], ...);
run_console(['php', 'artisan', 'settings:set', 'PterodactylSettings', 'admin_token', $key], ...);
run_console(['php', 'artisan', 'settings:set', 'PterodactylSettings', 'user_token', $clientkey], ...);
127.0.0.1:8081 -> vulnerable CtrlPanel 1.1.1
127.0.0.1:8082 -> patched CtrlPanel 1.2.0
127.0.0.1:9100 -> fake Pterodactyl API
Services :
vuln : réel CtrlPanel 1.1.1patched : réel CtrlPanel 1.2.0fake-api : fausse API Pterodactyl locale utilisée uniquement pour satisfaire les vérifications de l'installateurmysql_vuln / mysql_patched : instances MariaDB séparéesredis_vuln / redis_patched : instances Redis séparéesLe lab ne modifie pas le code source de l'application CtrlPanel.
Les Dockerfiles ne font qu'envelopper le point d'entrée du conteneur d'origine pour normaliser les permissions d'exécution Docker Desktop pour :
/var/www/html/storage
/var/www/html/bootstrap/cache
Après la correction des permissions, le wrapper exécute le point d'entrée du produit d'origine.
PoC principal :
poc/poc_http_only.py
Propriétés :
docker execid, whoami, hostnameScript auxiliaire :
poc/poc_lab.py
Objectif :
docker compose execFichier de preuve dans le conteneur applicatif :
/var/www/html/storage/logs/cve_2026_34234_proof.txt
Démarrez à partir d'un état de lab propre :
docker compose down -v --remove-orphans
docker compose up -d --build
Attendez que les conteneurs applicatifs soient opérationnels, puis exécutez :
python3 poc/poc_lab.py
Sortie attendue :
== Testing vulnerable ==
proof_exists: True
result: PASS expected_proof=True
== Testing patched ==
proof_exists: False
result: PASS expected_proof=False
[+] Expected result reached:
vulnerable => proof file created
patched => no proof file
Envoyez le PoC HTTP uniquement à la cible vulnérable :
python3 poc/poc_http_only.py --target http://127.0.0.1:8081
Vérifiez la preuve manuellement :
docker compose exec vuln sh -lc 'cat /var/www/html/storage/logs/cve_2026_34234_proof.txt'
Preuve attendue :
uid=1000(laravel) gid=1000(laravel) groups=1000(laravel)
laravel
<container-hostname>
Exécutez la même requête contre la version corrigée :
python3 poc/poc_http_only.py --target http://127.0.0.1:8082
Vérifiez le comportement corrigé :
docker compose exec patched sh -lc 'test -f /var/www/html/storage/logs/cve_2026_34234_proof.txt && cat /var/www/html/storage/logs/cve_2026_34234_proof.txt || echo "no proof file"'
Attendu :
no proof file
Supprimez les conteneurs, les réseaux et les volumes du lab :
docker compose down -v
Ce dépôt est fourni uniquement pour la recherche en sécurité à des fins éducatives et la validation défensive.
Toutes les démonstrations sont destinées à s'exécuter dans l'environnement de lab Docker local fourni. La preuve de concept évite les actions destructrices, la persistance, le vol d'identifiants, l'exfiltration de données et le ciblage dans le monde réel.
N'utilisez pas ce projet contre un système sans autorisation explicite. L'auteur n'est pas responsable d'une mauvaise utilisation ou des dommages résultant de ce contenu.