CVE-2025-49132 的概念验证,针对 Pterodactyl Panel <= 1.11.10 的远程代码执行。
此 PoC 参考了以下来源:
# 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>
分析 v1.11.10 和 v1.11.11 之间 LocaleController.php 的 git diff(链接),可以看到控制器接受 locale 和 namespace 参数,没有任何验证或清理。
为了理解这为什么危险,我们将查看 panel/routes/base.php 中配置的路由。该路由明确移除了与身份验证相关的中间件,允许未经身份验证的用户调用 /locales/locale.json 并提供所需的查询参数,例如 locale 和 namespace。这意味着这些参数现在由用户控制。
Route::get('/locales/locale.json', Base\LocaleController::class)
// Remove authentication middleware for this endpoint
->withoutMiddleware(['auth', RequireTwoFactorAuthentication::class]);
回到 LocaleController.php,以下代码行提供了 RCE 魔法。
$this->loader->load($locale, str_replace('.', '/', $namespace))
load 函数来自 Laravel 的翻译框架(此处):
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 函数接受 3 个参数。虽然起初令人困惑,但 str_replace('.', '/', $namespace) 的值实际上被传递给第二个参数 $group。$namespace 永远不会是 *,因为它始终为 NULL。同样,$group 也永远不会是 *,因为我们可以控制传入的值。$namespace 为 null,将执行该分支,从而导致在 #4 处执行 loadPaths。理解上述内容后,我们开始分析 loadPaths,真正的魔法在此发生。
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() 加载该文件,该函数会执行 PHP 文件并返回其数组。由于我们控制着 namespace URL 参数,我们可以影响应用尝试加载的文件。通过提供一个映射到 .php 脚本的值,我们可以让服务器执行该脚本。例如,要加载 phpinfo.php,我们可以将参数设置为:namespace=phpinfo
该 PoC 尝试通过 pearcmd.php 执行任意文件写入。此方法已在许多类似的 PoC 和安全研究中使用过。
请注意,pearcmd.php 可能位于不同的目录中,例如:
/usr/share/php/pearcmd.php/usr/share/php/PEAR/pearcmd.php/usr/lib/php/pearcmd.php以及更多。
被写入的文件将包含典型的恶意 PHP 代码。
仅用于授权测试。请勿在您不拥有或未获得明确测试许可的系统上使用。
您有责任遵守所有适用法律;作者对滥用不承担任何责任。
Python 的 requests 库有点过于“热心”了,因为它会对提供的 URL 中的字符进行编码。花了很长时间试图解决这个问题,最终只能直接使用 curl。如果有人能对此问题提供一些帮助,将不胜感激。