CVE-2026-73846: ckan-mcp-server: cache key collision enables poisoning

GHSA-78x9-fhhx-v2g6MEDIUMPoC AVAILABLECISA: TRACK*
Published September 3, 2026
CISO Take

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.

Sources:NVDGitHub AdvisoryEPSSATLAS

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?

Cache priming
Attacker sends a crafted request whose parameter value is engineered to canonicalize identically to a victim's future, structurally different query, populating the shared cache with an attacker-chosen response.
AML.T0099
Victim query collision
The victim issues a legitimate, distinct query against the same server_url; the unescaped '&'/'|' separators cause it to hash to the same cache key as the attacker's primed entry.
Poisoned content delivery
The cache serves the attacker's primed entry to the victim's AI agent as if it were the genuine result, including attacker-controlled dataset title/notes fields.
AML.T0051.001
Impact
The agent treats the poisoned content as trusted tool output, executing embedded instructions as an indirect prompt injection and undermining the integrity of results delivered to the victim.
AML.T0080

What systems are affected?

PackageEcosystemVulnerable RangePatched
@aborruso/ckan-mcp-servernpm< 0.4.1120.4.112

Do you use @aborruso/ckan-mcp-server? You're affected.

How severe is it?

CVSS 3.1
6.5 / 10
EPSS
0.2%
chance of exploitation in 30 days
Higher than 9% of all CVEs
Exploitation Status
Exploit Available
Exploitation: MEDIUM
Sophistication
Moderate
Exploitation Confidence
medium
○CISA SSVC: Public PoC
○Public PoC indexed (trickest/cve)
Composite signal derived from CISA KEV, VulnCheck KEV, CISA SSVC, EPSS, Metasploit, Exploit-DB, trickest/cve, Nuclei templates, and inthewild.io exploitation reports.

What is the attack surface?

AVACPRUISCIA
AVNetwork
ACHigh
PRNone
UINone
SUnchanged
CLow
IHigh
ANone

What should I do?

1 step
  1. 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 does CISA's SSVC say?

DecisionTrack*
Exploitationpoc
AutomatableNo
Technical Impactpartial

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:

EU AI Act
Article 15-Accuracy, Robustness and Cybersecurity
NIST AI RMF
MANAGE-4.1-Post-deployment monitoring for AI system risks including third-party components
OWASP LLM Top 10
LLM01-Prompt Injection

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

agent frameworksRAG pipelines

MITRE ATLAS Techniques

AML.T0051.001Indirect
AML.T0099AI Agent Tool Data Poisoning

Compliance Controls Affected

EU AI Act: Article 15
NIST AI RMF: MANAGE-4.1
OWASP LLM Top 10: LLM01

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:N

Timeline

Published
September 3, 2026
Last Modified
September 3, 2026
First Seen
September 3, 2026

Related Vulnerabilities