GHSA-vx5f-957p-qpvm: Glances Central Browser Autodiscovery Leaks Reusable Credentials to Zeroconf-Spoofed Servers
SummaryIn Central Browser mode, Glances stores both the Zeroconf-advertised server name and the discovered IP address for dynamic servers, but later builds connection URIs from the untrusted advertised name instead of the discovered IP. When a dynamic server reports itself as protected, Glances also uses that same untrusted name as the lookup key for saved passwords and the global `[passwords] default` credential.An attacker on the same local network can advertise a fake Glances service over Zeroconf and cause the browser to automatically send a reusable Glances authentication secret to an attacker-controlled host. This affects the background polling path and the REST/WebUI click-through path in Central Browser mode.DetailsDynamic server discovery keeps both a short `name` and a separate `ip`:The Zeroconf listener populates those fields directly from the service advertisement:However, the Central Browser connection logic ignores `server['ip']` and instead uses the untrusted advertised `server['name']` for both password lookup and the destination URI:That URI is used automatically by the background polling thread:The password lookup itself falls back to the global default password when there is no exact match:The sample configuration explicitly supports that `default` credential reuse:The secret sent over the network is not the cleartext password, but it is still a reusable Glances authentication credential. The client hashes the configured password and sends that hash over HTTP Basic authentication:There is an inconsistent trust boundary in the interactive browser code as well:`glances/client_browser.py:44` opens the REST/WebUI target via `webbrowser.open(self.servers_list.get_uri(server))`, which again trusts `server['name']``glances/client_browser.py:55` fetches saved passwords with `self.servers_list.password.get_password(server['name'])``glances/client_browser.py:76` uses `server['ip']` for the RPC client connectionThat asymmetry shows the intended safe destination (`ip`) is already available, but the credential-bearing URI and password binding still use the attacker-controlled Zeroconf name.Exploit FlowThe victim runs Glances in Central Browser mode with autodiscovery enabled and has a saved Glances password in `[passwords]` (especially `default=...`).An attacker on the same multicast domain advertises a fake `_glances._tcp.local.` service with an attacker-controlled service name.Glances stores the discovered server as `{'name': <advertised-name>, 'ip': <discovered-ip>, ...}`.The background stats refresh calls `get_uri(server)`.Once the fake server causes the entry to become `PROTECTED`, `get_uri()` looks up a saved password by the attacker-controlled `name`, falls back to `default` if present, hashes it, and builds `http://username:hash@<advertised-name>:<port>`.The attacker receives a reusable Glances authentication secret and can replay it against Glances servers using the same credential.PoCStep 1: Verified local logic proofThe following command executes the real `glances/servers_list.py` `get_uri()` implementation (with unrelated imports stubbed out) and demonstrates that:password lookup happens against `server['name']`, not `server['ip']`the generated credential-bearing URI uses `server['name']`, not `server['ip']`Verified output:This confirms the code path binds credentials to the advertised `name` and ignores the discovered `ip`.Step 2: Live network reproductionConfigure a reusable browser password:Start Glances in Central Browser mode on the victim machine:On an attacker-controlled machine on the same LAN, advertise a fake Glances Zeroconf service and return HTTP 401 / XML-RPC auth failures so the entry becomes `PROTECTED`:On the next Central Browser refresh, Glances first probes the fake server, marks it `PROTECTED`, then retries with:The attacker captures the Basic-auth credential and can replay that value as the Glances password hash against Glances servers that share the same configured password.Impact**Credential exfiltration from browser operators:** An adjacent-network attacker can harvest the reusable Glances authentication secret from operators running Central Browser mode with saved passwords.**Authentication replay:** The captured pbkdf2-derived Glances password hash can be replayed against Glances servers that use the same credential.**REST/WebUI click-through abuse:** For REST servers, `webbrowser.open(self.servers_list.get_uri(server))` can open attacker-controlled URLs with embedded credentials.**No user click required for background theft:** The stats refresh thread uses the vulnerable path automatically once the fake service is marked `PROTECTED`.**Affected scope:** This is limited to Central Browser deployments with autodiscovery enabled and saved/default passwords configured. Static server entries and standalone non-browser use are not directly affected by this specific issue.Recommended FixUse the discovered `ip` as the only network destination for autodiscovered servers, and do not automatically apply saved or default passwords to dynamic entries.And use the same `_get_preconfigured_password()` logic in `glances/client_browser.py` instead of calling `self.servers_list.password.get_password(server['name'])` directly.
CVSS v3.1: 8.1