Repository navigation
[Security] Cross-session approval hijack in the dsh web gateway allows unauthenticated local code execution #1220
qinaigonghe
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Security disclosure — verified end-to-end against the default configuration on rc.5@47f943859b.
This repo has no SECURITY.md, private vulnerability reporting is not enabled, and Issues are disabled, so per the README this report is posted as a discussion.
SUMMARY
The dsh Web gateway broadcasts every session's pending tool-approval and user-question
prompts over a global, unauthenticated WebSocket stream (
/api/events.mux) and settlesthem via
/api/respondkeyed only byrpcId, with no binding between the answerer andthe session owner. Any local process or page — or any LAN peer when the server is
exposed — can silently approve or reject another session's human-in-the-loop decisions.
Verified on the default configuration: an attacker answered a victim session's
danger-full-accesssandbox escalation and the command executed on the host without thevictim human ever clicking.
VULNERABILITY DETAILS
The gateway (default
http://127.0.0.1:3080) holds one process-wide pending-approvaltable (
pendingApprovals) and one global set of mux subscriber queues (muxQueues).Global broadcast: every
approval/requestis pushed as anapproval/requestedserver-request to ALL connected mux queues, and a new mux subscriber receives a
replay of every session's still-pending approvals/questions.
The stream is WebSocket-only (
/api/events.mux; plain GET -> 426) and carries noparameters, token, or session-owner binding.
Unbound answering:
/api/respondroutes by the echoedrpcIdthroughpendingApprovalsand validates only that the payload'sapprovalId/sessionIdmatch the pending entry. The answer payload schema is exactly three fields
(
sessionId,approvalId,outcome), and the client-response envelope has noidentity field.
respondis not inPRIVILEGED_METHODS.The same
rpcId-only routing also lets an attacker answer another session'sask_user_questionprompts (api-proxy.ts:3717-3745).REPRODUCTION (verified end-to-end on rc.5@47f943859b, real provider, default
workspace-write+approval: ask)it and the model retries the same command with
sandbox_permissions: "danger-full-access"+justification(its sanctionedescalation path). An approval is suspended for the victim session.
{"rpcId":"304d7b36-c552-42c0-b5b5-445d1e16e1cd","method":"approval/requested",
"payload":{"type":"approval/requested",
"sessionId":"session-f0ec9c08-1248-45c8-9252-be8dafaae877",
"approvalId":"53469106-6520-4a13-9860-605fe3093793",
"toolName":"bash","callId":"call_00_ubOxPeXMj9gwFQiQ10J82254",...}}
curl -s -X POST http://127.0.0.1:3080/api/respond -H 'content-type: application/json'
-d '{"type":"client-response","rpcId":"304d7b36-...",
"result":{"ok":true,"value":{"sessionId":"session-f0ec9c08-...",
"approvalId":"53469106-...","outcome":"allowed-once"}}}'
-> {"accepted":true}
approval/resolved; victim audit showsapproval/asked(seq 2635) ->approval/decided(seq 2636, allowed-once) with nohuman interaction; the bash call executes under
danger-full-access(tool/result: PROBE, exit 0) and writes a workspace-external file
(/Users/lihao/Documents/dsh-outside-workspace-probe.txt, cleaned up after test).
A one-line variant with "outcome":"rejected" silently vetoes the victim's legitimate
approvals; the first responder to settle an rpcId wins.
IMPACT
Unauthenticated local process (other user, malware, compromised same-origin page, or
LAN peer when the server is exposed with --host 0.0.0.0 and a matching Host) can grant
itself danger-full-access command execution on the dsh host through another user's
session, or veto that user's approvals, without consent and leaving only a forged audit
record. The same primitive hijacks ask_user_question prompts. This breaks the product's
stated human-in-the-loop approval contract across sessions.
AFFECTED VERSIONS
(rc.6 not available for verification).
PATCHED VERSIONS
SEVERITY
beyond loopback.
auth layer".
RELATIONSHIP TO PRIOR REPORTS
the model and survives that fix.
cross-session answering of approvals and questions.
SUGGESTED FIX
create/resume; /api/respond must prove it).
REFERENCES
CREDITS
DeepSeek provider, default workspace-write + approval: ask).
TIMELINE
All reactions