用于演示 CtrlPanel 中 CVE-2026-34234 的本地 Docker 实验环境。
本仓库对比:
vuln:按摘要固定的 CtrlPanel 1.1.1patched:按摘要固定的 CtrlPanel 1.2.0该实验环境仅限本地使用,服务绑定到 127.0.0.1。
CVE-2026-34234 是 CtrlPanel Web 安装程序中的一个未认证 RCE 漏洞。
该问题由两个串联利用的缺陷引起:
install.lock 检查之前即可访问。在本实验环境中,存在漏洞的容器会执行一条无害的验证命令并将其输出写入容器内部。已修复的容器收到相同的请求,但不会创建验证文件。
预期结果:
vulnerable => proof file created
patched => no proof file
1.1.1 中存在漏洞的 shell 执行原始存在漏洞的文件:
public/installer/src/functions/shell.php
1.1.1 中相关的上游代码:
function run_console(string $command, ...) {
$path = dirname(__DIR__, 4);
$handle = proc_open("cd '$path' && bash -c 'exec -a ServerCPP $command'", ...);
}
问题:
run_console() 接受单个 shell 命令字符串。bash -c。原始存在漏洞的文件:
public/installer/src/forms/pterodactyl.php
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'", ...);
问题:
url、key 和 clientkey 来源于安装程序的 POST 数据。安全公告指出,public/installer/index.php 仅在加载/执行安装程序表单逻辑之后才检查 install.lock。这导致即使在已安装的实例上,安装程序处理器也可访问。
修复将 install.lock 检查移到表单处理器加载之前。
修复后的行为:
if (file_exists('../../install.lock')) {
exit("The installation has been completed already. Please delete the File 'install.lock' to re-run");
}
原始修复文件:
public/installer/src/functions/shell.php
1.2.0 中相关的上游代码:
function run_console(array $command, ...): string {
$cwd = $cwd ?? $path;
$handle = proc_open($command, $descriptors, $pipes, $cwd, null, $options);
}
为什么这能修复该问题:
run_console() 现在接受 argv 风格的数组。$() 之类的载荷语法会作为字面输入,而不是 shell 语法。1.2.0 中的修复表单行为使用数组风格的命令执行:
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
服务:
vuln:真实的 CtrlPanel 1.1.1patched:真实的 CtrlPanel 1.2.0fake-api:本地伪造的 Pterodactyl API,仅用于满足安装程序的检查mysql_vuln / mysql_patched:独立的 MariaDB 实例redis_vuln / redis_patched:独立的 Redis 实例本实验环境不会修改 CtrlPanel 应用程序源代码。
Dockerfile 仅包装原始容器入口点,以规范化以下目录的 Docker Desktop 运行时权限:
/var/www/html/storage
/var/www/html/bootstrap/cache
修复权限后,包装器会执行原始产品入口点。
主要 PoC:
poc/poc_http_only.py
特性:
docker execid、whoami、hostname辅助脚本:
poc/poc_lab.py
用途:
docker compose exec 验证容器内的证明文件应用容器内的证明文件:
/var/www/html/storage/logs/cve_2026_34234_proof.txt
从干净的实验环境状态开始:
docker compose down -v --remove-orphans
docker compose up -d --build
等待应用容器启动完成后,运行:
python3 poc/poc_lab.py
预期输出:
== 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
向存在漏洞的目标发送 HTTP-only PoC:
python3 poc/poc_http_only.py --target http://127.0.0.1:8081
手动验证证明文件:
docker compose exec vuln sh -lc 'cat /var/www/html/storage/logs/cve_2026_34234_proof.txt'
预期证明:
uid=1000(laravel) gid=1000(laravel) groups=1000(laravel)
laravel
<container-hostname>
对已修复版本运行相同的请求:
python3 poc/poc_http_only.py --target http://127.0.0.1:8082
验证已修复版本的行为:
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"'
预期:
no proof file
删除容器、网络和实验卷:
docker compose down -v
本仓库仅用于教育性安全研究和防御性验证。
所有演示均旨在提供的本地 Docker 实验环境中运行。该概念验证避免破坏性操作、持久化、凭据窃取、数据外泄和针对现实系统的攻击。
未经明确授权,请勿对本项目用于任何系统。作者不对因使用本材料造成的误用或损害负责。