rootdirective-sec/CVE-2026-34234-Lab
GitHub: rootdirective-sec/CVE-2026-34234-Lab
这是一个用于在本地 Docker 中演示和验证 CtrlPanel 安装程序未授权 RCE 漏洞(CVE-2026-34234)的安全实验环境,并附带了无害的概念验证脚本。
Stars: 0 | Forks: 0
# CVE-2026-34234 - CtrlPanel 安装程序 RCE 实验环境
用于在 CtrlPanel 中演示 CVE-2026-34234 的本地 Docker 实验环境。
本仓库对比了以下内容:
- `vuln`:通过摘要固定的 CtrlPanel `1.1.1`
- `patched`:通过摘要固定的 CtrlPanel `1.2.0`
本实验环境仅限本地使用,并将服务绑定到 `127.0.0.1`。
## 摘要
CVE-2026-34234 是 CtrlPanel Web 安装程序中的一个未授权 RCE。
该问题是由于两个 bug 链式触发的:
1. 安装程序的表单处理器在 `install.lock` 验证关卡之前即可访问。
2. 安装程序的输入被直接插入到 shell 命令字符串中。
在本实验环境中,易受攻击的容器会执行一个无害的验证命令,并将其输出写入容器内部。已修复的容器会接收到相同的请求,但不会创建验证文件。
预期结果:
```
vulnerable => proof file created
patched => no proof file
```
## 根本原因
### 1. `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`。
- 用户可控的安装程序值可能会成为该命令字符串的一部分。
- shell 元字符可以改变命令结构。
### 2. 存在漏洞的安装程序表单路径
原始存在漏洞的文件:
```
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 数据。
- 这些值被嵌入到 shell 命令字符串中。
- 安装程序的 endpoint 无需身份验证即可访问。
### 3. 安装程序验证关卡顺序
安全通告指出,`public/installer/index.php` 仅在加载/执行安装程序表单逻辑之后才检查 `install.lock`。这使得安装程序处理器在已安装的实例上依然可被访问。
## 补丁 / 修复
### 1. 提前进行安装程序锁定检查
该修复将 `install.lock` 检查移至加载表单处理器之前。
修复后的行为:
```
if (file_exists('../../install.lock')) {
exit("The installation has been completed already. Please delete the File 'install.lock' to re-run");
}
```
### 2. 避免 shell 字符串执行
原始修复后的文件:
```
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 字符串。
- 诸如 `$()` 之类的 payload 语法将保持为字面输入,而不会被解释为 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.1`
- `patched`:真实的 CtrlPanel `1.2.0`
- `fake-api`:本地伪造的 Pterodactyl API,仅用于满足安装程序检查
- `mysql_vuln` / `mysql_patched`:独立的 MariaDB 实例
- `redis_vuln` / `redis_patched`:独立的 Redis 实例
本实验环境未修改 CtrlPanel 应用程序的源代码。
Dockerfiles 仅包装了原始容器的 entrypoint,以规范化 Docker Desktop runtime 的权限:
```
/var/www/html/storage
/var/www/html/bootstrap/cache
```
在修复权限后,wrapper 会执行原始产品的 entrypoint。
## PoC 设计
主要的 PoC:
```
poc/poc_http_only.py
```
特性:
- 仅发送 HTTP POST 请求
- 不使用 `docker exec`
- 不检查容器
- 不启动反弹 shell
- 仅使用无害命令:`id`、`whoami`、`hostname`
辅助脚本:
```
poc/poc_lab.py
```
用途:
- 发送相同的 HTTP 请求
- 使用 `docker compose exec` 验证容器内部的验证文件
- 仅用于演示和回归测试
App 容器内部的验证文件:
```
/var/www/html/storage/logs/cve_2026_34234_proof.txt
```
## 运行
从干净的实验环境状态开始:
```
docker compose down -v --remove-orphans
docker compose up -d --build
```
等待 app 容器启动,然后运行:
```
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 专属测试
向易受攻击的目标发送 HTTP 专属 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
```
对已修复的容器运行相同的请求:
```
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
```
## 注意事项
- 本实验环境仅供本地安全研究使用。
- 请勿对您不拥有或未经授权测试的系统运行 PoC。
- 验证文件被有意限制为仅在容器内部输出命令结果。
- 存在漏洞和已修复的服务使用独立的数据库和 Redis 实例。
- fake API 的存在仅仅是为了模拟安装程序流程所需的最低限度的 Pterodactyl API 响应。
## 免责声明
本仓库仅供教育性安全研究和防御性验证使用。
所有演示均旨在提供的本地 Docker 实验环境内运行。概念验证避免了破坏性操作、持久化、凭据窃取、数据泄露和针对现实世界的攻击。
未经明确授权,请勿将此项目用于任何系统。对于因使用本材料而造成的误用或损害,作者不承担任何责任。
## 参考
- GitHub 安全通告:https://github.com/Ctrlpanel-gg/panel/security/advisories/GHSA-jmhr-q9q5-fqwh
- CVE 记录 / NVD:https://nvd.nist.gov/vuln/detail/CVE-2026-34234
- 修复版本:https://github.com/Ctrlpanel-gg/panel/releases/tag/1.2.0
- 上游仓库:https://github.com/Ctrlpanel-gg/panel
标签:Docker, Go语言工具, OpenVAS, PHP, 安全防御评估, 搜索引擎查询, 漏洞复现环境, 版权保护, 请求拦截, 远程命令执行, 逆向工具