ANSI escape injection in MCP servers: Hidden from humans, visible to AI

ANSI escape sequence injection in MCP servers poses a severe security risk by exploiting the gap between human rendering and AI text processing, allowing attackers to conceal instructions from humans while revealing them to AI models.
Detecting ANSI Escape Sequence Injection in MCP Servers with DAST
ANSI escape sequences were designed for terminals, not for language models. They are invisible control codes that tell a terminal to change colors, hide text, clear the screen, or move the cursor. A human reading a rendered terminal never sees the codes themselves — only their effect. A language model reading the same content sees every byte.
That gap is the whole attack. If an attacker can smuggle ANSI escape sequences into text that an AI agent consumes, they can hide instructions from the human reviewing the output while leaving those instructions perfectly legible to the model. This is the same class of output-neutralization failure seen in real products: Kubernetes kubectl (CVE-2021-25743) accepted control sequences in Event strings that could spoof terminal output, and Git (CVE-2024-52005) printed remote sideband messages without neutralizing terminal controls. In the Model Context Protocol (MCP) — where servers expose tools, resources, and prompts that stream text straight into an agent — every field the model reads is a potential injection surface.
DAST is the natural way to catch this class of flaw. The Bright scanner exercises a running target from the outside, sends crafted inputs, and inspects real responses for evidence of a vulnerability — no source access required. That black-box, runtime posture is exactly what ANSI Escape Sequence Injection (AESI) demands, because whether a payload survives depends entirely on how the live server processes and relays it. This article covers two variants of the attack, direct-fetch AESI and stored AESI, what they put at risk, and, most importantly, how a DAST scanner detects them automatically.
What ANSI escape sequences are
ANSI escape sequences date back to the 1970s, when hardware terminals from different vendors each spoke their own incompatible control dialect. The ANSI X3.64 standard unified them: a program could emit one agreed-upon set of codes to move the cursor, clear the screen, or set colors, and any conforming terminal would obey. They were invented to make text terminals controllable in a portable way — and that same control capability is what the attack abuses today.
An ANSI escape sequence starts with the escape byte (ESC, hex 0x1B) followed by a control code the terminal treats as a command rather than printable text. Two categories matter here: concealment codes that make text invisible, and screen and cursor codes that clear the display, move the cursor, or erase a line to scrub or overwrite legitimate-looking output.
The key insight: rendering hides these bytes from people, but a model processes the raw text. What looks like an empty line or a tidy report to a human can carry a full set of instructions to the agent.
Attack 1: Direct-fetch AESI
Many MCP servers expose a tool that fetches a URL and returns its contents to the model — a “summarize this page” or “read this document” capability. The attack works like this:
- The attacker hosts content laced with ANSI escape sequences and hidden instructions at a URL they control.
- That URL is supplied to a fetch-style tool as an argument.
- The server retrieves the content and relays it into a model-consumable field — a response location the agent actually ingests.
- The model reads the concealed instructions and acts on them.
The concept to hold onto here is the model-consumable field. Not every part of an MCP response reaches the model. What matters are the specific fields an agent treats as content:
- tool results — the text inside result.content[].text
- resource reads — the text inside result.contents[].text
- prompt templates — the text inside result.messages[].content.text
A payload that surfaces anywhere else is not exploitable. This is a form of cross-prompt injection: the malicious instructions ride in on data the agent was asked to process, not on the user’s own prompt.
Attack 2: Stored AESI
Stored AESI is the same payload with a more dangerous delivery mechanism. Instead of the server fetching a live URL, the attacker writes the payload into storage through one entrypoint — a note, a comment, a record — and it detonates later when a different entrypoint reads that data back into a model field.
Three properties make the stored variant worse than direct-fetch:
Persistence. The payload sits in storage and fires again on future sessions, potentially against other users. Decoupling. The write and the read are different entrypoints, and they can even use different protocols. A payload written through an ordinary HTTP endpoint can resurface when the agent reads the same record back through an MCP tool or resource. Delayed exposure. Nothing looks wrong at write time. The injection only becomes active when some later, unrelated read pulls the stored text into the model’s context.
Crucially, you cannot infer these write-then-read paths from parameter names, tool descriptions, or protocol type. The only reliable way to know where written data resurfaces is to probe for it empirically — which is exactly what the automated detection does.
Security impact
Unauthorized agent actions. Concealed instructions can cause the model to invoke tools, disclose data, alter analysis, or produce attacker-controlled output within the agent’s existing privileges.
Human-in-the-loop bypass. ANSI concealment can hide attacker instructions from the user supervising or approving the agent. The visible response appears benign, while the model still receives the raw payload and may follow it.
Log and audit trail manipulation. Screen-clear, cursor-movement, and line-erasure sequences can spoof terminal or log-viewer output, obscuring activity during review and incident response.
Persistent injection risk. In stored AESI, the payload remains in application data and can reappear through later MCP tools, resources, or prompts. A single write can affect future sessions, other users, and cross-protocol read paths.
In practice, one poisoned record in a shared knowledge base can influence every later agent workflow that reads it. The risk is not just a misleading summary; it is attacker-controlled data reaching an agent with access to tools, credentials, and downstream systems.
Automating detection with DAST
The heart of this work is turning the attack into a repeatable, automated test. The detection is built on a few design principles, then applied through two pipelines — one for direct-fetch, one for stored.
Design principles
Inspect raw bytes. The ANSI escape bytes are the signal, so any rendering, sanitizing, or normalizing before inspection destroys it. Detection reads the raw HTTP/MCP response bytes. Only model-consumable fields count — the sink. The classifier walks the JSON paths a model actually ingests (tool content, resource contents, prompt message text) and ignores everything else. Unique trigger markers. Each payload carries its own improbable marker, so a match ties an effect back to exactly one injection — the mechanism black-box confirmation depends on. Three-signal confirmation. A field is vulnerable only when an ANSI escape byte, a fixed instruction phrase, and the payload’s trigger all survive together in the same model-consumable field. Requiring all three is what suppresses false positives. Recurse into encoded values. The classifier descends into stringified JSON so a payload wrapped inside a JSON-encoded field is still caught.
Source: Hacker News















