Feb 23, 2026·5 min read·33 visits
The terminal-controller-mcp package, designed to give AI models terminal access, attempted to block dangerous commands like 'mkfs' using a simple string blacklist. This CVSS 10.0 vulnerability allows attackers to bypass that check using standard shell features (e.g., specific character concatenation), leading to full Remote Code Execution (RCE).
A critical command injection vulnerability in terminal-controller-mcp allows attackers to bypass keyword-based filters and execute arbitrary code. By leveraging shell command substitution, malicious actors can reconstruct forbidden commands at runtime, rendering the application's blacklist security mechanism useless.
The Model Context Protocol (MCP) is the new hotness in the AI world. It’s essentially a standardized way for Large Language Models (LLMs) to interface with local tools, databases, and—in this specific case—your system terminal. The terminal-controller-mcp package does exactly what it says on the tin: it exposes a server that allows an AI (or anyone controlling the prompt) to execute shell commands on the host machine.
From a security perspective, this is the digital equivalent of handing a loaded shotgun to a toddler and hoping they don't pull the trigger. The premise of the tool requires exec capabilities by design. The developers knew this was risky, so they implemented a safety mechanism. They decided to act as a nanny, filtering out "bad words" before passing the command to the system shell.
Unfortunately, as history has taught us time and time again, trying to secure a shell interpreter by grepping for bad strings is a fool's errand. It’s not just a vulnerability; it’s a fundamental misunderstanding of how command shells parse input.
The core issue lies in the execute_command function. The developers implemented a blacklist—a list of specific strings that are strictly forbidden. If you try to run rm -rf / or mkfs, the Python code spots the substring, wags its finger at you, and returns a security warning.
Here is the logic flaw: Python sees a string of characters, but the Shell sees a programming language. Python checks the input statically before execution. The Shell evaluates the input dynamically during execution. If you can make the input look safe to Python but dangerous to the Shell, you win.
This is a classic "Time of Check to Time of Use" (TOCTOU) logical gap, but specifically regarding parsing context. The Python filter is looking for the literal sequence m-k-f-s. It does not understand that in Bash, $(echo mkfs) is semantically identical to mkfs.
Let's look at the vulnerable code in terminal_controller.py. It’s short, readable, and dangerously naive. It effectively tries to secure the system by checking if a substring exists within the user input.
# Inside terminal_controller.py
def execute_command(self, command: str) -> str:
# The fatal mistake: A static blacklist
dangerous_commands = ["rm -rf /", "mkfs"]
# The check
if any(dc in command.lower() for dc in dangerous_commands):
return "For security reasons, this command is not allowed."
try:
# The execution
result = subprocess.run(
command,
shell=True, # <--- The root of all evil
capture_output=True,
text=True,
timeout=timeout
)
return result.stdout
except Exception as e:
return str(e)The presence of shell=True is the final nail in the coffin. It tells Python to pass the entire string to /bin/sh (or cmd.exe) for parsing. This enables piping, redirection, and variable expansion—features that the simple if statement above completely ignores.
To exploit this, we don't need buffer overflows or heap spraying. We just need to be creative with string concatenation. We want to execute mkfs (make file system) to wipe a drive, but we can't type "mkfs".
We can use shell command substitution $() to construct the command character by character. The Python filter will see a bunch of echo commands, which aren't on the blacklist. The Shell, however, will execute the inner commands first, assemble the string, and then execute the result.
The Attack Payload:
echo "$($(echo -n m; echo -n k; echo -n f; echo -n s))"The Execution Flow:
$().echo -n m, echo -n k, etc. The result is the string mkfs.This technique is robust and works against almost any keyword-based filter that doesn't account for shell meta-characters.
Because terminal-controller-mcp is designed to run shell commands, the impact of bypassing the filter is effectively total system compromise. The severity is rated Critical (10.0) for a reason.
If this package is running inside a Docker container, the attacker has root inside that container. If the container is privileged or shares mounts with the host, escape is likely. If the user installed this locally to let their AI assistant "help with coding," the attacker now has the same permissions as that developer—access to SSH keys, source code, and AWS credentials.
This isn't just about wiping drives with mkfs. An attacker can just as easily construct a reverse shell using the same obfuscation techniques to maintain persistent access without ever triggering the "dangerous command" filter.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
terminal-controller-mcp GongRzhe | <= 0.1.7 | TBD |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-78 (OS Command Injection) |
| CVSS v3.1 | 10.0 (Critical) |
| Attack Vector | Network |
| Privileges Required | None |
| Exploit Status | Proof-of-Concept Available |
| EPSS Score | 0.00682 |
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.
A missing authorization vulnerability (CWE-862) exists in Open WebUI from version 0.8.12 before 0.10.0. This flaw allows authenticated non-administrative users to access restricted underlying backend models via specific task endpoints, bypassing configured model permissions.
An authorization bypass and cross-session cache leakage vulnerability exists in the model-listing backend of Open WebUI. The flaw stems from a configuration error in the @cached decorator of the aiocache library, which maps all unique user session queries to a single static cache key.
A Broken Object Level Authorization (BOLA) / Insecure Direct Object Reference (IDOR) vulnerability in Open WebUI prior to v0.10.0 allows authenticated users to access and disclose private message contents, thread context, and channel metadata from other restricted private or Direct Message (DM) channels without proper authorization.
Open WebUI versions starting from 0.7.0 up to, but excluding, 0.10.0 are vulnerable to a sensitive data exposure flaw in the channels API. The GET /api/v1/channels/{id}/members endpoint exposes full user database representations, including private API keys and webhook configurations. This allows authenticated users to extract private credentials of other members within the same channel.
CVE-2026-59219 identifies a session-revocation bypass vulnerability in Open WebUI versions 0.9.0 through 0.9.99. While standard HTTP REST endpoints enforce stateful JWT revocation using a Redis-backed blacklist, WebSocket and Socket.IO endpoints bypassed these checks. Consequently, a token revoked via sign-out or OIDC logout remains valid for establishing real-time communication channels and accessing server terminal proxies.
CVE-2026-59864 is a critical path traversal vulnerability in Microsoft Kiota occurring when custom OpenAPI extensions specify a nested static template file. Lack of input sanitization allows malicious inputs to write directory traversal sequences directly into plugin manifests, causing out-of-package local file disclosure in downstream environments like Microsoft 365 Copilot.