CVE-2026-58485
mcp-searxng: DNS-resolved Private Hostname SSRF in `web_url_read`
mcp-searxng is a Model Context Protocol server that gives AI assistants web search and URL-reading capabilities through SearXNG. Prior to 1.7.1, web_url_read receives its caller-controlled URL through src/index.ts and validates only the literal hostname in assertUrlAllowed() within src/url-reader.ts before undiciFetch() performs operating-system DNS resolution. A public-looking attacker-controlled hostname that resolves to a private, loopback, link-local, or cloud-metadata address therefore passes the lexical check and causes the MCP server to connect to the internal destination. In the default HTTP configuration, an unauthenticated network client can use this path to read internal services, expose credentials or service tokens, and enumerate reachable internal hosts; in STDIO deployments, prompt-influenced tool selection can provide the malicious URL. Direct private IP literals are blocked, and MCP_HTTP_ALLOW_PRIVATE_URLS remains an explicit opt-out. This issue is fixed in version 1.7.1.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.1
mcp-searxng: DNS-resolved Private Hostname SSRF in `web_url_read`
mcp-searxng is a Model Context Protocol server that gives AI assistants web search and URL-reading capabilities through SearXNG. Prior to 1.7.1, web_url_read receives its caller-controlled URL through src/index.ts and validates only the literal hostname in assertUrlAllowed() within src/url-reader.ts before undiciFetch() performs operating-system DNS resolution. A public-looking attacker-controlled hostname that resolves to a private, loopback, link-local, or cloud-metadata address therefore passes the lexical check and causes the MCP server to connect to the internal destination. In the default HTTP configuration, an unauthenticated network client can use this path to read internal services, expose credentials or service tokens, and enumerate reachable internal hosts; in STDIO deployments, prompt-influenced tool selection can provide the malicious URL. Direct private IP literals are blocked, and MCP_HTTP_ALLOW_PRIVATE_URLS remains an explicit opt-out. This issue is fixed in version 1.7.1.
- Record
CVE-2026-58485- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
EUVDEUVD-2026-78924
7.1
mcp-searxng is a Model Context Protocol server that gives AI assistants web search and URL-reading capabilities through SearXNG. Prior to 1.7.1, web_url_read receives its caller-controlled URL through src/index.ts and validates only the literal hostname in assertUrlAllowed() within src/url-reader.ts before undiciFetch() performs operating-system DNS resolution. A public-looking attacker-controlled hostname that resolves to a private, loopback, link-local, or cloud-metadata address therefore passes the lexical check and causes the MCP server to connect to the internal destination. In the default HTTP configuration, an unauthenticated network client can use this path to read internal services, expose credentials or service tokens, and enumerate reachable internal hosts; in STDIO deployments, prompt-influenced tool selection can provide the malicious URL. Direct private IP literals are blocked, and MCP_HTTP_ALLOW_PRIVATE_URLS remains an explicit opt-out. This issue is fixed in version 1.7.1.
- Record
EUVD-2026-78924- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-mrvx-jmjw-vggc
7.1
SearXNG MCP Server: DNS-resolved Private Hostname SSRF in `web_url_read`
DNS-resolved Private Hostname SSRF in web_url_read
Summary
The web_url_read MCP tool in mcp-searxng is vulnerable to Server-Side Request Forgery (SSRF) via DNS rebinding bypass. The assertUrlAllowed() function at src/url-reader.ts:85-93 validates only the syntactic hostname string against a private IP/hostname blocklist without performing DNS resolution. An attacker who controls a domain that resolves to a private or loopback IP address (e.g., via a wildcard DNS service like nip.io, or a custom DNS entry) can bypass the security check and cause the MCP server to read arbitrary internal HTTP services reachable from the server host. This allows exfiltration of sensitive data from internal services with no authentication required in the default HTTP configuration.
Details
The vulnerable code is in src/url-reader.ts, where the assertUrlAllowed() function performs a hostname-only string comparison:
// src/url-reader.ts:85-93
function assertUrlAllowed(url: URL): void {
const security = getHttpSecurityConfig();
if (security.allowPrivateUrls) {
return;
}
if (isPrivateHostname(url.hostname) || isPrivateIpv4(url.hostname) || isPrivateIPv6(url.hostname)) {
throw createURLSecurityPolicyError(url.toString());
}
}
This function checks whether url.hostname lexically matches a private address pattern but never resolves the hostname via DNS. The OS-level DNS resolution happens later, inside undiciFetch() at src/url-reader.ts:367, after the security gate has already passed.
The full data flow is:
src/index.ts:37-49—isWebUrlReadArgs()acceptsargs.urlas any string value with no URL policy enforcement.src/index.ts:226-240—web_url_readpassesargs.urldirectly tofetchAndConvertToMarkdown().src/url-reader.ts:307-313— URL is parsed andassertUrlAllowed(parsedUrl)is called (string check only).src/url-reader.ts:85-93—assertUrlAllowed()checksurl.hostnameas a string; no DNS resolution is performed.src/url-reader.ts:367—undiciFetch(currentUrl.toString(), currentRequestOptions)resolves the hostname via OS DNS and connects to the resolved IP, which may be a private or loopback address.
In the default HTTP mode (MCP_HTTP_HARDEN unset), requireAuth is false (see src/http-security.ts:30-41), meaning no authentication is required to invoke web_url_read.
The recommended remediation is to resolve the hostname via node:dns/promises inside assertUrlAllowed() before the fetch is issued:
--- a/src/url-reader.ts
+++ b/src/url-reader.ts
@@
import { isIP } from "node:net";
+import { lookup } from "node:dns/promises";
@@
-function assertUrlAllowed(url: URL): void {
+async function assertUrlAllowed(url: URL): Promise<void> {
const security = getHttpSecurityConfig();
if (security.allowPrivateUrls) {
return;
}
+ if (!["http:", "https:"].includes(url.protocol)) {
+ throw createURLSecurityPolicyError(url.toString());
+ }
if (isPrivateHostname(url.hostname) || isPrivateIpv4(url.hostname) || isPrivateIPv6(url.hostname)) {
throw createURLSecurityPolicyError(url.toString());
}
+ if (isIP(url.hostname) === 0) {
+ const answers = await lookup(url.hostname, { all: true, verbatim: true });
+ if (answers.some(({ address }) => isPrivateIpv4(address) || isPrivateIPv6(address))) {
+ throw createURLSecurityPolicyError(url.toString());
+ }
+ }
}
@@
- assertUrlAllowed(parsedUrl);
+ await assertUrlAllowed(parsedUrl);
@@
- assertUrlAllowed(nextUrl);
+ await assertUrlAllowed(nextUrl);
PoC
Prerequisites:
- Docker installed on the test machine.
- The
mcp-searxngrepository cloned locally (version 1.6.0 / commit90423e5).
Build the container:
docker build -t mcp-searxng-ssrf-001 -f vuln-001/Dockerfile .
Run the PoC:
docker run --rm \
--add-host ssrf-target.internal:127.0.0.1 \
-v $(pwd)/vuln-001/poc.py:/poc.py \…
- Record
GHSA-mrvx-jmjw-vggc- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
NVD
7.1
mcp-searxng is a Model Context Protocol server that gives AI assistants web search and URL-reading capabilities through SearXNG. Prior to 1.7.1, web_url_read receives its caller-controlled URL through src/index.ts and validates only the literal hostname in assertUrlAllowed() within src/url-reader.ts before undiciFetch() performs operating-system DNS resolution. A public-looking attacker-controlled hostname that resolves to a private, loopback, link-local, or cloud-metadata address therefore passes the lexical check and causes the MCP server to connect to the internal destination. In the default HTTP configuration, an unauthenticated network client can use this path to read internal services, expose credentials or service tokens, and enumerate reachable internal hosts; in STDIO deployments, prompt-influenced tool selection can provide the malicious URL. Direct private IP literals are blocked, and MCP_HTTP_ALLOW_PRIVATE_URLS remains an explicit opt-out. This issue is fixed in version 1.7.1.
- Record
CVE-2026-58485- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
OSV
7.1
mcp-searxng: DNS-resolved Private Hostname SSRF in `web_url_read`
mcp-searxng is a Model Context Protocol server that gives AI assistants web search and URL-reading capabilities through SearXNG. Prior to 1.7.1, web_url_read receives its caller-controlled URL through src/index.ts and validates only the literal hostname in assertUrlAllowed() within src/url-reader.ts before undiciFetch() performs operating-system DNS resolution. A public-looking attacker-controlled hostname that resolves to a private, loopback, link-local, or cloud-metadata address therefore passes the lexical check and causes the MCP server to connect to the internal destination. In the default HTTP configuration, an unauthenticated network client can use this path to read internal services, expose credentials or service tokens, and enumerate reachable internal hosts; in STDIO deployments, prompt-influenced tool selection can provide the malicious URL. Direct private IP literals are blocked, and MCP_HTTP_ALLOW_PRIVATE_URLS remains an explicit opt-out. This issue is fixed in version 1.7.1.
- Record
CVE-2026-58485- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
OSVGHSA-mrvx-jmjw-vggc
7.1
SearXNG MCP Server: DNS-resolved Private Hostname SSRF in `web_url_read`
DNS-resolved Private Hostname SSRF in web_url_read
Summary
The web_url_read MCP tool in mcp-searxng is vulnerable to Server-Side Request Forgery (SSRF) via DNS rebinding bypass. The assertUrlAllowed() function at src/url-reader.ts:85-93 validates only the syntactic hostname string against a private IP/hostname blocklist without performing DNS resolution. An attacker who controls a domain that resolves to a private or loopback IP address (e.g., via a wildcard DNS service like nip.io, or a custom DNS entry) can bypass the security check and cause the MCP server to read arbitrary internal HTTP services reachable from the server host. This allows exfiltration of sensitive data from internal services with no authentication required in the default HTTP configuration.
Details
The vulnerable code is in src/url-reader.ts, where the assertUrlAllowed() function performs a hostname-only string comparison:
// src/url-reader.ts:85-93
function assertUrlAllowed(url: URL): void {
const security = getHttpSecurityConfig();
if (security.allowPrivateUrls) {
return;
}
if (isPrivateHostname(url.hostname) || isPrivateIpv4(url.hostname) || isPrivateIPv6(url.hostname)) {
throw createURLSecurityPolicyError(url.toString());
}
}
This function checks whether url.hostname lexically matches a private address pattern but never resolves the hostname via DNS. The OS-level DNS resolution happens later, inside undiciFetch() at src/url-reader.ts:367, after the security gate has already passed.
The full data flow is:
src/index.ts:37-49—isWebUrlReadArgs()acceptsargs.urlas any string value with no URL policy enforcement.src/index.ts:226-240—web_url_readpassesargs.urldirectly tofetchAndConvertToMarkdown().src/url-reader.ts:307-313— URL is parsed andassertUrlAllowed(parsedUrl)is called (string check only).src/url-reader.ts:85-93—assertUrlAllowed()checksurl.hostnameas a string; no DNS resolution is performed.src/url-reader.ts:367—undiciFetch(currentUrl.toString(), currentRequestOptions)resolves the hostname via OS DNS and connects to the resolved IP, which may be a private or loopback address.
In the default HTTP mode (MCP_HTTP_HARDEN unset), requireAuth is false (see src/http-security.ts:30-41), meaning no authentication is required to invoke web_url_read.
The recommended remediation is to resolve the hostname via node:dns/promises inside assertUrlAllowed() before the fetch is issued:
--- a/src/url-reader.ts
+++ b/src/url-reader.ts
@@
import { isIP } from "node:net";
+import { lookup } from "node:dns/promises";
@@
-function assertUrlAllowed(url: URL): void {
+async function assertUrlAllowed(url: URL): Promise<void> {
const security = getHttpSecurityConfig();
if (security.allowPrivateUrls) {
return;
}
+ if (!["http:", "https:"].includes(url.protocol)) {
+ throw createURLSecurityPolicyError(url.toString());
+ }
if (isPrivateHostname(url.hostname) || isPrivateIpv4(url.hostname) || isPrivateIPv6(url.hostname)) {
throw createURLSecurityPolicyError(url.toString());
}
+ if (isIP(url.hostname) === 0) {
+ const answers = await lookup(url.hostname, { all: true, verbatim: true });
+ if (answers.some(({ address }) => isPrivateIpv4(address) || isPrivateIPv6(address))) {
+ throw createURLSecurityPolicyError(url.toString());
+ }
+ }
}
@@
- assertUrlAllowed(parsedUrl);
+ await assertUrlAllowed(parsedUrl);
@@
- assertUrlAllowed(nextUrl);
+ await assertUrlAllowed(nextUrl);
PoC
Prerequisites:
- Docker installed on the test machine.
- The
mcp-searxngrepository cloned locally (version 1.6.0 / commit90423e5).
Build the container:
docker build -t mcp-searxng-ssrf-001 -f vuln-001/Dockerfile .
Run the PoC:
docker run --rm \
--add-host ssrf-target.internal:127.0.0.1 \
-v $(pwd)/vuln-001/poc.py:/poc.py \…
- Record
GHSA-mrvx-jmjw-vggc- CVSS 3.1
- 7.1 High
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-918
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
mcp-searxngnpm |
before 1.7.1 | 1.7.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-58485 is a high vulnerability (CVSS 7.1) in Ihor-sokoliuk Mcp-searxng, published June 19, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.19%, the 8th percentile. Fixed in mcp-searxng 1.7.1.
References
github.com/ihor-sokoliuk/mcp-searxng/security/advisories/GHSA-mrvx-jmjw-vggc
github.com/ihor-sokoliuk/mcp-searxng/pull/120
github.com/ihor-sokoliuk/mcp-searxng/commit/14575eaaa46a21391477d2b68673858ba6b52bd6
github.com/ihor-sokoliuk/mcp-searxng/releases/tag/v1.7.1
github.com/advisories/GHSA-mrvx-jmjw-vggc
github.com/CVEProject/cvelistV5/tree/main/cves/2026/58xxx/CVE-2026-58485.json
nvd.nist.gov/vuln/detail/CVE-2026-58485
github.com/ihor-sokoliuk/mcp-searxng
Left out here: the rest of 2 source descriptions. The JSON record has them.