Raw capture โ raw/ansi-escape-injection-mcp.md
url: https://brightsec.com/research/detecting-ansi-escape-sequence-injection-in-mcp-servers-with-dast/ title: Detecting ANSI Escape Sequence Injection in MCP Servers with DAST author: Bright Security date_fetched: 2026-07-21 date_published: unknown
ANSI escape sequences are invisible control codes that tell a terminal to change colors, hide text, clear the screen, or move the cursor. A human reading rendered terminal output never sees the codes, but a language model reads every byte โ and that gap is the core of the attack.
Two real-world CVEs as precedent: Kubernetes kubectl CVE-2021-25743 (control sequences in Event strings spoofing terminal output) and Git CVE-2024-52005 (remote sideband messages printed without neutralizing terminal controls).
In the Model Context Protocol (MCP), where servers stream text into an agent, "every field the model reads is a potential injection surface."
The article introduces the term ANSI Escape Sequence Injection (AESI) and covers two variants: direct-fetch AESI and stored AESI.
ANSI X3.64 standard unified terminal control across incompatible hardware terminals. An escape sequence starts with ESC (hex 0x1B) followed by a command code. Two categories:
"Rendering hides these bytes from people, but a model processes the raw text."
Many MCP servers expose a tool that fetches a URL and returns its contents. The attack flow:
Model-consumable fields are the specific response locations an agent ingests:
- tool results โ text inside result.content[].text
- resource reads โ text inside result.contents[].text
- prompt templates โ text inside result.messages[].content.text
"A payload that surfaces anywhere else is not exploitable." This is a form of cross-prompt injection.
The attacker writes the payload into storage through one entrypoint and it detonates later when a different entrypoint reads that data back into a model field.
Three properties making stored AESI worse: 1. Persistence โ payload sits in storage and fires again on future sessions, potentially against other users. 2. Decoupling โ write and read are different entrypoints, possibly using different protocols (e.g., written via HTTP, read via MCP). 3. Delayed exposure โ nothing looks wrong at write time; injection only activates on a later, unrelated read.
"You cannot infer these write-then-read paths from parameter names, tool descriptions, or protocol type."
"One poisoned record in a shared knowledge base can influence every later agent workflow that reads it."
Detection is binary โ the three-signal rule holds or it doesn't.
A small corpus of hosted payload files exercises different ANSI techniques: concealment, background-matched foreground, clear-screen, cursor-erase, and an HTML-wrapped variant, each with its own trigger. "Variety matters because a server that strips one technique may still relay another."
Targets MCP action-request entrypoints (tools/call, resources/read, prompts/get) with at least one parameter. Treats a broad set of text parameter types (string, URL, email, phone, file-URI) as injectable.
Steps: 1. Inject โ substitute the payload's hosted URL into an injectable text parameter and invoke the entrypoint. 2. Capture โ record the full MCP response as raw bytes; unsuccessful or JSON-RPC error responses are skipped. 3. Classify โ walk model-consumable fields, apply the three-signal rule. 4. Report โ file one issue per vulnerable parameter, recording the payload URL and field path.
Phase 1 โ Probe: Assign one unique inert marker to each injection entrypoint and write it embedded in benign carrier text. Injection candidates are MCP tools/call or prompts/get entrypoints with a text parameter, or HTTP POST/PUT/PATCH endpoints with a writable text parameter.
Phase 2 โ Map reflections: Read back every reflection candidate and scan raw responses for probe markers. When a marker is found, the engine records the exact JSON field path. The result is a map from injection entrypoints to read surfaces and field paths.
Phase 3 โ Attack and confirm: For each mapped path, write the real AESI payload, re-read the mapped MCP reflection surfaces, and apply the classifier only at the recorded field path.
The probe phase "converts an unknown source-to-sink topology into a concrete, field-path-anchored map."
"Effective detection has to model the data flow, not just individual requests."
"AESI shows that AI agents inherit every ambiguity of the formats they consume: a 1970s cursor code becomes an injection primitive the moment a model, not a terminal, reads the bytes." As MCP feeds agents more untrusted text, future attacks will come less from novel exploits than from "old representation quirks reaching a consumer never meant to interpret them."
"Detection belongs in DAST because survival depends on what the live server fetches, stores, strips, and resurfaces โ none of it visible from the code. The concealment that makes AESI dangerous to a human reviewer is exactly what makes it tractable to automate: the model sees every byte, and so should the scanner."