CVE-2025-61492

Terminal Velocity: Bypassing MCP "Security" with CVE-2025-61492

Amit Schendel
Amit Schendel
Senior Security Researcher

Jan 9, 2026·5 min read

Executive Summary (TL;DR)

The developers of `terminal-controller-mcp` tried to secure their tool by blacklisting strings like "rm -rf". They failed to account for how shells actually work. By using command substitution and string concatenation, an attacker can construct any blacklisted command (e.g., `mkfs`) at runtime, completely bypassing the filter and executing arbitrary code with the privileges of the MCP server.

A Critical (10.0) command injection vulnerability in terminal-controller-mcp allows attackers to bypass a naive blacklist filter using basic shell obfuscation, granting full Remote Code Execution (RCE).

The Hook: Giving Robots Shell Access

The Model Context Protocol (MCP) is the new hotness, designed to let AI models interface directly with local tools and data. It's a great idea until someone decides to write a tool called terminal-controller-mcp. As the name implies, this tool literally gives an AI agent (and by extension, anyone controlling the prompt or the API connection) a direct line to your system's shell.

The developers weren't completely reckless. They knew that giving a robot raw access to /bin/bash might lead to accidental (or intentional) chaos. So, they implemented a security layer. A gatekeeper. A digital bouncer designed to stop the bad commands and let the good ones through.

Unfortunately, this bouncer is easily distracted by shiny objects and basic linguistics. Instead of implementing a secure execution environment, they relied on the oldest, most brittle defensive pattern in the book: the blacklist.

The Flaw: The Blacklist Fallacy

Root cause analysis often reveals complex memory corruption or race conditions. Here, the root cause is a fundamental misunderstanding of how command interpreters work. The developers implemented a check that looks for specific "dangerous" substrings within the user's input.

The fatal flaw is assuming that a command is defined by its static string representation. In a shell environment, a command is dynamic. cat /etc/passwd is the same as c''at /et??/pas*wd.

The application filters the input string, but the shell executes the interpreted result. If the input string doesn't contain the letters "m", "k", "f", "s" in that exact contiguous order, the filter passes it. But the shell, being helpful and flexible, allows us to construct that string on the fly using variables, substitution, or concatenation.

The Code: Pythonic Suicide

Let's look at the smoking gun in terminal_controller.py. This is the actual logic used to protect the system:

# The "Security" Logic
dangerous_commands = ["rm -rf /", "mkfs"]
 
def execute_command(command):
    # Check if the command is naughty
    if any(dc in command.lower() for dc in dangerous_commands):
        return "For security reasons, this command is not allowed."
    
    # Fire ze missiles!
    # (Implied execution context passing this to os.system or subprocess with shell=True)
    return run_shell(command)

This code performs a case-insensitive substring search. If you type mkfs, you get blocked. If you type rm -rf /, you get blocked.

But notice what isn't blocked: echo, printf, $(), |, ;. By failing to sanitize the mechanism of execution itself (the shell meta-characters) and focusing only on specific payloads, the code creates an illusion of security that vanishes the moment you use a sub-shell.

The Exploit: Linguistic Gymnastics

To exploit this, we don't need buffer overflows. We just need to speak Bash. Let's say we want to run the forbidden command mkfs (Make Filesystem - a great way to ruin a server's day).

If we send mkfs, the Python in operator catches us.

Instead, we construct the word "mkfs" using echo and command substitution. The Python filter sees a bunch of echoes. The shell sees the instruction to build a command and run it.

The Payload:

echo "$($(echo -n m; echo -n k; echo -n f; echo -n s))"

The Execution Flow:

  1. Python Filter: Does the string echo "$($(echo -n m... contain mkfs? No. Pass.
  2. Bash Execution:
    • Bash executes the inner sub-shell: echo -n m; ... outputs mkfs.
    • Bash substitutes that result back into the command.
    • The command becomes mkfs.
    • Bash executes mkfs.

This technique works for rm -rf / or any other blacklisted phrase. You can simply base64 encode your payload and pipe it to sh if you want to avoid all character filters entirely.

The Impact: Total Ownership

This is a CVSS 10.0 for a reason. There is no authentication required (assuming the MCP endpoint is exposed or the AI is tricked into using it), and the impact is total compromise of the user running the service.

In a containerized environment, an attacker now has a shell inside the container. From there, they can:

  • Exfiltrate API keys or environment variables.
  • Pivot to other services on the internal network.
  • Perform container escapes if the container is privileged (which, ironically, tools like this often are to access the host filesystem).

For a developer running this locally to give their "Agent" power? The attacker just got a reverse shell on their laptop.

The Fix: Stop Using Shells

The mitigation is simple: Stop letting the shell parse the command.

1. Disable Shell Execution: In Python, use subprocess.run with shell=False (the default) and pass the command as a list of arguments. This prevents shell expansion and command chaining.

# Safe(r) Implementation
subprocess.run(["ls", "-la"], shell=False)

2. Whitelisting: If you must allow command execution, define a strict dictionary of allowed commands and their exact allowed arguments. Never trust a blacklist.

3. Input Validation: Ensure arguments match strict regex patterns (e.g., ^[a-zA-Z0-9._-]+$). If you see a $, ;, or |, reject the request immediately.

Technical Appendix

CVSS Score
10.0/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

Affected Systems

terminal-controller-mcp <= 0.1.7super-shell-mcp <= 2.0.13

Affected Versions Detail

Product
Affected Versions
Fixed Version
terminal-controller-mcp
terminal-controller-mcp
<= 0.1.7TBD
super-shell-mcp
super-shell-mcp
<= 2.0.13TBD
AttributeDetail
CWE IDCWE-77 (Command Injection)
Attack VectorNetwork (JSON-RPC)
CVSS v3.110.0 (Critical)
ImpactRemote Code Execution (RCE)
Exploit ComplexityLow
Privileges RequiredNone
CWE-77
Improper Neutralization of Special Elements used in a Command ('Command Injection')

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.

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.