
Lab Docker locale per dimostrare CVE-2026-34234 in CtrlPanel.
Questo repository confronta:
vuln: CtrlPanel 1.1.1 bloccata tramite digestpatched: CtrlPanel 1.2.0 bloccata tramite digestIl lab è esclusivamente locale e vincola i servizi a 127.0.0.1.
CVE-2026-34234 è una RCE non autenticata nell'installer web di CtrlPanel.
Il problema è causato da due bug concatenati:
install.lock.In questo lab, il container vulnerabile esegue un comando di prova innocuo e scrive il suo output all'interno del container. Il container corretto riceve la stessa richiesta ma non crea il file di prova.
Risultato atteso:
vulnerable => proof file created
patched => no proof file
1.1.1File vulnerabile originale:
public/installer/src/functions/shell.php
Codice upstream pertinente in 1.1.1:
function run_console(string $command, ...) {
$path = dirname(__DIR__, 4);
$handle = proc_open("cd '$path' && bash -c 'exec -a ServerCPP $command'", ...);
}
Problema:
run_console() accetta una singola stringa di comando shell.bash -c.File vulnerabile originale:
public/installer/src/forms/pterodactyl.php
Comportamento upstream pertinente in 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'", ...);
Problema:
url, key e clientkey provengono dai dati POST dell'installer.L'advisory afferma che public/installer/index.php controllava install.lock solo dopo aver caricato/eseguito la logica dei moduli dell'installer. Ciò rendeva i gestori dell'installer raggiungibili anche su istanze già installate.
La correzione sposta il controllo di install.lock prima del caricamento dei gestori dei moduli.
Comportamento corretto:
if (file_exists('../../install.lock')) {
exit("The installation has been completed already. Please delete the File 'install.lock' to re-run");
}
File corretto originale:
public/installer/src/functions/shell.php
Codice upstream pertinente in 1.2.0:
function run_console(array $command, ...): string {
$cwd = $cwd ?? $path;
$handle = proc_open($command, $descriptors, $pipes, $cwd, null, $options);
}
Perché questo risolve il problema:
run_console() ora accetta un array in stile argv.$() rimane input letterale invece di sintassi shell.Il comportamento del modulo corretto in 1.2.0 usa l'esecuzione di comandi in stile array:
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
Servizi:
vuln: CtrlPanel reale 1.1.1patched: CtrlPanel reale 1.2.0fake-api: API Pterodactyl finta locale usata solo per soddisfare i controlli dell'installermysql_vuln / mysql_patched: istanze MariaDB separateredis_vuln / redis_patched: istanze Redis separateIl lab non modifica il codice sorgente dell'applicazione CtrlPanel.
I Dockerfile si limitano a incapsulare l'entrypoint originale del container per normalizzare i permessi di runtime di Docker Desktop per:
/var/www/html/storage
/var/www/html/bootstrap/cache
Dopo aver corretto i permessi, il wrapper esegue l'entrypoint originale del prodotto.
PoC primario:
poc/poc_http_only.py
Proprietà:
docker execid, whoami, hostnameScript di supporto:
poc/poc_lab.py
Scopo:
docker compose execFile di prova all'interno del container dell'app:
/var/www/html/storage/logs/cve_2026_34234_proof.txt
Parti da uno stato pulito del lab:
docker compose down -v --remove-orphans
docker compose up -d --build
Attendi che i container dell'app siano attivi, poi esegui:
python3 poc/poc_lab.py
Output atteso:
== 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
Invia il PoC solo HTTP al target vulnerabile:
python3 poc/poc_http_only.py --target http://127.0.0.1:8081
Verifica manualmente la prova:
docker compose exec vuln sh -lc 'cat /var/www/html/storage/logs/cve_2026_34234_proof.txt'
Prova attesa:
uid=1000(laravel) gid=1000(laravel) groups=1000(laravel)
laravel
<container-hostname>
Esegui la stessa richiesta contro il container corretto:
python3 poc/poc_http_only.py --target http://127.0.0.1:8082
Verifica il comportamento corretto:
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"'
Atteso:
no proof file
Rimuovi container, reti e volumi del lab:
docker compose down -v
Questo repository è fornito esclusivamente per ricerca di sicurezza educativa e validazione difensiva.
Tutte le dimostrazioni sono pensate per essere eseguite all'interno dell'ambiente lab Docker locale fornito. La proof-of-concept evita azioni distruttive, persistenza, furto di credenziali, esfiltrazione di dati e l'uso contro sistemi reali.
Non usare questo progetto contro alcun sistema senza autorizzazione esplicita. L'autore non è responsabile per usi impropri o danni derivanti da questo materiale.