CVE-2026-58169: Verified Reproduction
CVE-2026-58169: Vibe-Trading DNS rebinding authentication bypass leading to RCE
CVE-2026-58169 is verified against the affected target. Vulnerability class: RCE. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00254.
What Is CVE-2026-58169?
CVE-2026-58169 is a high-severity DNS rebinding authentication bypass (CWE-346) in Vibe-Trading that lets an unauthenticated attacker bypass API authentication and reach a remote code execution primitive. Pruva reproduced it (reproduction REPRO-2026-00254).
CVE-2026-58169 Severity & CVSS Score
CVE-2026-58169 is rated high severity, with a CVSS base score of 7.5 out of 10.
High — serious impact or readily exploitable. Prioritize remediation.
How to Reproduce CVE-2026-58169
pruva-verify REPRO-2026-00254 curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00254/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-58169
- reached the target end-to-end
- full exploit chain demonstrated
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
DNS-rebound Host header (rebind.attacker.test:8899) with no bearer token, sent via curl --resolve to 127.0.0.1
- DNS rebinding
- loopback TCP peer
- _is_local_client() bypasses API_AUTH_KEY
- _shell_tools_enabled_for_request() enables bash tool
- POST /sessions/{id}/messages
- BashTool subprocess.run(shell=True)
- RCE
How the agent worked
Root Cause and Exploit Chain for CVE-2026-58169
Vibe-Trading before v0.1.10 contains a DNS rebinding authentication bypass vulnerability (CWE-346 Origin Validation Error). The FastAPI API server (agent/api_server.py) binds to 0.0.0.0 by default and treats any TCP connection from a loopback peer address (127.0.0.1, ::1, localhost) as a trusted local/dev-mode caller, bypassing API_AUTH_KEY bearer-token authentication entirely. Because the server performs no Host header validation on loopback-trusted requests, an attacker can use DNS rebinding to make a victim's browser issue same-origin JSON requests to the local API with an attacker-controlled Host header and no bearer token. The loopback peer IP causes _is_local_client() to return True, so _validate_api_auth() skips authentication. Furthermore, _shell_tools_enabled_for_request() returns True for loopback clients, enabling the bash tool in the agent's tool registry. The attacker can then send a message to a session that triggers the BashTool, which executes subprocess.run(command, shell=True) — achieving remote code execution as the API process user. The attacker can also overwrite LLM and data-source settings to exfiltrate credentials.
- Package/component affected:
HKUDS/Vibe-Trading—agent/api_server.py(loopback trust/auth boundary, Host-header validation),agent/src/tools/bash_tool.py(BashTool RCE primitive),agent/src/tools/__init__.py(shell tool gating) - Affected versions: Vibe-Trading < 0.1.10 (confirmed on v0.1.9, commit
8cebccb9b301f962d11bf0fa8c6be9bf7d7e12f2) - Fixed version: v0.1.10 (commit
54f944743efee7b9cbaaeafb5966c583d0d4ac66), via PR #242 (6e06017) — adds_reject_untrusted_loopback_hostmiddleware - Risk level: High (CVSS 7.7 / 8.1)
- Consequences: Unauthenticated remote code execution as the API process user; unauthorized access to privileged control-plane endpoints (
/live/runner/start,/swarm/runs,/sessions/{id}/messages); credential exfiltration via settings overwrite (/settings/llm,/settings/data-sources)
Impact Parity
- Disclosed/claimed maximum impact: DNS rebinding authentication bypass leading to remote code execution (code_execution) and credential exfiltration
- Reproduced impact from this run: Full chain demonstrated — DNS rebinding → auth bypass → shell tools enabled → BashTool executes arbitrary shell command as API process user (
uid=1000(vscode)). The proof file/tmp/vibe_trading_rce_proof_*.txtwas created withRCE_CONFIRMED. The agent trace showstool_call: bashwithexit_code: 0andstdout: "uid=1000(vscode)...". - Parity:
full - Not demonstrated: The reproduction used a mock LLM server (simulating an attacker-controlled LLM endpoint) to instruct the agent to invoke the bash tool. In a real attack, the attacker would either poison the LLM settings (via the auth-bypassed
/settings/llmPUT) to point the agent at an attacker-controlled LLM, or craft a user message that persuades the agent to run a shell command. The code-execution primitive (BashTool withsubprocess.run(shell=True)) is the same either way.
Root Cause
The root cause is a loopback peer IP trust without Host header validation:
_is_local_client(request)(line 668) checks onlyrequest.client.host(the TCP peer address). If it is127.0.0.1,::1,localhost, or a trusted Docker gateway, it returnsTrue._validate_api_auth()(line 645): whenAPI_AUTH_KEYis not configured (dev mode), a loopback client is allowed without any token. WhenAPI_AUTH_KEYIS configured, the loopback shortcut is not used — but in dev mode (the default), the bypass is complete._shell_tools_enabled_for_request(request)(line 727): returns_is_local_client(request) or _env_shell_tools_enabled(). For loopback clients, this isTrue, enabling thebashandbackground_runtools in the agent registry.No Host header validation existed — a loopback request carrying
Host: attacker.example:8899was trusted as local. DNS rebinding makes a browser send exactly such a request: the attacker's domain resolves to127.0.0.1, the browser connects from loopback, and sendsHost: attacker.example:8899(the original hostname) with no bearer token.The
BashTool.execute()(line 38 ofbash_tool.py) callssubprocess.run(command, shell=True, cwd=cwd, ...)— arbitrary command execution.
Fix (PR #242, commit 6e06017): Added _reject_untrusted_loopback_host middleware that rejects loopback requests carrying an untrusted Host header with HTTP 403 ("Untrusted local API host") before route handlers are reached. Accepted loopback Host values: localhost, 127.0.0.1, ::1, [::1], testserver, plus any configured via API_ALLOWED_HOSTS. Remote clients remain governed by bearer-token auth. Normal loopback dev-mode requests with Host: 127.0.0.1:port continue to work (no regression).
Related fixes in the same v0.1.10 cycle:
- PR #243 (
7eb8dea): require explicit opt-in (VIBE_TRADING_ENABLE_SHELL_TOOLS) for agent shell tools — closes the BashTool exposure even if the Host gate is bypassed - PR #245 (
b608fc5): require bearer token for settings WRITE endpoints — closes the credential exfiltration path
Reproduction Steps
- Script:
bundle/repro/reproduction_steps.sh— self-contained, run withPRUVA_ROOT=<bundle_dir> bash bundle/repro/reproduction_steps.sh - What the script does:
- Installs Python dependencies (fastapi, uvicorn, langchain, dnslib, etc.)
- Clones/reuses the Vibe-Trading repo from the project cache
- Starts a DNS rebinding server (
dns_rebind_server.py) that resolvesrebind.attacker.testto127.0.0.1 - Proves the DNS rebinding with a real DNS A-record query →
127.0.0.1 - Starts a mock OpenAI-compatible LLM server (
mock_llm_server.py) that returns abashtool-call instructing the agent to executeid; whoami; echo RCE_CONFIRMED > /tmp/... - Phase 1 (vulnerable v0.1.9): Starts the real Vibe-Trading API server (bound to
0.0.0.0:8899, dev mode, mock LLM). Usescurl --resolve rebind.attacker.test:8899:127.0.0.1to send DNS-rebound HTTP requests with no bearer token:POST /sessions→ HTTP 201 (auth bypassed, session created)POST /sessions/{id}/messages→ triggers the agent loop → BashTool executes the attacker's command → RCE proof file createdPUT /settings/llm→ auth-bypassed settings write (credential exfiltration path)
- Phase 2 (fixed v0.1.10): Same DNS-rebound requests → HTTP 403 ("Untrusted local API host"). Normal loopback with
Host: 127.0.0.1:8899→ HTTP 201 (no regression). - Writes
validation_verdict.jsonandruntime_manifest.json
- Expected evidence:
logs/dns_rebind_proof.txt:DNS_A:rebind.attacker.test:127.0.0.1logs/vuln_create_session.txt: HTTP 201 with session_id (no auth)logs/vuln_rce_proof.txt:RCE_CONFIRMEDlogs/vuln_trace.jsonl:tool_call: bashwithexit_code: 0,stdout: "uid=1000(vscode)..."logs/fixed_create_session.txt: HTTP 403{"detail":"Untrusted local API host"}logs/fixed_normal_loopback.txt: HTTP 201 (no regression)
Evidence
All artifacts are under bundle/logs/ and bundle/repro/:
| Artifact | Description |
|---|---|
logs/reproduction_steps.log |
Full script execution log |
logs/dns_rebind_proof.txt |
DNS A-record query proving rebind.attacker.test → 127.0.0.1 |
logs/vuln_create_session.txt |
Vulnerable: POST /sessions → HTTP 201 (auth bypass) |
logs/vuln_send_message.txt |
Vulnerable: POST /sessions/{id}/messages → HTTP 200 |
logs/vuln_rce_proof.txt |
RCE proof file: RCE_CONFIRMED |
logs/vuln_trace.jsonl |
Agent trace showing tool_call: bash, tool_result: exit_code 0, uid=1000(vscode) |
logs/vuln_session_messages.json |
Session message history (user + assistant reply) |
logs/vuln_settings_write.txt |
Vulnerable: PUT /settings/llm → HTTP 422 (auth bypassed, validation error) |
logs/fixed_create_session.txt |
Fixed: POST /sessions → HTTP 403 "Untrusted local API host" |
logs/fixed_normal_loopback.txt |
Fixed: normal loopback → HTTP 201 (no regression) |
logs/fixed_settings_write.txt |
Fixed: PUT /settings/llm → HTTP 403 |
logs/api_server_vulnerable.log |
Vulnerable API server log |
logs/api_server_fixed.log |
Fixed API server log |
logs/mock_llm_server.log |
Mock LLM server log |
logs/dns_rebind_server.log |
DNS rebinding server log |
Key trace excerpt (vuln_trace.jsonl):
{"type": "tool_call", "iter": 1, "tool": "bash", "args": {"command": "id; whoami; echo RCE_CONFIRMED > /tmp/vibe_trading_rce_proof_49561.txt", "run_dir": "..."}}
{"type": "tool_result", "iter": 1, "tool": "bash", "status": "ok", "elapsed_ms": 4, "preview": "{\"status\": \"ok\", \"exit_code\": 0, \"stdout\": \"uid=1000(vscode) gid=1000(vscode) groups=1000(vscode),969(969)\\nvscode\\n\", \"stderr\": \"id: cannot find name for group ID 969\\n\"}"}
Vulnerable vs fixed comparison:
| Request | v0.1.9 (vulnerable) | v0.1.10 (fixed) |
|---|---|---|
POST /sessions (DNS-rebound, no auth) |
201 (auth bypassed) | 403 ("Untrusted local API host") |
POST /sessions/{id}/messages (DNS-rebound) |
200 → BashTool RCE | 403 |
PUT /settings/llm (DNS-rebound) |
422 (auth bypassed) | 403 |
POST /sessions (normal loopback Host: 127.0.0.1) |
201 | 201 (no regression) |
Environment:
- Python 3.14.4, FastAPI + uvicorn, langchain-openai (ChatOpenAI)
- Vibe-Trading v0.1.9 (vulnerable) / v0.1.10 (fixed)
- API server bound to
0.0.0.0:8899(default), dev mode (noAPI_AUTH_KEY) - DNS rebinding server:
dnslibUDP server on127.0.0.1:5353 - Mock LLM:
http.serveron127.0.0.1:9099returning OpenAI-compatible tool-call responses
Recommendations / Next Steps
- Upgrade to v0.1.10 or later — the
_reject_untrusted_loopback_hostmiddleware (PR #242) blocks DNS-rebinding Host headers on loopback peers with HTTP 403. - Set
API_AUTH_KEYin any deployment exposed beyond a single-user localhost — this forces bearer-token auth even for loopback clients when configured, removing the dev-mode bypass. - Set
VIBE_TRADING_ENABLE_SHELL_TOOLS=1only when explicitly needed — PR #243 requires explicit opt-in for the BashTool; without it, the RCE primitive is unavailable even if auth is bypassed. - Bind to
127.0.0.1instead of0.0.0.0when the API is not meant to be network-accessible. - Configure
API_ALLOWED_HOSTSif fronting the local API with a specific loopback hostname. - Testing: run the security regression tests:
PYTHONPATH=agent python -m pytest agent/tests/test_security_auth_api.py -q
Additional Notes
- Idempotency: The script was run twice consecutively; both runs produced identical results (HTTP 201 + RCE on vulnerable, HTTP 403 on fixed). The script cleans up all background processes on exit and uses a PID-suffixed proof file to avoid collisions.
- Mock LLM justification: The reproduction uses a mock OpenAI-compatible LLM server that returns a
bashtool-call. This simulates the attack scenario where the attacker either (a) poisons the LLM settings via the auth-bypassed/settings/llmPUT to point at an attacker-controlled LLM, or (b) sends a user message that the agent's LLM responds to with a bash tool-call. The BashTool'ssubprocess.run(shell=True)is the actual RCE primitive in both cases — the mock LLM merely drives the agent loop to reach it. - The
PUT /settings/llmreturned 422 (not 200) on the vulnerable version because the request body did not match the full Pydantic model schema, but the important point is that the request passed authentication (no 401/403) — the auth bypass is confirmed. On the fixed version, the same request returns 403 at the Host-gate middleware before reaching the route handler. - CORS does not defend against DNS rebinding — after rebinding, the attacker's hostname becomes the browser's same origin, so credentialed CORS requests are sent without cross-origin restrictions.
CVE-2026-58169 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
cd /data/pruva/project-cache/b9cd83d4-18cf-44e8-8cea-3367d315f18e && git clone --quiet https://github.com/HKUDS/Vibe-Trading.git repo 2>&1 | tail -5; echo "EXIT: $?"; ls repo 2>/dev/null | headEXIT: 0 AGENT_CONTRIBUTOR_GUIDE.md CHANGELOG.md CODE_OF_CONDUCT.md CONTRIBUTING.md Dockerfile LICENSE MANIFEST.in
Artifacts and Evidence for CVE-2026-58169
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-58169
FAQ: CVE-2026-58169
How does the CVE-2026-58169 exploit chain reach RCE?
Which Vibe-Trading versions are affected by CVE-2026-58169, and where is it fixed?
How severe is CVE-2026-58169?
How can I reproduce CVE-2026-58169?
References for CVE-2026-58169
Authoritative sources for CVE-2026-58169 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.