DSH · QVD SECURITY PATCH
YUME STAR
COMMUNITY PATCH·非官方· AS-IS 无担保·2026-09-26 轮次

QVD 漏洞全景

 

DeepSeek Harness 的 5 个 QVD 编号漏洞、1 个 CVE 与 1 条未定级的零日 —— 逐条摊开成因、后果、阻断点,以及至今没修好的那几条。 配套:社区补丁 · 33 个红队对抗用例 · 32 项只读验证。

向下滚动
01 / OVERVIEW

先说结论:这是一个被打过、也打过自己的补丁集

它不是官方的安全更新,也不是一份完整审计。它做三件事:修已披露的漏洞、拿红队去拆自己的补丁、把拆出来的缺口如实列出来。

6个
已披露编号(5 QVD + 1 CVE)
10.0CVSS
最高分:52646 子进程逃逸
7条
攻击链(A–G)逐条映射阻断点
33个
红队对抗用例(3 个套件)
32项
只读静态验证(11 + 9 + 12)
8条
本轮明确未修的残留

这是什么

公开漏洞的 社区补丁 + 对抗测试 + 验证脚本。非官方、非完整审计,不保证没有别的问题。

我该用哪个

0.1.7-rc.2 → fixes-0.1.7-rc.2.patch;0.1.5-rc.2 → fixes-0.1.5-rc.2.patch;0.1.2-alpha.2 → fixes.patch。单一补丁、一条命令。

装完怎么验

跑 verify-dsh-fixes.bat:只读、不联网、不上传;-tests 才调用你自己机器上的 pnpm。

不该期待什么

它 不解决间接提示注入,也挡不住「先拿到管理 RPC 再改会话模式」的主动提权链。残留 8 条见后文。

10.0 严重9.8 / 9.6 极危高危(官方未给数值,不臆造)7.8 / 7.5 高危未定级
02 / VULNERABILITIES

六个编号,一条比一条更靠近宿主机

从「配置里塞一段表达式」到「沙箱内的 AI 自己把沙箱拆了」——同一套架构上,越靠后的越难修,也越容易被漏掉。

10.0
QVD-2026-52646严重

bash / pwsh 子进程逃逸

链 D · 已阻断(提示注入型)
成因

受限策略下 bash / pwsh 仍可直接 spawn 子进程,绕过沙箱。

后果

与 52644 串联即达成宿主机任意命令执行(公开演示可达 root)。默认组合即可命中,不依赖任何部署配置失误。

修复

SubprocessSpawnSpec 增加 sandboxPolicy 与 argvConfined;受限策略未声明 argvConfined 即拒绝启动。

红队

原守卫用 mode !== undefined 做前置 —— sandboxPolicy: {} 会静默放行。已改 fail-closed:policy 一旦存在,只有字面 danger-full-access 才豁免。诚实标注:这是纵深防御缺口,不是可远程触发的漏洞。

9.8
QVD-2026-57410部分修复

伪造 Host · 未授权管理 RPC

链 E · 已阻断(未声明 trustedProxies 时)
成因

管理 RPC 仅凭 HTTP Host 头判断请求是否来自本机回环 —— 而 Host 完全由客户端构造。

后果

远程未认证 RCE:伪造 Host: 127.0.0.1 → 解锁高权限 RPC → 注册一个指向攻击者假模型服务器的临时 Provider → 用确定性工具调用驱动 bash。无需真实模型、无需有效 API key。

本仓库

传输围栏(Host 声称 loopback 而 TCP peer 不是 → 拒绝)+ token 交换补 peer 校验 + trustedProxies(默认空 = 最严格)。

0.1.7

duplicate Host 拒绝(读 rawHeaders,不被折叠骗过)· /api/file 只读根白名单 · /api/session.export 鉴权 · /plugins 与 /plugins/events 信任围栏。

残留

上游建议原文就是「校验 TCP remoteAddress 而非 Host 头」,未被采纳 → token 成为唯一防线;token 一旦进日志 / Referer / 截图 / 被分享的链接,伪造 Host 即可换取浏览器 cookie。

9.6
CVE-2026-82533极危

沙箱内的 Agent 自己把沙箱拆了

链 F · 部分缓解
根因

与 57410 同一个根因(只信 Host、不看 TCP 来源),但方向相反:不是外面打进来,而是里面的 agent 自己解除限制。

后果

DSH 启动时把控制接口地址与会话 ID 交给 agent 的 shell → 沙箱内一条命令把会话模式设为 danger-full-access → 不弹审批、不留异常记录(「改模式」不算「申请提权」,审批钩子不触发)。

为什么

传输围栏挡不住这条 —— peer 本来就是 loopback,判据天然放行。真正相关的是读面:cookie 的签名密钥就在 .credentials.yaml 里。

0.1.7

凭据(.credentials*)在任何模式下对模型可读面都被拒(含 danger-full-access);但 shell / pwsh 通道仍可读 —— 未闭合。

高危
QVD-2026-52644官方未给数值

cordis 沙箱工具逃逸

链 C · 已阻断(提示注入型)
成因

沙箱内自定义工具 execute 拿到的执行上下文里,携带真实的 agent / ctx / session 对象。

后果

从「受限 vm 代码执行」升级为「访问宿主服务(含秘密存储)」。它通常不作为单独一击,而是链 D 的前置一步。

修复

新增 sandboxToolExec 白名单执行视图,只含 name / callId / arguments(JSON clone)/ signal。

诚实

官方只标注「高危」、未给出具体分值 —— 此处不臆造数字,环内直接写「高危」。

7.8
QVD-2026-52631高危

loader 配置表达式注入

链 A · 部分缓解
成因

cordis.yml 里的 !!js 表达式用 with (ctx) { eval(expr) } 包在 new Function 中求值,表达式经 ctx 落到宿主全局作用域,可拿到 process / require / module。

后果

投递一份恶意配置即以宿主进程权限同步执行任意 Node 代码,并可借 HMR 监听形成「写入即执行、每次启动重放」的持久后门。

更正

威胁模型比原始描述更重:不只需「诱导用户粘贴配置」——装一个第三方插件就足够投递(bundle 层的 cordis.patch.yml 同样能写 !!js)。

修复

改为 node:vm 隔离求值;上下文以单个 JSON 字符串注入并在 vm 内重建(宿主对象零注入);process 只暴露 env/platform/arch/version/execPath/cwd;runInContext 限时 1000ms。

残留

vm 内的 process.env 仍是全量投影,可读任意环境变量并经抛错把值带进错误信息。本仓库无法修:按来源分层投影需要来源信息,而它在 composeEntries 的 layers.flat() 处已被丢弃 —— 需上游引入「配置来源信任」模型。

7.5
QVD-2026-52632高危

fs-sandbox 读逃逸

链 B · 部分缓解
成因

fs-sandbox 只约束了写入,读取可以越过工作区读任意路径。

后果

进程内:模型可读工作区外任意路径;进程级:受限 shell 子进程可读整个宿主文件系统 —— ~/.ssh 私钥、.env、云凭据、API key。泄露的凭据可用于横向移动。

修复

读方法在执行点按策略校验,越界抛结构化 FS_SANDBOX_DENIED;tool-str-replace-editor 的 view 补策略;动态插件的 fs 门面强制携带会话策略。

0.1.7

① 搜索根栅栏:grep / glob 此前裸跑 ripgrep、不传策略,一次 path: '~/.ssh' 就能读到 read 拒绝的内容 —— 现在在 spawn 之前拦截,拒绝文本与 read 完全一致(12 条用例)。② 凭据不可读:$DSH_HOME/.credentials* 任何模式都拒。

残留

进程级读面(只读会话里的 bash / pwsh 仍可能读 ~/.ssh)· 硬链接绕过(路径判定无法察觉,需先有工作区写权限,属限制而非独立漏洞)· read / glob / grep 的拒绝提示提到 sandbox_permissions,但三个工具的 schema 里没有这个字段。

未定级
链 G · 自有零日未上报上游 · 细节不公开

run_code 代码运行时隔离缺口

无前置条件 · 已提供修复本节不含可复现攻击步骤
影响

code-runtime-worker-thread 把模型代码交给 worker 的全局作用域执行,且该运行时不接收会话沙箱策略。

前置

无 —— 不需要网络暴露、令牌、Host 伪造、插件,也不需要模型越狱。

实测

同一会话、同一 workspace-write 策略下 A/B 对照:read / write 与 pwsh 子进程对工作区外路径全部被拒,而 run_code 仍可读写工作区外文件。

修复

旧架构(0.1.2–0.1.5)用 code-runtime-isolation.patch:改 node:vm 上下文、禁用字符串代码生成、不提供 importModuleDynamically、定时器 null-prototype 注入。0.1.7 换了 ptc-runtime-node 架构,改在新架构上封堵宿主对象泄漏面(detachedHostSurface():null 原型包装 + Reflect.construct 保留 new + WeakSet 防环)。

验证

console.log.constructor('return process.pid')() 由返回宿主 pid 变为 not a function。但 shell / pwsh 的进程级读面仍未动。

03 / ATTACK CHAINS

七条链,每一步都标了它的下场

这份表里必然包含「没被掐断的步骤」—— 判定只有三态:已阻断 / 部分缓解 / 未验证。「未验证」不会被写成「安全」,「没观察到攻击」也不会被写成「不可能被攻击」。

CHAIN A

配置加载 !!js → 宿主 RCE

QVD-2026-52631 · 7.8部分缓解
恶意 cordis.yml▸!!js 表达式▸with(ctx){eval}▸宿主全局作用域▸任意 Node 代码

阻断点:改为 node:vm 隔离求值,上下文以 JSON 字符串注入并在 vm 内重建,process 只暴露白名单字段。

没掐断:vm 内 process.env 仍是全量投影,可经抛错把密钥带进日志 / UI。需上游引入「配置来源信任」模型。

CHAIN B

只读沙箱读逃逸 → 密钥泄露

QVD-2026-52632 · 7.5部分缓解
只读会话▸grep / read / 受限 shell▸工作区外路径▸私钥 · .env · API key

阻断点:读方法执行点策略校验 + FS_SANDBOX_DENIED;editor view 补策略;插件 fs 门面强制带策略;0.1.7 再补搜索根栅栏与凭据不可读。

没掐断:硬链接绕过、进程级读面(bwrap --ro-bind / / 一类)、读侧「放宽一次」的审批入口根本没有。

CHAIN C

动态插件 vm 逃逸 → 拿到宿主 exec

QVD-2026-52644 · 高危已阻断
间接提示注入▸cordis_define▸cordis_run▸execute 拿到真实 ctx▸宿主服务(含秘密存储)

阻断点:sandboxToolExec 白名单执行视图,只含 name / callId / arguments(JSON clone)/ signal。

但:「已阻断」只对提示注入型链成立 —— 攻击者若能先改会话模式(链 E / F),这层守卫按设计豁免。

CHAIN D

C 续 · 无约束 spawn → 宿主 RCE

QVD-2026-52646 · 10.0已阻断
vm 逃逸取得宿主 exec▸subprocess.spawn▸无约束宿主进程▸以 DSH 进程权限执行命令

阻断点:argvConfined + sandboxPolicy;受限策略未声明即拒绝;红队发现 fail-open 后改为 fail-closed。

但:同样只挡提示注入型链。对已经拿到管理 RPC 的攻击者,这四个补丁不构成阻断 —— 阻断发生在链 E / F 的入口。

CHAIN E

Host 头伪造 → 未授权管理 RPC → RCE

QVD-2026-57410 · 9.8已阻断(远程攻击者)
服务暴露到非 loopback▸伪造 Host: 127.0.0.1▸高权限管理 RPC▸注册假模型 Provider▸确定性工具调用驱动 bash

阻断点:传输围栏(Host 声称 loopback 而 peer 不是 → 拒绝)+ token 交换补 peer 校验;0.1.7 再加 duplicate Host 拒绝、读根白名单、导出鉴权、/plugins 两道围栏。

没掐断:结构化拒绝 code 已导出但未进 HTTP 响应体;/api/file 的 Config.roots 只有直接挂载才能设;session.export 的身份只到「部署」层;反代实景未测。

CHAIN F

沙箱内的 Agent 自己解除沙箱

CVE-2026-82533 · 9.6部分缓解 · 围栏挡不住
启动时把控制接口 + 会话 ID 交给 shell▸一条命令改会话模式▸danger-full-access▸不弹审批 · 不留异常记录

为什么挡不住:传输围栏的判据是「peer 是否 loopback」,而这条链的 peer 本来就是 loopback —— 天然放行。

真正相关的是读面:token 的签名密钥存在 $DSH_HOME/.credentials.yaml,agent 若能读到即可伪造 cookie。建议把凭据存储纳入沙箱读面保护(对 agent 不可读),而不是继续在 HTTP 层堆判据。0.1.7 已让模型读面读不到凭据,但 shell / pwsh 通道仍可读。

CHAIN G

run_code 代码运行时隔离缺口

未上报 · 未定级该逃逸面已封堵
模型代码▸worker 全局作用域▸不接收会话策略▸读写工作区外文件

为什么值得单列:其它链都要求某种前置(配置、插件、网络暴露、令牌),这条什么前置都不要,且与工具层的拒绝行为形成刺眼的 A/B 对照。

披露节奏:细节只提交给上游安全渠道,本页与仓库文档均不含可复现攻击步骤;旧架构补丁请勿在上游审核前公开分发。

04 / ROOT CAUSE

同一个根因,两个相反的方向

「你是不是从本机来的?」—— 这个问题,实现用 Host 请求头回答,而上游 PoC 给出的修复建议原文是「校验 TCP remoteAddress 而非 Host 头」。这一个字的差别,同时造出了从外向内打的漏洞和从内向外拆的漏洞。

判据 = HTTP Host 头

Host 完全由客户端构造 · 可任意伪造
Host: 127.0.0.1  ←  真实来源 203.0.113.7
✗ 判定为「本机」→ 放行高权限 RPC → 远程未认证 RCE
  • 不需要真实模型、不需要有效 API key
  • 注册一个指向攻击者假模型服务器的临时 Provider 即可驱动 bash
  • 前提:服务暴露到非 loopback(Docker -p 0.0.0.0、nginx 透传 Host)

判据 = TCP peer(本仓库的传输围栏)

Host 声称 loopback 而 peer 不是 → 拒绝
Host: 127.0.0.1 · peer: 203.0.113.7  ✗ 403
✓ 判定为「远端」→ 拒绝;并给 token 交换补上 peer 校验
  • 新增 trustedProxies,默认空 = 最严格
  • 0.1.7 追加:duplicate Host 拒绝(>1 个或与请求权威不符 一律拒绝)
  • 仍未解决链 F —— 那条链的 peer 就是 loopback,围栏天然放行
05 / PATCHES

三轮补丁,选你的版本

上游在 0.1.2 → 0.1.5 的三个版本区间里没有修复任何一个 QVD,57410 只被部分修复。所以这个仓库按版本给了三份补丁 —— 选错了会白忙一场。

补丁 fixes-0.1.7-rc.2.patchSHA256 B23EB756…105 文件 · 719078 字节
git apply --check fixes-0.1.7-rc.2.patch
git apply fixes-0.1.7-rc.2.patch
  • 7 项:搜索根栅栏 · 凭据不可读 · run_code 宿主对象封堵 · /api/file 根白名单 · /api/session.export 鉴权 · /plugins 与 /plugins/events 围栏 · duplicate Host 拒绝
  • 跑 apply-dsh-fixes.bat 也行:按检出 package.json 的版本自动选补丁,先预检、再询问、后写入
  • 残留 8 条一条没修,逐条列在下一节
补丁 fixes-0.1.5-rc.2.patch48 文件 · +2114 / −74单一自洽 diff
git apply --check fixes-0.1.5-rc.2.patch
git apply fixes-0.1.5-rc.2.patch
  • 把基线 + 5 个补充补丁的最终效果合并成单一自洽 diff,不会再出现「只打了基线、装上一个带 fail-open 缺陷的守卫」这种事故
  • 0.1.5 用户请优先用它,不要再用「基线 → 补充补丁」的分步流程
  • 同一份 apply-dsh-fixes.bat 会自动认出这个版本
补丁 fixes.patch15 文件 · +668 / −44相对 0.1.2-alpha.2
git apply --check fixes.patch
git apply fixes.patch
  • 修 52631 / 52632 / 52644 / 52646 四个漏洞
  • ⚠️ 此补丁在干净的 0.1.5-rc.2 上打不上(5 个文件冲突,原因是上游改了注释与代码,不是行尾问题)—— 0.1.5 用户请用左边的补丁
  • ⚠️ 其中的 assertConfinedUnderPolicy 是带 fail-open 缺陷的旧版本,在 0.1.5 上必须叠加补充补丁 4
  • Windows 下若因 CRLF 报 does not apply:git apply --ignore-whitespace fixes.patch
06 / STILL OPEN

没修好的,就写「没修好」

这一节是这份补丁集里最该被读的部分。它的价值不在于修了多少,而在于它没有把没修的说成修了。

0.1.7-rc.2 轮次明确未修的 8 条

写在补丁自己的 README 里,逐条如实记录 —— 不是脚注,是正文。

01读侧「放宽一次」审批入口缺失:read / glob / grep 的拒绝提示提到 sandbox_permissions,但这三个工具的 schema 里没有该字段。未修
02进程级读面未修:shell / pwsh 仍可读全盘(bwrap --ro-bind / / 一类),包括凭据 —— 链 F 仍走得通。未修
03/api/file 的 Config.roots 只有直接挂载才能设:随附组合未暴露到 cordis.yml。未修
04session.export 的调用方身份只到「部署」层:同一 DSH_HOME 多实例共享注册表时区分不了实例。未修
05includeDescendants 拉入的后代日志未逐个复检。未修
06结构化拒绝 code 已导出但未进 HTTP 响应体。未修
07apps/web e2e 未跑:DSH_SNAPSHOT=replay pnpm run test:web。未验证
08上游 0.1.7-rc.2 对这些点一条都没修(除 run_code 零日:官方已换 ptc-runtime-node 架构,旧 PoC 不再适用)。上游

跨轮残留同样在册:52631 的 env 全量投影(架构限制)· 硬链接读取工作区外 inode · read 侧的审批入口 · trustedProxies 非空时的来源未验证 · TOCTOU(栅栏检查与读取之间的路径替换窗口)未验证 · Linux / macOS 上的进程级沙箱行为未验证。

07 / VERIFY

怎么证明它真的生效了

静态检查只能证明「修复标记字符串在位」—— 被注释掉的代码同样含有那个字符串。真正的证据是回归测试和对抗结果。

32

项只读静态检查

verify-dsh-fixes.bat:基线 11 + 补充与红队 9 + 0.1.7 轮次 12。缺失记 [FAIL] / [SKIP],行为可预期;不联网、不上传、不下载、不执行外部程序。

33

个红队对抗用例

3 个套件,主动尝试绕过本仓库自己的补丁。结果与已知绕过见 REDTEAM.md —— 包括本轮拆出来的 fail-open 守卫、搜索根缺口、凭据可读面。

7

条攻击链 × 三态判定

每条链的每一步都映射到具体补丁与用例:已阻断 / 部分缓解 / 未验证。这张表里必然存在没被掐断的步骤,因为它不许把「没观察到」写成「不可能」。

4

组 2026-09-26 实测

run_code 逃逸 A/B 对照 · cookie 伪造 · /plugins/events 未认证 · 上游版本对照。修复前后各测一次,拿行为差异说话。

这份文档不把 Not Tested 写成 Secure,也不把 Attack Not Observed 写成 Attack Impossible。
—— 攻击链文档的开篇立场。它的价值不在于「修了多少」,而在于「没修的说成没修」。