CVE-2026-73846: ckan-mcp-server: cache key collision enables poisoning
GHSA-78x9-fhhx-v2g6MEDIUMPoC AVAILABLECISA: TRACK*The ckan-mcp-server MCP tool builds its response-cache key by joining request parameters with unescaped '&' and '|' separators, so two genuinely different queries can hash to the identical cache key and share one cached response. On the default shared-cache deployments (Cloudflare Workers' global cache or a Node process's in-memory LRU), an attacker who primes a colliding key first can force a victim's distinct, legitimate query to be served the attacker's chosen data instead — and because that data flows straight into an AI agent as trusted tool output, it can carry an indirect prompt injection payload without the victim ever touching the malicious dataset directly. The blast radius is limited to deployments that share a cache across clients (AC:H, PR:N, UI:N) and the flaw isn't in CISA KEV, has no public exploit or Nuclei template, and carries a low EPSS score (0.00137), so this is not an active, drive-by threat. Still, any team running ckan-mcp-server as a shared MCP endpoint for multiple users or agents should patch to 0.4.112 now, since the fix is a straightforward key-encoding change and the downside — silent data/prompt substitution across users — is disproportionate to the effort of upgrading.
What is the risk?
Medium severity (CVSS 6.5, AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N) — no authentication or user interaction needed, but exploitation requires the deployment to run with caching enabled and shared across clients (the default for the Cloudflare Workers and Node in-process modes), plus successful priming ahead of the victim's request. Confidentiality impact is low since only same-portal public CKAN data is at stake, but integrity impact is high: victims can silently receive attacker-chosen results for queries they never made. EPSS is very low (0.00137) and there's no KEV listing, public PoC weaponization, or scanner template beyond the vendor's own PoC script, so near-term mass exploitation is unlikely — this is a targeted, deployment-specific risk rather than an internet-wide one.
How does the attack unfold?
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| @aborruso/ckan-mcp-server | npm | < 0.4.112 | 0.4.112 |
Do you use @aborruso/ckan-mcp-server? You're affected.
How severe is it?
What is the attack surface?
What should I do?
1 stepUpgrade @aborruso/ckan-mcp-server to >=0.4.112, which fixes canonicalizeParams/buildCacheKey to use an unambiguous, injection-proof encoding (structured/typed JSON hashing or escaped key=value pairs with a safe separator). If immediate upgrade isn't possible, disable response caching or partition the cache per-client/tenant so no client can influence another's entries. Detection: review cache hit logs or add monitoring for cache keys with identical hashes but differing raw request parameters, and audit any homegrown cache-key builders elsewhere in the MCP tool stack for the same string-concatenation-without-escaping pattern.
What does CISA's SSVC say?
Source: CISA Vulnrichment (SSVC v2.0). Decision based on the CISA Coordinator decision tree.
How is it classified?
Which compliance frameworks are affected?
This CVE is relevant to:
Frequently Asked Questions
What is CVE-2026-73846?
The ckan-mcp-server MCP tool builds its response-cache key by joining request parameters with unescaped '&' and '|' separators, so two genuinely different queries can hash to the identical cache key and share one cached response. On the default shared-cache deployments (Cloudflare Workers' global cache or a Node process's in-memory LRU), an attacker who primes a colliding key first can force a victim's distinct, legitimate query to be served the attacker's chosen data instead — and because that data flows straight into an AI agent as trusted tool output, it can carry an indirect prompt injection payload without the victim ever touching the malicious dataset directly. The blast radius is limited to deployments that share a cache across clients (AC:H, PR:N, UI:N) and the flaw isn't in CISA KEV, has no public exploit or Nuclei template, and carries a low EPSS score (0.00137), so this is not an active, drive-by threat. Still, any team running ckan-mcp-server as a shared MCP endpoint for multiple users or agents should patch to 0.4.112 now, since the fix is a straightforward key-encoding change and the downside — silent data/prompt substitution across users — is disproportionate to the effort of upgrading.
Is CVE-2026-73846 actively exploited?
Proof-of-concept exploit code is publicly available for CVE-2026-73846, increasing the risk of exploitation.
How to fix CVE-2026-73846?
Upgrade @aborruso/ckan-mcp-server to >=0.4.112, which fixes canonicalizeParams/buildCacheKey to use an unambiguous, injection-proof encoding (structured/typed JSON hashing or escaped key=value pairs with a safe separator). If immediate upgrade isn't possible, disable response caching or partition the cache per-client/tenant so no client can influence another's entries. Detection: review cache hit logs or add monitoring for cache keys with identical hashes but differing raw request parameters, and audit any homegrown cache-key builders elsewhere in the MCP tool stack for the same string-concatenation-without-escaping pattern.
What systems are affected by CVE-2026-73846?
This vulnerability affects the following AI/ML architecture patterns: agent frameworks, RAG pipelines.
What is the CVSS score for CVE-2026-73846?
CVE-2026-73846 has a CVSS v3.1 base score of 6.5 (MEDIUM). The EPSS exploitation probability is 0.19%.
What is the AI security impact?
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0051.001IndirectAML.T0099AI Agent Tool Data PoisoningCompliance Controls Affected
What are the technical details?
Original Advisory
## Summary The response cache derives its key from an ambiguous string serialization of the request parameters. `canonicalizeParams` joins sorted `${key}=${value}` pairs with `&` and does not escape `&`, `=`, or the `|` field separators used in `buildCacheKey`. Two **different** logical parameter sets can therefore serialize to the **same** key and share one cache entry. Because the cached value is whatever the upstream returned for whichever request populated the entry first, an attacker can prime a colliding key so a victim's distinct query (same `server_url`) is served the attacker's cached response. ## Affected code ```js // src/utils/cache.ts export function canonicalizeParams(params) { const keys = Object.keys(params).sort(); const pairs = []; for (const key of keys) { const value = params[key]; if (value === undefined || value === null) continue; const serialized = typeof value === "object" ? JSON.stringify(value) : String(value); pairs.push(`${key}=${serialized}`); // value not escaped } return pairs.join("&"); // '&' delimiter, injectable } export async function buildCacheKey(serverUrl, action, params) { const raw = `${serverUrl}|${action}|${canonicalizeParams(params)}`; // '|' also unescaped return sha1Hex(raw); } ``` Confirmed collisions (identical key): - `{ q: "budget", rows: 10 }` ≡ `{ q: "budget&rows=10" }` → both canonicalize to `q=budget&rows=10` - `{ filters: { a: "b" } }` ≡ `{ filters: '{"a":"b"}' }` → both canonicalize to `filters={"a":"b"}` (object-vs-string ambiguity) An attacker can reproduce **any** target canonical string by injecting it into the alphabetically-first parameter, so the collision is general, not incidental. ## Impact - **Cache poisoning / confusion.** On a shared cache (caching is enabled by default; the Cloudflare Workers deployment uses the shared `caches.default`, and a Node HTTP instance shares one in-process LRU across all clients), an attacker primes a colliding entry so that another client's genuinely different query receives the attacker-chosen response for the same portal. - **Integrity of results.** Victims receive data for a query they did not make (wrong dataset list, wrong record set), undermining trust in tool output. - **Chains with indirect prompt injection (advisory #07).** The attacker's colliding request can be one whose upstream response surfaces an attacker-controlled dataset (with malicious `notes`/`title`); the victim's benign query then serves that poisoned content to the model — delivering prompt injection via the cache, without the victim ever querying the malicious dataset. Confidentiality impact is low (same-portal public data); the primary damage is integrity. `AC:H` reflects the need for caching to be enabled and a shared instance plus priming before the victim's request populates the entry. ## Proof of concept `poc/cache-collision-poc.mjs` primes a single-param request and shows a victim's distinct two-param request being served the attacker-primed entry: ``` attacker canonical : q=budget&rows=10 victim canonical : q=budget&rows=10 same cache key : true victim served from cache: true victim RECEIVED : RESULT_FOR({"q":"budget&rows=10"}) victim EXPECTED : RESULT_FOR({"q":"budget","rows":10}) ``` ## Remediation - Build the cache key from an unambiguous, injection-proof encoding: hash a structured, canonical JSON (with typed values) or percent-encode/escape each key and value before joining, and use a separator that cannot appear in the encoded fields. Include a type tag so `{a:{...}}` (object) and `{a:"..."}` (string) never coincide. - Consider partitioning the cache per client/tenant on shared deployments so one client cannot influence another's entries.
Exploitation Scenario
An attacker sends a request to the shared ckan-mcp-server instance with a single parameter whose value is crafted to contain '&' and '=' characters (e.g., q="budget&rows=10"), engineering it so canonicalizeParams produces the exact same string as a victim's distinct two-parameter query (q="budget", rows=10). The attacker's request populates the shared cache entry with a response pointing to an attacker-controlled dataset whose title/notes fields contain hidden instructions. When the victim later issues their legitimate, differently-structured query against the same server_url, the collision causes the cache to return the attacker's primed entry instead of fresh results. The victim's AI agent ingests this poisoned response as trusted tool output, and the embedded instructions execute as an indirect prompt injection — redirecting the agent's behavior without the victim ever knowingly querying the malicious dataset.
Weaknesses (CWE)
CWE-345 — Insufficient Verification of Data Authenticity: The product does not sufficiently verify the origin or authenticity of data, in a way that causes it to accept invalid data.
Source: MITRE CWE corpus.
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:NReferences
Timeline
Related Vulnerabilities
CVE-2026-6144710.0PraisonAI: RCE via unsandboxed LLM code execution
Same attack type: Prompt InjectionCVE-2026-5476910.0Langroid: prompt injection to RCE via broken eval() sandbox
Same attack type: Prompt InjectionCVE-2026-3493810.0praisonaiagents: sandbox bypass enables full host RCE
Same attack type: Prompt InjectionGHSA-wpqr-6v78-jr5g10.0Gemini CLI: RCE via malicious workspace in CI/CD
Same attack type: Prompt InjectionCVE-2026-6153910.0Xinference: eval() on LLM output enables RCE
Same attack type: Prompt Injection