AI serving and RAG platform wave — unauthenticated KV-cache data plane, tenant-blind secondary routes, and validate-endpoint code sinks¶
Source: GitHub Security Advisory wave published 2026-10-02 00:31Z (Mooncake, RAGFlow, Langflow, Langchain-Chatchat, Devika). Related existing pages: Oct 1 MCP trust-layers cluster, Sept 16 vLLM route-guard parity page, Aug 26 DB-GPT upload-route page, May 21 Fission/MLflow/Langflow SSRF page.
One rule covers the wave: the AI stack's internal plumbing (transfer engines, metadata services, per-app API registries, "validate" endpoints) was designed inside a flat trust zone and never got the authz layer the UI routes got. Fingerprint a deployment by its unauthenticated internal ports and secondary route families before touching the login form.
What changed¶
| Advisory | Component | Boundary | Operator value |
|---|---|---|---|
| GHSA-mp8g-r7h2-5x36 / CVE-2026-103764 (9.8) | Mooncake transfer engine < 0.3.13 | ServerSession::readHeader on the TCP transport data port trusts the addr/size fields of a crafted SessionHeader with READ/WRITE opcodes → unauthenticated arbitrary process-memory read/write on the serving node | The disaggregated-inference data plane is a bare TCP protocol with a length/pointer struct as its only "auth". READ opcodes disclose KV cache contents, prompts, and secrets sitting in the serving process; WRITE walks toward code execution. On any inference-cluster foothold (or any pod that can route to the data port), port-scan for the transfer-engine listener and treat it as a memory oracle, not a service |
| GHSA-3mpp-98wg-qvr5 / CVE-2026-103765 (9.4) | Mooncake ≤ 0.3.13.post1 | HTTP metadata server /metadata handler has no authentication: read/overwrite/delete transfer-engine metadata keys, including segment descriptors such as tcp_data_port and rpc_meta entries | The control-plane poisoning twin of the memory bug: rewrite a victim node's segment descriptor and the cluster redirects KV-cache transfers to an attacker-controlled listener — harvest prompts/secrets at scale without ever touching the memory primitive. Any unauthenticated key-value metadata service should be swept for descriptor fields that other nodes' clients consume |
| GHSA-q37m-rx33-792r / CVE-2026-51892 | RAGFlow 0.24.0 | /v1/document/get/<doc_id> — caller-selected doc_id reaches document data access with no owner/tenant binding | Cross-tenant document read by ID enumeration on a deployed RAG platform |
| GHSA-w4g6-3g8p-8mjr / CVE-2026-51894, GHSA-p84p-8f64-q95f / CVE-2026-51893 | RAGFlow 0.24.0 | run_mindmap / trace_mindmap accept a caller-selected object/tenant identifier and reach data access with no visible owner, tenant, workspace, or membership binding | The mindmap/preview feature family is where the per-route authz middleware was never wired — enumerate feature-sidecar routes, not just the main CRUD API |
| GHSA-86mq-h2wm-8hx9 / CVE-2026-51895 | RAGFlow ≤ 0.24.0 | update_metadata_setting (api/apps/kb_app.py) — unauthorized cross-session/privilege-crossing write | Knowledge-base settings are mutable cross-tenant, not just readable |
| GHSA-fjwv-9m73-7c4f / CVE-2026-51896 | RAGFlow 0.25.3 | resume (api/apps/connector_app.py) improper access control | Connector-app routes lag the main app's authz even in newer branches |
| GHSA-p3qm-chr8-gf9x / CVE-2026-51897 | RAGFlow 0.24.0 | get_dataset (api/apps/evaluation_app) — advisory states the exposed entry can lead to attacker-controlled code or command execution | The evaluation app — a benchmarking sidecar almost nobody threat-models — is the escalation leg. On a RAGFlow target, the api/apps/*_app.py registry (main, kb, connector, evaluation) is the authorization-matrix checklist |
| GHSA-qqw9-2vj9-hx7r / CVE-2026-51886 | Langflow ≤ 1.9.3 | POST /api/v1/validate/code forwards raw user-supplied Python into a server-side compile/exec validation path with no entitlement guard | "Validate/preview/lint/test" endpoints in low-code/AI builders are execution sinks by design. An authenticated low-privilege account (or a CSRFable call) is server RCE. Probe every route whose name contains validate/preview/debug and diff what actually runs server-side |
| GHSA-5rwq-2vwh-7g73 / CVE-2026-51888 | Langflow ≤ 1.8.4 | knowledge_bases.create_knowledge_base HTTP POST forwards attacker-controlled path/filename into host file creation with no boundary enforcement → arbitrary file write outside the storage root | Knowledge-base name/path parameters are path components, not just strings — same class as the Chatchat knowledge_base_name items below |
| GHSA-p52p-3gpf-v5f9 / CVE-2026-51882 | Langchain-Chatchat 0.3.x | OpenAI-compatible /v1/files upload: crafted filenames escape the openai_files directory | The OpenAI-compat shim is a second upload implementation that does not share the main endpoint's sanitization. When one upload route is fixed, the compatibility surface is the unfixed sibling |
| GHSA-c4wm-cr62-pcg5 / CVE-2026-51883 | Langchain-Chatchat 0.3.0/0.3.1 | KB creation/upload: ..\ sequences in knowledge_base_name write KB content outside the configured KB root | Configuration-style name fields double as directories — traversal lives in the name, not the file |
| GHSA-c869-r44m-5f4w / CVE-2026-51884 | Langchain-Chatchat 0.3.1 | /knowledge_base/upload_temp_docs crafted filenames write outside the temp directory | Third upload surface in the same product — count the routes, not the bugs |
| GHSA-f262-872p-52hv, GHSA-cpp9-ff82-c7vg, GHSA-5xcr-qppv-2p66 / CVE-2026-51873/51874/51875 | Devika v1.0 | save_code_to_project directory traversal in Coder, Patcher, and Feature agents — same unsanitized sink copy-pasted per agent role → write outside the project workspace | Multi-agent frameworks replicate one write helper per agent class; a fix for the Coder agent does not fix the Patcher/Feature agents. Test every agent role's persistence path independently |
Durable axes¶
- Inference-cluster internal protocols are pre-auth by design — and they carry model secrets. Disaggregated serving (Mooncake and friends) moves KV cache over TCP with no authentication on either the data port or the metadata service. From any network position that reaches those ports (a compromised pod, a tenant VM, a flat VPC), you get (a) a process-memory read primitive over the transfer opcodes and (b) a descriptor-poisoning primitive that redirects other nodes' transfers to you. Sweep AI infra for non-HTTP listeners first: transfer-engine data ports, metadata/RPC bootstrap services, RDMA/TCP rendezvous endpoints. Evidence value here is prompt/secret disclosure from synthetic sessions only.
- Poison the control plane instead of touching the data plane. Writing one metadata key (
tcp_data_port,rpc_meta) is quieter and scales better than exploiting a memory bug: the cluster delivers victim traffic to your listener. For any unauthenticated KV/metadata service, dump the schema and grep the values for host:port fields consumed by other components. - Per-app route registries drift independently in RAG platforms. RAGFlow ships separate Flask-style apps (
main,kb_app,connector_app,evaluation_app); authz middleware applied to one does not cover the others. On source-available deployments, enumerate every*_app.py/router module and build the authorization matrix per module; on closed deployments, probe feature-sidecar families (evaluation, connector, mindmap, preview, temp upload) with two disposable tenants and cross-referenced IDs. - Caller-selected identifiers on data-access routes are the standing IDOR oracle.
doc_id,dataset_id,session_id,bot_id,book_id— enumerate ID-bearing params on every read and write route and replay tenant B's ID under tenant A's token. RAGFlow's six-GHSA spread is what a full sweep of one product looks like: the fix list is the route list. - "Validate" endpoints in low-code AI builders execute. Langflow's
/validate/codecompiles/execs submitted Python server-side. Authenticated ≠ authorized: map what a minimal-privilege account can make the server run, with inert marker statements in a lab instance. - Compatibility shims and temp routes are unfixed siblings of fixed routes.
/v1/files(OpenAI-compat),upload_temp_docs, KB-creation name fields — the filename/path sanitizer applied to the primary upload is re-implemented (or absent) on each alternate surface. Grep the codebase for every parameter that reachesopen(...,'w')/os.path.join, per the fixed-handler-is-the-map rule.
Validation boundaries¶
Mooncake checks belong in a lab inference cluster with synthetic prompts and marker payloads — never against shared serving nodes, and never read real KV caches or other tenants' prompts. RAG-platform IDOR proofs use two disposable tenants, synthetic documents/datasets, and cross-read checks only; Langflow code-execution proofs use inert marker statements in disposable flows on owned instances. Chatchat/Devika/KB-name traversal proofs write marker files only inside disposable roots. No production tenant data, real model outputs, or customer embeddings in wiki/report evidence.
Tracked from this wave without publication¶
- Mooncake memory-exhaustion + DoS pair (GHSA-62mv-x7jm-w9f4 / CVE-2026-103761, GHSA-mmqv-rpx9-mqhx / CVE-2026-103760) — availability-only siblings of the promoted pair, folded into the same fingerprinting context.
- 389-ds-base StartTLS plaintext-buffer injection (CVE-2026-86345) — folded on the Sept 20 grammar-desync page; worker-pool exhaustion DoS (CVE-2026-86344) tracked (connection-hold class).
- PictShare raw-metadata
infoendpoint +rand()delete_code (CVE-2026-104051, CVE-2026-104356) — folded on the Sept 18 predictable-token page. - DeepTutor Oct 2 siblings (WebSocket ExecTool, EditFileTool traversal, session/bot/book IDOR) — folded on the Oct 1 MCP cluster page; Office-PowerPoint-MCP-Server
output_pathtraversal (GHSA-xpvr-6r3p-gm34) likewise. - ClipBucket v5 SQLi (CVE-2026-103766) single; Johnson Controls EasyIO/Neo credential+cmdi pack (established appliance class); 2009-era CVE backfill batch (Green Dam, ContentKeeper, Belkin Bulldog, Dogfood CRM) — historical GHSA re-publication, no live surface.
October 2 15:31Z follow-up: GitLab AI Gateway flow-config sandbox escape + agent-framework code-injection re-surfacing wave¶
- GitLab AI Gateway prompt-template sandbox escape → command execution on the gateway (GHSA-5295-vp56-jghq / CVE-2026-90970, 9.9): an authenticated user with Duo Agent Platform access escapes the prompt-template sandbox via a specially crafted flow configuration, yielding arbitrary command execution on the AI Gateway (affected 18.1.6→<19.2.4, 19.3→<19.3.2, 19.4→<19.4.1). Operator value: the escape vector is the flow/config artifact itself, not prompt text — on any agent platform where low-tier users author flows, graphs, or pipeline descriptors, treat the configuration format as a code-execution surface and fuzz schema edges (nested fields, embedded expressions, template directives) rather than only prompt injection. The privilege floor is "can access the agent platform," not admin — verify on lab/self-hosted GitLab whether Duo Agent Platform entitlements are granted broadly; prove escape only with inert marker commands in an owned instance. Same shape as the Oct 2 Langflow validate-code RCE and the Sept 16 route-guard-parity family: the AI feature's authoring surface gets less sandbox rigor than its UI sibling.
- Agent-framework code-injection re-surfacing (updated 15:32Z, published Sept 29–30, previously unprocessed): Devika
Runner.run_codecode injection (GHSA-pxxm-r9vp-9cmm), camel-aiCodeExecutionToolkitrunning model-produced code (GHSA-q2f7-8vfc-xg45), AgentDesk browser-tools-mcp path handling (April-dated re-surfacing), agentgpt API-router access control (GHSA-m2qp-332m-87w4), AgentScopeinsert_text_filepath traversal (GHSA-pwqj-xpq4-649r), bisheng backend code injection (GHSA-4w4g-gc64-8m74). Shared audit predicate across all six: model- or user-produced strings reachingexec/eval/file APIs with no deny-boundary between the generation step and the execution step. When fingerprinting an agent stack in an authorized assessment, enumerate its built-in tool names (run_code,CodeExecutionToolkit,insert_text_file, file-write tools) — the toolkit registry is the attack-surface map, and prompt-injection-to-RCE claims stand or fall on whether that registry has a sandbox, allowlist, or approval gate. Proofs stay in owned containers with marker files; never execute host-visible or persistent payloads. - Tracked from this wave without publication: AWS EFS-volume argument-delimiter injection in volume handling (GHSA-9w9f-p9qm-rcm4, local mount-argument class, no remote axis published); Chrome WakeLock/WebView/Bluetooth missing-authorization singles (browser-desktop class, no operator workflow); remaining Linux-kernel patch backfill.
- Apache Thrift second bindings wave at 15:31Z (
hfqq-j57v-2292protocol-skip recursion miss across bindings, plus ~20 allocation/recursion/overflow per-binding GHSAs in go/php/python/ruby/java/lua/c_glib/delphi/perl) — continuation of the Oct 1 12:31Z 17-GHSA Thrift pack already tracked: fingerprint the specific binding, not "Thrift," before any protocol-fuzz engagement; the skip-routine recursion miss means fuzz depth limits must be enforced client-side per binding. Still no single promotable operator workflow beyond the fingerprint rule.
October 2 18:31Z follow-up: LocalAI unauthenticated gallery SSRF + the agent-framework code-injection second wave¶
- LocalAI
POST /models/applyunauthenticated SSRF through gallery URLs (GHSA-8c5q-hx4g-qq23 / CVE-2026-59707, 8.6, fixed after 2026-07-03): the endpoint passes unsanitized gallery URL fields straight into the gallery-config fetcher, forcing the server to issue HTTP GETs to private and loopback ranges with partial response content leaked through error messages. This is the LocalAI instance of the standing AI-infra rule: the model-fetch surface (gallery URIs, model download endpoints,modelparams on generation routes) is an SSRF channel that ships unsanitized because it is conceived as an admin convenience, not an attack surface. Recon/proof pattern for any exposed LocalAI-compatible server: enumerate model-apply/download/install-style routes first (they exist on every OpenAI-compat fork), point the gallery URL at an owned callback, and read the error-message reflection — partial-body leak in errors makes even a blind-looking fetch a read primitive. Never pivot the fetch at internal production services beyond an owned canary in authorized scopes. - Agent-framework code-injection second wave (published 18:31Z) — same audit predicate as the 15:31Z re-surfacing pack, six more products, three shapes:
- Built-in tool executes directly: pandas-ai
CodeExecutor.execute(GHSA-whj7-cr8f-v2cc / CVE-2026-51898), agentscopeexecute_shell_command(GHSA-cj2c-23cc-r2qj / CVE-2026-51922), FinRobotCodingUtils.create_file_with_code/modify_code(GHSA-xfxm-q6r8-5xm6 / CVE-2026-51918, GHSA-rhq5-3cmm-qrj2), vannaVannaBase.get_plotly_figure(GHSA-349g-39fc-mvhj / CVE-2026-51911) — generated code/Plotly spec reachingexec/evalunconditionally. The toolkit-name enumeration rule holds:CodeExecutor,execute_shell_command,create_file_with_code,get_plotly_figureare the map. - Controller endpoints with no cross-tenant check: SuperAGI seven-GHSA cluster — agent controller CRUD accepts
project_idwithout verifying org ownership (GHSA-q43w-q4wm-h8j4 / CVE-2026-51899), plus agent-execution controller access drift (GHSA-mv3w-9m62-vw4g, GHSA-63jh-v3gf-wqcq / CVE-2026-51901/51904) and tool/agent access-control singles (CVE-2026-51914/51915/51916). Caller-selectedproject_idas the standing IDOR oracle, applied to agent-management routes: in any multi-tenant agent platform, cross-tenant agent create/schedule/delete means you can task another tenant's agent (their tools, their credentials) — test with two disposable orgs and a marker tool. - Plugin save-paths traverse: TaskingAI DALL-E and QR-code plugins'
save_url_image/save_base64_imagepath traversal (GHSA-w3f3-w8fw-rqq5, GHSA-rprh-3hxw-c233 / CVE-2026-51906/51907) — every plugin that saves a generated artifact re-implements (or skips) the filename sanitizer; the per-plugin save path is its own boundary, same per-agent-role Devika rule. - Tracked from this wave without publication: ruby_llm polynomial ReDoS (CVE-2026-67989, availability-only); image-downloader 4.3.0 URL-controlled path-traversal write (GHSA-h4gg-43m2-62cm, 9.1, detail-free — revisit if sink detail lands); Apache OpenOffice Java-integration code execution (GHSA-55r6-rg38-gm55, document-open class, no remote axis); Apache Traffic Server GHSA-r73v-c3ch-65gj / CVE-2026-102795 (9.3, generic "improper access control" superseding CVE-2026-41920 — per the June 11 ATS rule, confirm the byte-level differential before classing it; the July 29 four-recorder lab applies once details land); Meari IoT OpenAPI device-shadow read + config-write IDOR (GHSA-9m6x-pxxf-689m 6.5, GHSA-xj5r-68hv-vxqx 7.7, no ownership check on device ID — standard device-cloud IDOR, appliance class tracked).
October 2 19:31Z follow-up: Trigger.dev control-plane SSRF + loader-vs-action authz parity¶
- Trigger.dev webhook alert channel = signed SSRF proxy from any org member (GHSA-xxv7-2vv3-h682, 7.7, fixed 4.5.2): the alert-channel
urlis stored as a barez.string()(not even.url()) and the control plane POSTs to it via rawfetch()with no private-IP/link-local/metadata/redirect filtering anywhere in the webapp — repository-wide search for an SSRF guard returns nothing. Creating the channel requires only organization membership, not admin. The attacker then fails a task run in their own environment and the delivery job POSTs a validly HMAC-signed payload tohttp://169.254.169.254/...or internal-only admin services. Durable axes: (1) outbound-notification surfaces (webhooks, alert channels, integrations) are SSRF channels that ship unsanitized because they are conceived as convenience, not attack surface — same shape as the LocalAI gallery leg above and the Faraday/9Router outbound-fetch family; (2) the signature makes it worse, not better — a server-originated, validly-signed request is indistinguishable from legitimate platform traffic to the receiving internal service, so perimeter allowlisting of the platform's egress does not stop it; (3) blind-SSRF oracle still applies: response body isn't returned, but status/latency/error deltas in delivery logs/state give a probe channel. Bug-hunt pattern: on any SaaS with user-configurable webhook/callback URLs, check whether the creation route's schema constraint matches the delivery sink's checks, and whether the lowest membership tier (not admin) can create the channel. - Trigger.dev run-replay cross-tenant environment injection (GHSA-qxpp-qjg8-x4jv, 7.1, fixed 4.5.2): the replay action member-scopes the source run correctly, but takes the target
environmentIdverbatim from the form body; the lookup (findEnvironmentById) filters byidonly — no org/project/membership predicate — and the downstream task service trusts the resolved environment blindly. The replay loader constrains the environment picker to the run's own project, but the action never re-imposes it. This is the crisp loader-vs-action parity drift: UI-gated constraints are not server-side controls; the picker's WHERE clause living in a different module than the submit handler is the audit predicate. Sweep every mutation route whose GET/loader scopes a selector and ask whether the POST repeats the scope. Impact shape: authenticated user injects task runs into another tenant's environment (including production), burning their queue concurrency/compute and poisoning run history — cross-tenant write that needs only a target CUID plus a task identifier that exists there. Proof pattern: two disposable orgs, one owned run, victim environment id from the second org; assert run creation lands in the victim environment, then delete the marker runs. - Shared fingerprint note: Trigger.dev is the async-jobs/agent-task control plane under a growing slice of AI products; where a deployment self-hosts the webapp, both legs (alert-channel SSRF, replay environment drift) apply at
<4.5.2with the same low privilege floor — org membership.
October 2 22:3xZ follow-up: Trigger.dev full control-plane tier + Vibe-Trading unauthenticated agent stack + LLM-proxy CSWSH¶
Trigger.dev second tier (7 more GHSAs, published 22:35–22:45Z — same control plane as the 19:31Z pair above)¶
- Self-host default secrets = unauthenticated infrastructure compromise (GHSA-pqxw-g93w-hj9x, high):
hosting/docker/.env.exampleships hardcoded SESSION_SECRET / MAGIC_LINK_SECRET / ENCRYPTION_KEY / MANAGED_WORKER_SECRET;MAGIC_LINK_SECRETfeedsremix-auth-email-linkAES token creation withvalidateSessionMagicLinkdefaulting to false, and accounts auto-create whenWHITELISTED_EMAILSis unset — copy the shipped example, forge a magic link for any email, land in admin with no email delivery. Fingerprint: any self-hosted product whose.env.examplecarries hex values that look real (not placeholders) — copy-paste deploy = unauth admin by design. Backing services (Postgres/Redis/ClickHouse) additionally sit unauthenticated on the compose network. - Hardcoded Socket.IO coordinator secret + decrypted-env exfil handler (GHSA-gg6r-gp4c-89hp, critical):
COORDINATOR_SECRET/PROVIDER_SECRETdefault to"coordinator-secret"/"provider-secret"inenv.server.tsand are not documented in self-hosting docs, .env.example, or helm values — the override exists only in source, so operators who didn't read source ship the default./coordinatormounts on every webapp boot; plain string compare;READY_FOR_EXECUTIONreturns the run's decrypted env-var dictionary for any internal runId, cross-tenant (V1/marqs deployments fully exposed). The sibling handlers (TASK_RUN_COMPLETED,CHECKPOINT_CREATED,CREATE_WORKER…) accept attacker-supplied writes against arbitrary runs with no caller check. Operator rule: Socket.IO/socket.io namespaces are a second transport to enumerate with a socket client, not a browser — auth model, default secrets, and per-event authorization all differ from the REST surface (transport-parity rule from the UTMStack page again). - Cross-tenant SQLi in the TSQL→ClickHouse compiler (GHSA-9q4r-4842-93vw, 7.7): the query compiler parameterizes strings, escapes identifiers, allowlists function names — except the window-function name, concatenated raw into the SQL. A backtick-quoted identifier is lexed, unescaped, and stripped, so a whole subquery becomes the "function name" and lands outside the injected per-tenant
WHEREguard → any authenticated customer reads every tenant's analytics. Durable axes: (1) in a DSL/SQL compiler, enumerate every AST node whose name/identifier path skips the printer's escape/allowlist helper — one uncovered node type is the whole bypass (same grammar-coverage rule as the Sept 18 ORM query-grammar page); (2) tenant guards attached to the outer query authorize nothing inside injected subqueries. Probe any SQL-DSL endpoint by putting an expression where an identifier is expected. - Environment-scoping drift, two more spellings (GHSA-4672-hwv6-gq62 deployment cancel scoped by
projectIdonly — the GET route 404s a foreign prod deployment while the cancel mutation on the same id succeeds; GHSA-59h8-w5q6-mfmpPOST /realtime/v1/streams/:runId/:streamIdhas no auth at all ("backwards compatibility with older clients" comment) and no environment scoping → unauthenticated data injection into any run's stream given a friendlyId, which are sequential-ish (run_+ short hex). Loader-vs-action parity now has a GET-vs-mutation twin: read route scoped, mutation route unscoped is a standing diff to run on every resource family. The "backwards compatibility" code comment is itself a fingerprint for intentionally unauthenticated legacy routes — grep for it. - CLI debug log secret disclosure (GHSA-fj2x-mqqp-3v2w, 5.5):
deploy --dry-run --log-level debugserializes the whole build-worker options object includingenvVarsplaintext, while the non-debug listing correctly hides values. For red-team/C2 operators: developer debug transcripts, CI debug logs, and support bundles are secret-bearing artifacts; for audits: check the debug serializer against the user serializer.
Vibe-Trading: what a shipped AI-agent stack looks like when nobody gates it (3 GHSAs, two CVSS 10.0)¶
- Unauthenticated API + root container + RCE chain (GHSA-v2f8-6655-7grj, 10.0): shipped
.env.exampleleavesAPI_AUTH_KEYcommented out andrequire_auth()returnsNoneimmediately when unset → every endpoint is unauthenticated; Dockerfile has noUSERdirective → API runs as uid 0; compose binds0.0.0.0:8899. Plus read-side authz gap persisting even with the key set, unauthenticated.py/.shfile-write to a server-returned path, default-permissive CORS combined with a loopback-only check, and partial key disclosure via_mask_secret()(masker shape gap again). - LLM-callable tool registry = unfiltered RCE surface (GHSA-jqmf-mx4f-hfr6, 10.0):
BashToolpasses LLM-emitted commands tosubprocess.run(shell=True)with zero filtering;BackgroundRunToolis its async twin; backtest loading runsexec_module()top-level statements before the class check (an RCE path that doesn't match BashTool signatures);read_urlis unguarded SSRF; Jinja2 codegen templates ship autoescape off. Registration is unconditional — no operator opt-in gates the registry. - File-read tools whose "safe path" envelope is the whole home dir (GHSA-5rmq-chc7-m22f, 7.5):
safe_user_path()accepts anything underPath.home()orPath.cwd()— in a root container that's/root(id_rsa,.aws/credentials,.kube/config) and/app; a second toolread_document()has no sandbox call at all and reads/etc/shadow,/proc/self/environ. F9 and F10 have different fix scopes — one function missing the call, one wrong envelope definition; test the guard's bound, not the guard's existence. - Durable rules: (1) the tool registry is the attack-surface map (Oct 2 AI page rule) — in default config every registered tool is reachable by anonymous prompt OR by prompt-injection into any document the agent summarizes; the advisory states the injected-
SYSTEM:-in-PDF path explicitly; (2) fingerprint shipped AI-agent stacks for the trio: commented-out auth key in.env.example, missingUSERin Dockerfile,0.0.0.0compose bind — three greps, all remote-visible once you have the repo; (3) signature-based filtering of one bash tool doesn't cover exec_module/import-side-effects paths — enumerate all execution sinks in the registry, not just the obviously-named ones.
October 3 follow-up: Zilliz Attu Playground — the DB admin console ships an open HTTP proxy (2 GHSAs, <3.0.0)¶
- GHSA-4xhx-g43j-j7vj / CVE-2026-105049 (5.8): Attu's Playground feature requires no authentication to proxy arbitrary HTTP/HTTPS requests to public-internet URLs; GHSA-f5vq-p2v5-96j9 / CVE-2026-105048 (4.0): the same feature proxies to private IP ranges. Split across two advisories, one sink — fingerprinting must check the pair together, since either half alone reads as low severity.
- Same rule as the LocalAI gallery leg above, one product-class over: the "try it in the browser" convenience panel (DB admin UIs, model galleries, API playgrounds) is a full-URL fetcher that ships unsanitized and often pre-auth — Attu is the standard Milvus/Zilliz management console, so it appears next to vector DBs on AI-stack internal recon. Recon pattern: on any exposed admin console, enumerate playground/proxy/test-connection/embed-fetch routes before assuming auth is uniform across the UI; the panel handler frequently lives outside the app's auth middleware because it was written as a dev affordance.
- Proof boundary: owned callback URL, one request; no internal-range fetching beyond customer-approved canaries.
Headroom LLM proxy: CSWSH turns any visited webpage into an API-key spender¶
- No Origin check before
websocket.accept()on/v1/responses(GHSA-h46j-26q3-rggf / CVE-2026-71416, 8.8): the WS proxy handler accepts any client, and when the client omitsAuthorizationit falls back to the server'sOPENAI_API_KEYenv var ("safety net for clients that don't forward auth headers via WebSocket upgrade"). Any page in any browser that can reach the proxy issues arbitrary LLM requests on the operator's key. Two axes: (1) WebSocket upgrades are not subject to CORS preflight — a missing server-side Origin check is the only wall, and header-fallback credential injection converts an unauthenticated WS handshake into paid third-party API use; (2) developer LLM proxies binding loopback/LAN with zero WS auth are a standing recon target in internal engagements — enumerate/v1/*WS endpoints like REST routes. Prove with an owned page +new WebSocket()handshake; measure token spend as the effect, never exfiltrate existing conversations.
October 6 follow-up: vLLM 0.25.1 dump — isolation-control drops after the fix, and cross-route trust handoffs (6 GHSAs, 23:42Z–00:02Z)¶
A six-advisory vLLM wave with sequential CVEs (CVE-2026-105752 through 105757/105760) landed in 20 minutes — another coordinated audit dump, and the durable part for operators is the shape of three of the legs, not their CVSS:
cache_saltsilently dropped on tool-continuation re-submission (GHSA-935w-9g4m-p28p / CVE-2026-105752): on the GPT-OSS Harmony/v1/responsespath, turn 1 carries the caller'scache_salt, but the multi-turn tool-continuation rebuild re-submitstokens_input(token_ids)with no salt — the continuation prefix lands in the global unsalted prefix-cache namespace, and a second tenant who reconstructs the low-entropy post-tool history reads exact per-turncached_tokenscounts from the Responses usage → the prompt-membership oracle thatcache_saltis documented to prevent, restored from a different sink than the CVE-2025-46570 fix closed. Operator rule: when a mitigation exists as an opt-in parameter, test every workflow branch that reconstructs engine input after the opt-in is set — multi-turn/continuation/rewind paths are where per-request security fields get dropped in rebuild code. Probe on authorized deployments: enable salting, drive a tool call, then replay the reconstructed continuation unsalted and diff cached-token counts.- Disaggregated scale-out transport trusts caller-supplied render output (GHSA-ph72-cqr5-qpp7 / CVE-2026-105754): the architecture splits rendering (
/v1/chat/completions/render, trusted) from generation (/inference/v1/generate), but the generate route decodes a caller-suppliedfeaturesblob (encoder tensors,mm_hashes, placeholder ranges, field-processor selection) and forwards it as if it came from the renderer, with no rebinding to the active model's renderer contract. Forged fields give engine-fatal crashes, cross-request encoder-cache poisoning via unbound cache-key hash, and integrity loss on replay. This is the general two-step trust handoff shape: any service that decomposes into produce-then-consume HTTP steps where the consume step accepts reconstructed intermediate state over the wire without validating it against what the produce step would emit. Standing check for disaggregated/PD-separated inference platforms and any render-then-generate API split: submit an otherwise-valid first-step output with one forged field and observe whether the second step re-derives or re-trusts it. - Mirrored IPC caches desync on rejected requests (GHSA-ph3r-5jfg-f84f / CVE-2026-105753): the sender cache populates during render before validation rejects the request (
max_model_lencheck after rendering), the receiver never sees the item, and the next request reusing that media hash sendsNonefor a cache-hit → receiver assertion kills EngineCore. DoS-leg, but the axis is reusable: lockstep mirrored caches between two processes break on any request path that mutates one side then aborts — a cheap crash primitive on default config (one oversized prompt + one reused image hash). For availability-boundary reports on shared inference endpoints this is the fingerprint; do not run crash loops against shared tenants. - Remaining legs (2823 loose
cache_saltvalidation killing EngineCore, 2phq caller-controlled request-id embedding cache, x6mc/58v5 request-drivenmax_frames/video sampler exhaustion, 85xf structured-output errors terminating the shared EngineCore): same product, known DoS/trust shapes; the recurring lesson across all six is shared-state blast radius — nearly every leg converts one request into engine-global failure or cross-tenant signal because caches, the EngineCore process, and IPC state are shared across requests with no per-tenant partition. When fingerprinting vLLM-family servers for an authorized AI-stack assessment, treat any request-controlled field that reaches a cache key or engine-global structure as tenant-isolation-relevant, then check the version against this dump.
October 8 15:3xZ follow-up: Ollama /api/pull digest path traversal → write outside the model store → root RCE on restart (CVE-2026-103663 / GHSA-w8p2-phwr-px3r, critical, fixed 0.35.0)¶
The most-used local-inference server published an unauthenticated path traversal in /api/pull: the digestToPath helper does not validate layer digests, so a traversal sequence supplied as a digest writes the pulled "layer" binary outside the model store. The kill-chain finisher is the load-path, not the write: in the default Docker images the server process has write access to /usr/lib/ollama, so the attacker writes a malicious library there — the next server restart loads and executes it, as root.
Operator rules for this page's model-runtime section:
- Content-addressed fields are path components. Anywhere a
sha256:-style digest, model name, or revision string is joined into a filesystem path (digestToPathshape), traversal (../, absolute-path) belongs in the input battery — the value is treated as immutable content metadata by callers but is literal path material at the sink. Grep fingerprint:digest,revision,model,blob,layernear filepath-join/Sprintfinto open/write. - A file-write primitive on an inference host is a load-path primitive. The escalation was not a second bug — it was the runtime's own trust directory (
/usr/lib/ollamawritable by the service user and loaded at startup). For any writable-directory finding on AI infra, enumerate what the process later loads from writable locations (plugins, native libs, model sidecars, launcher dirs): write-to-load-path = code execution deferred one restart, which on containerized deployments arrives automatically on redeploy. - Ollama's default posture has always been unauthenticated with broad binds in dev images (this page's recurring default-posture rule); this leg converts that posture into root code execution via one HTTP request plus a restart. Standing recon on authorized AI engagements:
GET /api/versionfirst, then treat/api/pull,/api/push,/api/createas file-boundary surfaces and check the container image's writable-directory layout (lab copies only).
Proof discipline: lab Ollama in a disposable container with a marker write outside the model store; never push real images, never traverse on shared inference hosts, and restart/load-trigger tests only on deployments you own.