
Preuve de concept d'exploitation pour CVE-2025-49132 permettant une exécution de code à distance non authentifiée dans Pterodactyl Panel <= 1.11.10 via l'injection du paramètre locale. Inclut les modes de vérification et d'exploitation.
Preuve de concept pour CVE-2025-49132 pour l'exécution de code à distance dans Pterodactyl Panel <= 1.11.10
Ce PoC s'appuie sur les références suivantes
# Check if the web application is vulnerable
python3 poc.py -u <url> -m check
# Do something funny
python3 poc.py -u <url> -m exploit -p <path_to_pear_dir> -t <path_to_write_to>
En analysant le git diff de LocaleController.php entre v1.11.10 et v1.11.11 (lien), on constate que le contrôleur accepte les paramètres locale et namespace sans aucune validation ni assainissement.
Pour comprendre pourquoi c'est dangereux, examinons le routage configuré dans panel/routes/base.php. La route supprime explicitement les middlewares liés à l'authentification, permettant aux utilisateurs non authentifiés d'appeler /locales/locale.json et de fournir les paramètres de requête requis tels que locale et namespace. Cela signifie que ces paramètres sont désormais contrôlés par l'utilisateur.
Route::get('/locales/locale.json', Base\LocaleController::class)
// Remove authentication middleware for this endpoint
->withoutMiddleware(['auth', RequireTwoFactorAuthentication::class]);
Revenons à LocalController.php, la ligne de code suivante fournit la magie RCE.
$this->loader->load($locale, str_replace('.', '/', $namespace))
La fonction load provient du framework de traduction de Laravel (ici) :
public function load($locale, $group, $namespace = null) # 1
{
if ($group === '*' && $namespace === '*') { # 2
return $this->loadJsonPaths($locale);
}
if (is_null($namespace) || $namespace === '*') { # 3
return $this->loadPaths($this->paths, $locale, $group); # 4
}
return $this->loadNamespaced($locale, $group, $namespace);
}
load prend 3 arguments. Bien que déroutant au premier abord, la valeur str_replace('.', '/', $namespace) est en réalité passée au second argument $group.$namespace ne sera jamais * car il sera toujours NULL. De même, $group ne sera jamais * puisque nous contrôlons ce qui est passé ici.$namespace est null, la branche sera prise, ce qui entraîne l'exécution de loadPaths au #4.À la lumière de ce qui précède, passons à l'analyse de loadPaths, là où la vraie magie opère.
protected function loadPaths(array $paths, $locale, $group)
{
return (new Collection($paths))
->reduce(function ($output, $path) use ($locale, $group) {
if ($this->files->exists($full = "{$path}/{$locale}/{$group}.php")) { # 1
$output = array_replace_recursive($output, $this->files->getRequire($full)); # 2
}
return $output;
}, []);
}
.php.getRequire(), qui exécute le fichier PHP et retourne son tableau.Puisque nous contrôlons le paramètre d'URL namespace, nous pouvons influencer le fichier que l'application tente de charger. En fournissant une valeur qui correspond à un script .php, nous pouvons amener le serveur à exécuter ce script. Par exemple, pour charger phpinfo.php, nous pouvons définir le paramètre sur : namespace=phpinfo
Le PoC tente d'effectuer une écriture de fichier arbitraire via pearcmd.php. Cette méthode a été utilisée dans de nombreux PoC similaires et travaux de recherche en sécurité.
Notez que pearcmd.php peut se trouver dans différents répertoires, par exemple :
/usr/share/php/pearcmd.php/usr/share/php/PEAR/pearcmd.php/usr/lib/php/pearcmd.phpet bien d'autres.
Le fichier écrit contiendra le code PHP malveillant typique.
Pour tests autorisés uniquement. N'utilisez pas ceci sur des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation explicite de test.
Vous êtes responsable du respect de toutes les lois applicables ; les auteurs déclinent toute responsabilité en cas d'utilisation abusive.
La bibliothèque Python requests est un peu trop serviable : elle encode les caractères dans l'URL fournie. J'ai passé beaucoup de temps à essayer de résoudre ce problème et j'ai fini par utiliser directement curl. Je serais reconnaissant si quelqu'un pouvait m'aider sur ce problème.