CVE-2026-58169: Verified Repro With Script Download
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://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
Partial bypass of the v0.1.10 Host-validation fix (PR #242): the production loopback-Host allowlist _DEFAULT_LOOPBACK_HOSTS includes the non-loopback test hostname 'testserver', so a loopback peer with Host: testserver:<port> and no bearer token is auth-bypassed on the fixed v0.1.10 (POST /sessions -> 201). In the def…
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.
Variant Analysis & Alternative Triggers for CVE-2026-58169
A partial bypass of the v0.1.10 Host-validation fix (PR #242) was confirmed:
the production loopback-Host allowlist _DEFAULT_LOOPBACK_HOSTS includes
"testserver" — a non-loopback hostname added for Starlette TestClient
convenience. A loopback peer request carrying Host: testserver:<port> and no
bearer token passes the _reject_untrusted_loopback_host middleware and is
auth-bypassed on the fixed v0.1.10 (POST /sessions → HTTP 201), exactly
where an attacker-domain Host is correctly rejected (HTTP 403). In the default
configuration the independent shell-tools opt-in (PR #243) still blocks the
BashTool RCE primitive, so the partial bypass does not yield RCE by
default. However, when the operator has enabled VIBE_TRADING_ENABLE_SHELL_TOOLS=1
(the exact scenario the opt-in exists for), the testserver Host bypass
restores the full RCE chain on the fixed v0.1.10 — demonstrated end-to-end
(Host: testserver → session → agent message → BashTool
subprocess.run(shell=True) → RCE_CONFIRMED). This is a conditional bypass:
in the standard DNS-rebinding model the attacker cannot deliver
Host: testserver (they control an attacker-owned domain, not the bare label
testserver), so the standard DNS-rebinding RCE bypass is not confirmed.
The finding is a genuine incompleteness in the fix's allowlist that should be
closed.
Fix Coverage / Assumptions
Invariant the fix relies on (PR #242): a loopback peer may carry only a
Host that names a loopback address (or an operator-configured
API_ALLOWED_HOSTS name); any other Host on a loopback peer is a DNS-rebinding
artifact and is rejected with HTTP 403 before route handlers run.
Code paths explicitly covered: every HTTP request passes through
_reject_untrusted_loopback_host (@app.middleware("http"),
agent/api_server.py:562). Loopback peers with a Host outside
_DEFAULT_LOOPBACK_HOSTS ∪ _EXTRA_LOOPBACK_HOSTS are rejected. Non-loopback
peers pass through to per-route _validate_api_auth, which requires
API_AUTH_KEY for non-local access. Secondary defenses: PR #243 gates
bash/background_run behind VIBE_TRADING_ENABLE_SHELL_TOOLS; PR #245
requires a bearer token for settings writes.
What the fix does NOT cover: the allowlist trusts "testserver", a
non-loopback test hostname. This violates the fix's own invariant and lets a
loopback peer with Host: testserver be auth-bypassed on the fixed version.
Variant / Alternate Trigger
Variant ID: VT-CVE-2026-58169-testserver-host-allowlist
Entry point: POST /sessions/{id} and POST /sessions/{id}/messages over
the Vibe-Trading API (agent/api_server.py), reached by a loopback peer
(127.0.0.1) sending Host: testserver:<port> with no Authorization bearer
header. curl --resolve testserver:<port>:127.0.0.1 ... reproduces the request
shape a victim browser would produce when the victim's resolver maps
testserver → 127.0.0.1.
Code path (fixed v0.1.10):
_reject_untrusted_loopback_host(api_server.py:562):_is_local_client()True;_is_allowed_loopback_host("testserver:18899")→_host_without_port→"testserver"∈_DEFAULT_LOOPBACK_HOSTS→ True; middleware passes (no 403).require_auth→_validate_api_auth(api_server.py:760):_is_local_client()True → returns immediately (no auth).POST /sessions(api_server.py:1801) → HTTP 201, session created.POST /sessions/{id}/messages(api_server.py:2094) → agent loop._shell_tools_enabled_for_request(api_server.py:844): returns_env_shell_tools_enabled(). Default: False (bash disabled). WithVIBE_TRADING_ENABLE_SHELL_TOOLS=1: True →bashtool registered.- Mock/attacker LLM returns a
bashtool-call →BashTool.execute(agent/src/tools/bash_tool.py) →subprocess.run(command, shell=True)→ RCE.
Other candidates tested and ruled out:
Attacker-domain Host (
rebind.attacker.test): fixed v0.1.10 → 403 (fix works). This is the control, not a bypass.Direct cross-origin fetch to
localhost:Host: localhostis allowlisted by design; cross-origin browser requests are CORS-limited (loopback-only origins, no*with credentials), so the session_id cannot be read to drive RCE. Not a bypass.Unauthenticated routes (
/health,/api,/skills,/correlation): non-sensitive / read-only; do not reach the auth-bypass or RCE sink.WebSocket / SSE transports: no
@app.websocketendpoints; SSE streams go through the Host middleware then_validate_api_auth.Non-shell file tools (
write_file,edit_file): not gated by the shell-tools opt-in, so they could overwriteagent/.envonce an auth bypass exists — a secondary gap, not an independent bypass.Package/component affected:
HKUDS/Vibe-Trading—agent/api_server.py(_DEFAULT_LOOPBACK_HOSTS/_reject_untrusted_loopback_hostallowlist),agent/src/tools/bash_tool.py(BashTool RCE primitive),agent/src/tools/__init__.py(_SHELL_TOOL_NAMESgating).Affected versions (as tested):
- Vulnerable baseline: v0.1.9, commit
8cebccb9b301f962d11bf0fa8c6be9bf7d7e12f2. - Fixed target under test: v0.1.10, commit
54f944743efee7b9cbaaeafb5966c583d0d4ac66.
- Vulnerable baseline: v0.1.9, commit
Risk level: High for the conditional full-bypass scenario (opted-in shell tools +
testserverresolves to loopback): unauthenticated RCE as the API process user. Medium for the default-config partial bypass: unauthenticated session creation / control-plane access without RCE.Consequences: unauthenticated session creation and agent control on the fixed version via
Host: testserver; full RCE when shell tools are opted in.
Impact Parity
- Disclosed/claimed maximum impact (parent CVE): DNS rebinding → authentication bypass → remote code execution (code_execution) and credential exfiltration.
- Reproduced impact from this variant run:
- Auth bypass on fixed v0.1.10 via
Host: testserver→ HTTP 201 (confirmed). - Full RCE on fixed v0.1.10 only in the opted-in shell-tools config
(
VIBE_TRADING_ENABLE_SHELL_TOOLS=1):BashToolexecutedid; whoami; echo RCE_CONFIRMED > /tmp/...; proof file written (RCE_CONFIRMED); runstate.json={"status":"success"}. - In the default fixed config, the auth bypass does not escalate to RCE (bash gated by PR #243).
- Auth bypass on fixed v0.1.10 via
- Parity:
partial. The auth-bypass half of the CVE is reproduced on the fixed version; the RCE half is reproduced only under the non-default opted-in-shell-tools condition, and the standard DNS-rebinding delivery ofHost: testserveris not confirmed. - Not demonstrated: a fully unattended, standard-DNS-rebinding RCE bypass on
the fixed version (the attacker cannot deliver
Host: testserverfrom an attacker-owned domain), and credential exfiltration via settings writes on the fixed version (PR #245 blocks it).
Root Cause
The same underlying bug — loopback peer-IP trust accepted without a
sufficiently strict Host validation — is still reachable because the fix's
allowlist (_DEFAULT_LOOPBACK_HOSTS) over-trusts a non-loopback name,
"testserver". The fix's middleware correctly rejects attacker-domain Hosts,
but the allowlist membership test normalized in _DEFAULT_LOOPBACK_HOSTS
returns True for testserver, so a loopback peer bearing that Host is treated
as a trusted local caller and skips _validate_api_auth. The BashTool sink
(subprocess.run(shell=True)) is then reachable when shell tools are opted in.
Fix commit (the Host middleware): PR #242, commit 6e06017
(6e06017... in v0.1.9..v0.1.10). The gap lives in the
_DEFAULT_LOOPBACK_HOSTS frozenset at agent/api_server.py:483.
Reproduction Steps
- Script:
bundle/vuln_variant/reproduction_steps.sh— self-contained. Run withPRUVA_ROOT=<bundle_dir> bash bundle/vuln_variant/reproduction_steps.sh. - What the script does:
- Ensures Python deps; creates/uses isolated git worktrees
(
bundle/artifacts/vt-vuln@ v0.1.9,bundle/artifacts/vt-fixed@ v0.1.10) so the repro cache clone is not mutated. - Starts a mock OpenAI-compatible LLM that returns a
bashtool-call. - Phase 1 (v0.1.9 baseline): for Hosts
rebind.attacker.test,testserver,localhost→POST /sessions(all 201, no Host check); full RCE chain via attacker Host (control). - Phase 2 (v0.1.10 default): attacker Host → 403 (fix works);
testserverHost → 201 (partial bypass);localhost→ 201 (baseline);testserver+ message → no RCE (bash gated by opt-in). - Phase 3 (v0.1.10 opted-in,
VIBE_TRADING_ENABLE_SHELL_TOOLS=1): attacker Host → 403 (Host middleware still blocks DNS rebinding);testserverHost → 201 → message → BashTool RCE CONFIRMED. - Logs every attempt; exits 1 (no standard DNS-rebinding RCE bypass) but records the partial/conditional bypass.
- Ensures Python deps; creates/uses isolated git worktrees
(
- Expected evidence:
logs/vuln_variant/fixed_default_host_testserver.txt→ HTTP 201 (auth bypass on fixed).logs/vuln_variant/fixed_default_host_rebind.attacker.test.txt→ HTTP 403 (fix blocks standard DNS rebinding).logs/vuln_variant/fixed_optedin_host_testserver.txt→ HTTP 201.logs/vuln_variant/fixed_optedin_ts_rce_proof.txt→RCE_CONFIRMED(full chain on fixed, opted-in config).logs/vuln_variant/fixed_optedin_ts_state.json→{"status":"success"}.logs/vuln_variant/vuln_rce_proof.txt→RCE_CONFIRMED(v0.1.9 control).
Evidence
All artifacts under bundle/logs/vuln_variant/ and bundle/vuln_variant/:
| Artifact | Description |
|---|---|
logs/vuln_variant/variant_steps.log |
Full script execution log (both runs). |
logs/vuln_variant/vuln_host_{attacker,testserver,localhost}.txt |
v0.1.9: all Hosts → 201 (no Host check). |
logs/vuln_variant/vuln_rce_proof.txt |
v0.1.9 control RCE: RCE_CONFIRMED. |
logs/vuln_variant/fixed_default_host_rebind.attacker.test.txt |
Fixed default: attacker Host → 403. |
logs/vuln_variant/fixed_default_host_testserver.txt |
Fixed default: testserver Host → 201 (partial bypass). |
logs/vuln_variant/fixed_default_host_localhost.txt |
Fixed default: localhost → 201 (baseline). |
logs/vuln_variant/fixed_default_ts_msg.txt |
Fixed default: testserver message → no RCE (bash gated). |
logs/vuln_variant/fixed_optedin_host_rebind.attacker.test.txt |
Fixed opted-in: attacker Host → 403. |
logs/vuln_variant/fixed_optedin_host_testserver.txt |
Fixed opted-in: testserver Host → 201. |
logs/vuln_variant/fixed_optedin_ts_msg.txt |
Fixed opted-in: testserver message → triggers bash. |
logs/vuln_variant/fixed_optedin_ts_rce_proof.txt |
Fixed opted-in: RCE_CONFIRMED (full chain on fixed). |
logs/vuln_variant/fixed_optedin_ts_state.json |
Run state: {"status":"success"}. |
logs/vuln_variant/api_server_{vuln,fixed-default,fixed-optedin}.log |
API server logs per phase. |
vuln_variant/mock_llm_server.py |
Mock OpenAI LLM returning a bash tool-call. |
vuln_variant/reproduction_steps.sh |
Variant reproduction script. |
Key excerpt — fixed v0.1.10 default, testserver Host:
Host=testserver
HTTP 201
{"session_id":"0d6dac8dad41","title":"","status":"active",...}
Key excerpt — fixed v0.1.10 default, attacker Host (control):
Host=rebind.attacker.test
HTTP 403
{"detail":"Untrusted local API host"}
Key excerpt — fixed v0.1.10 opted-in RCE:
[08:29:23] *** fixed-optedin testserver RCE CONFIRMED: RCE_CONFIRMED ***
state.json: {"status":"success"}
Environment: Python 3.14.4, FastAPI + uvicorn, langchain-openai; isolated
git worktrees at v0.1.9 (8cebccb) and v0.1.10 (54f9447); API bound to
0.0.0.0:18899 (dev mode, no API_AUTH_KEY); mock LLM on 127.0.0.1:19099.
Recommendations / Next Steps
- Remove
testserverfrom the production allowlist. Move it to a test-only override (e.g. an env flagVIBE_TRADING_TEST_HOSTSconsulted only when testing, or inject it inconftest.pyviaAPI_ALLOWED_HOSTSfor the TestClient). Production_DEFAULT_LOOPBACK_HOSTSshould contain only genuine loopback names:localhost,127.0.0.1,::1,[::1]. This closes the partial bypass with no production behavior change (no real client sendsHost: testserver). - Tighten the allowlist semantics. Consider validating that the Host, after port stripping, resolves to a loopback address (or is an explicit operator entry) rather than trusting fixed bare labels — defense in depth against future allowlist drift.
- Gate file-mutating agent tools (
write_file,edit_file,skill_writer) behind the sameVIBE_TRADING_ENABLE_SHELL_TOOLSopt-in (or a separateVIBE_TRADING_ENABLE_FILE_TOOLS), so an auth-bypassed agent cannot overwriteagent/.envto redirect credentials, bypassing PR #245's settings-write gate. - Keep the shell-tools opt-in default-off (PR #243) — it correctly prevents
the
testserverpartial bypass from escalating to RCE in the default configuration. Document that operators who enable shell tools must not expose the API to environments wheretestserverresolves to loopback. - Bind defaults: consider changing the default
--hostfrom0.0.0.0to127.0.0.1so a fresh dev deployment is not network-reachable by default.
Additional Notes
- Idempotency: the script was run twice; both runs completed without crashing and produced identical verdicts (exit 1; partial auth-bypass = yes; opted-in RCE = yes; standard DNS-rebinding bypass = no). It reuses existing worktrees, frees ports between phases, and cleans up on exit.
- No repo state mutation: the repro cache clone
(
/data/pruva/project-cache/.../repo) remained at v0.1.10 (54f9447) throughout; all version switching used isolated git worktrees underbundle/artifacts/. - Honest scope: this is reported as a partial/conditional bypass, not a
confirmed standard DNS-rebinding RCE bypass, because
Host: testserveris not deliverable from an attacker-owned domain via normal DNS rebinding. The finding is nonetheless a real fix-completeness defect (an over-broad production allowlist) that restores the full chain under a realistic operator-opted-in configuration.
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.