system: OPERATIONAL
← back to all hacks
AGENTS MEDIUM NEW

MS-Agent's shell tool: a regex denylist turns prompt injection into RCE

CVE-2026-2256 lets attacker-controlled content steer ModelScope's MS-Agent into running OS commands. The root cause is a familiar anti-pattern: guarding a shell tool with a regex denylist instead of an allowlist.

2026-06-08 // 6 min affects: modelscope-ms-agent

What is this?

CVE-2026-2256 is a command-injection flaw in ModelScope’s MS-Agent, an open-source framework for building tool-using AI agents. It was disclosed through CERT/CC vulnerability note VU#431821, published 2 March 2026, and reported by researcher Itamar Yochpaz. ModelScope was notified on 15 January 2026; according to CERT/CC, no vendor statement or patch was obtained during coordination. SentinelOne records the affected range as v1.6.0rc1 and earlier and classifies the issue as CWE-77 (command injection).

The interesting part is not that an agent framework has an RCE. It’s how the bug is reached: not by an operator typing a malicious command, but by untrusted content — a document, a code repository, a log, a research input — steering the agent into running one. This is indirect prompt injection terminating in a real shell.

How it works

MS-Agent ships a Shell tool that lets an agent run operating-system commands to complete tasks. The tool does attempt to be safe: a check_safe() method filters dangerous commands. The problem is how it filters — with a regular-expression denylist.

Denylists for shell input are a well-known dead end. There are too many ways to express the same effect: encoding, command obfuscation, alternative shell syntax, trusted interpreters, and ordinary shell parsing semantics all give an attacker routes the pattern never anticipated. CERT/CC’s note is blunt about it: “Denylist-based filtering is inherently fragile and can often be bypassed through encoding, command obfuscation, or alternative shell syntax.”

The attack chain is:

untrusted content (doc / repo / log / web result)
        │  contains text that nudges the agent…

agent decides to use the Shell tool
        │  builds a command string containing attacker-influenced text

check_safe() regex denylist  ──►  [BYPASSED]


shell executes the string with the agent process's privileges

No payload is reproduced here. The shape is what matters: the agent itself assembles and submits the command as part of its normal execution flow, so there is no “operator misuse” to point at. The filter sees a string it judged benign; the shell sees executable logic.

Why it matters

Once the denylist is bypassed, the attacker runs commands with the privileges of the MS-Agent process. CERT/CC and the reporting describe the realistic blast radius: reading secrets such as API keys, tokens and config files; dropping payloads; modifying workspace state; establishing persistence; pivoting to internal services; and poisoning build outputs or reports consumed downstream.

A note on scoring: SentinelOne lists a CVSS of 6.5 (medium), with low confidentiality and integrity impact in the vector. Treat that as a floor, not a ceiling. The CVSS base metrics don’t capture the agentic context — an agent that ingests external content and holds credentials is exactly the “lethal trifecta” setup (private data + untrusted input + a way to act), and there the practical outcome escalates toward host compromise. The medium rating reflects a narrow technical view; the deployment reality is more serious.

The broader lesson generalises well beyond MS-Agent. Any agent that exposes a shell, code-exec, or file tool and tries to make it safe by blocking known-bad strings is repeating this mistake. Tool-call boundaries are an authorization problem, not a pattern-matching problem.

Defenses

  1. Replace denylists with allowlists. Do not try to enumerate dangerous commands. Enumerate the small set of permitted operations and reject everything else. This is the single change that fixes the class of bug, not just one bypass.
  2. Avoid shell=True. Where a shell tool is unavoidable, execute argument vectors directly (no shell interpretation) so parsing semantics can’t be turned into injection. Never concatenate model- or content-derived text into a shell string.
  3. Sandbox and least-privilege the tool. Run agents with shell capability in a container with dropped capabilities, read-only filesystem, no new privileges, and an MAC profile (AppArmor/SELinux). If the process can’t read your secrets, a bypass can’t exfiltrate them.
  4. Treat all ingested content as untrusted. Documents, repos, logs and web results are attacker-controlled by default. Only run MS-Agent against content you trust, validate, or sanitise — exactly what the CERT/CC advisory recommends.
  5. Break the lethal trifecta. Don’t co-locate private-data access, untrusted input, and an action-capable tool in one unsupervised agent. Separate privileges, or gate tool calls behind a human or a policy check.
  6. Monitor for the bypass. Log the command strings the agent submits and alert on shell metacharacters (;, |, &&, $(), backticks), unexpected child processes spawned by the agent, and execve()-style calls with suspicious arguments.

Status

ItemReferenceDateNotes
CVE-2026-2256VU#4318212026-03-02Command injection in MS-Agent Shell tool via check_safe() denylist bypass
Affected versionsSentinelOne2026-03ModelScope ms-agent v1.6.0rc1 and earlier; CWE-77; CVSS 6.5 (medium)
Vendor responseVU#4318212026-03-02Notified 2026-01-15; no vendor statement or patch obtained during coordination

The takeaway for builders: a shell tool guarded by a regex denylist is not guarded. If your agent can run commands, the boundary you actually control is the process’s privileges and the trustworthiness of what it reads — design those, and assume the string filter will fail.

Sources