РАЗБОР
На проверке
Атаки через escape-последовательности терминала
Режим чтения
[ обложка статьи ]
На пентесте внутренней инфраструктуры финтеха я получил реверс-шелл и полез читать логи веб-приложения через
less. Через минуту терминал начал вести себя странно - заголовок окна tmux сменился, часть вывода исчезла. Запуск cat -v на том же лог-файле показал россыпь ^[[ последовательностей, вшитых в user-agent строки. Кто-то до меня уже побывал на этом хосте и заминировал логи ANSI escape-кодами. С того момента любой текстовый вывод на чужой машине я прогоняю через xxd - и за полтора года собрал каталог техник, которые работают в реальных сценариях post-exploitation, lateral movement и - неожиданно - атак на LLM-инструменты разработчиков.ANSI escape-коды как вектор атаки на CLI
Управляющие последовательности ANSI начинаются с байта ESC (0x1B) и интерпретируются терминальными эмуляторами как команды: сменить цвет текста, переместить курсор, очистить экран, записать данные в буфер обмена. Стандарт восходит к ECMA-48 (пятая редакция - 1991 год) и реализован практически во всех современных эмуляторах - от gnome-terminal и iTerm2 до Windows Terminal и встроенного терминала VS Code.Проблема безопасности CLI в том, что терминал не различает «легитимную» и «враждебную» последовательность. Он слепо исполняет всё, что попадает в stdout или stderr. Для пентестера это значит: любой текст, который жертва читает в терминале - логи, вывод
git log, результат kubectl get events, сообщение от wall - может содержать инъекцию управляющих последовательностей. Терминал отрисует эффект (скроет строку, подменит prompt, запишет payload в clipboard), а пользователь увидит только результат. Или не увидит - что ещё хуже.Структура большинства CSI-кодов:
ESC [ <параметры> <буква-команда>. Например, \033[3A - перемещение курсора вверх на 3 строки, \033[2J - полная очистка экрана, \033[K - удаление от курсора до конца строки. Отдельная история - OSC (Operating System Command), формат \033] <код> ; <данные> \a. Через OSC управляют заголовком окна, буфером обмена и даже гиперссылками.По MITRE ATT&CK terminal injection ложится сразу на несколько техник: Command Obfuscation (T1027.010) для маскировки payload в текстовых данных, Masquerading (T1036) для подмены легитимного вывода, Clear Command History (T1070.003) для затирания визуальных следов через управление экраном.
CVE с инъекцией управляющих последовательностей в production
Три CVE из разных экосистем хорошо показывают масштаб. Общий знаменатель - CWE-150 (Improper Neutralization of Escape, Meta, or Control Sequences); CVE-2024-52005 дополнительно классифицирована как CWE-116 (Improper Encoding or Escaping of Output). Во всех случаях разработчики фильтровали escape-коды в одном канале ввода и забывали про другой. Классика.CVE-2024-28085: подмена sudo-промпта через wall
Утилитаwall из пакета util-linux (до версии 2.40) блокировала escape-последовательности во входном потоке stdin, но пропускала их в аргументах командной строки. CVSS 3.3 (LOW), вектор CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N - локальный доступ с минимальными привилегиями, без взаимодействия пользователя.Сценарий эксплуатации: атакующий на том же хосте отправляет через
wall сообщение с escape-последовательностями, которые имитируют приглашение sudo в терминале жертвы. Жертва вводит пароль, и он «засвечивается» как несуществующая команда. Обработчик command-not-found передаёт введённое в аргументы процесса - атакующий непрерывно поллит /proc/*/cmdline в узком временном окне между вводом и завершением обработки shell и читает пароль (race condition снижает надёжность, но на практике окно достаточно широкое).Конкретный payload (по данным opennet.ru):
Код:
wall $(printf "\033[3A\033[K[sudo] password for user:\033[?25l\033[38;2;48;10;36m")
\033[3A перемещает курсор вверх, \033[K стирает строку, далее рисуется фейковый prompt sudo, \033[?25l скрывает курсор, а цветовая последовательность маскирует ввод. Атака подтверждена в Ubuntu 22.04 с gnome-terminal в дефолтной конфигурации (mesg выставлен в y по умолчанию). В Debian атака затруднена из-за отключённого command-not-found. Уязвимость существовала с 2013 года (util-linux 2.24) - «тысячи глаз» open source не заметили её 11 лет. Так-что аргумент «open source безопаснее, потому что код читают все» - работает не всегда.CVE-2021-25743: спуфинг вывода kubectl
Kubernetes kubectl не нейтрализует escape-последовательности в сырых данных при выводе в терминал. CVSS 3.0 (LOW), векторCVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:N/I:L/A:N - сетевая доступность, scope changed. Атакующий с правами на создание Events или Pods в кластере внедряет escape-коды, которые отрабатывают в терминале администратора при kubectl get events или kubectl describe pod.По данным BrightSec, уязвимость позволяла спуфить терминальный вывод - подменять содержимое Event-строк так, что администратор видел «чистый» результат вместо реального. Для k8s-админа, который принимает решения по выводу
kubectl, это прямая угроза: он смотрит на экран и думает, что в кластере порядок, а там уже давно всё не так.CVE-2024-52005: Git sideband как канал доставки
Git приclone, fetch или push использует sideband channel для доставки информационных и error-сообщений от удалённого процесса. Сообщения выводятся в stderr с префиксом remote: без нейтрализации управляющих символов. CVSS 7.5 (HIGH) по CVSS v4, CWE-116 и CWE-150.Это самая опасная из тройки: вектор
AV:N, атакующий контролирует git-сервер (или MitM-позицию) и отправляет произвольные escape-последовательности прямо в терминал разработчика при обычном git clone. Жертва запускает git clone, fetch или push (UI:A в CVSS-векторе), после чего sideband-сообщения обрабатываются автоматически. Никаких подтверждений.Техники terminal injection: практический арсенал
Подмена вывода и фишинг через терминал
Базовая техника - комбинация cursor movement (\033[<N>A - вверх на N строк), line erase (\033[K) и рендеринга ложного вывода. Именно так работает payload CVE-2024-28085. Этот подход масштабируется в post-exploitation: если оператор C2 запускает на скомпрометированном хосте команду, которая выводит заранее подготовленный файл, payload может подменить вывод «чистым» результатом. Оператор думает, что проверил хост - а на экране была декорация. Я сам на это однажды чуть не попался.Второй вариант - скрытый текст через SGR (Select Graphic Rendition). Последовательность
\x1B[38;5;231;49m устанавливает белый цвет текста на белом фоне (по данным Trail of Bits, тестирование на Claude Code v0.2.76). Текст остаётся в потоке данных, его читает LLM или пейджер, но пользователь не видит ничего. Невидимые чернила, только цифровые.Третий - backspace (
\b) для «стирания» предыдущих символов. Атакующий выводит вредоносную строку, затем серию \b и «чистую» строку поверх. В терминале отображается только последнее, но при перенаправлении вывода в файл или парсинге через скрипт оба фрагмента сохраняются. Красивый трюк, если нужно пронести payload мимо human review.Clipboard hijack через OSC 52 и утечка данных через DNS
OSC 52 - последовательность\033]52;c;<base64_data>\a - записывает произвольные данные в системный буфер обмена. Поддерживается в iTerm2, tmux, Alacritty и ряде других эмуляторов. По данным EmbracerTheRed, большинство терминалов при записи через OSC 52 удаляют завершающий newline, что предотвращает немедленное выполнение при paste. Но не все - в некоторых эмуляторах newline сохранялся, и paste в шелл означал выполнение кода. Ctrl+V - и привет.Для macOS Terminal есть отдельный вектор: escape-последовательности, инициирующие DNS-запросы без взаимодействия пользователя. Уязвимость описана David Leadbeater на DEF CON 31 - zero-click утечка данных из контекста терминальной сессии через DNS-канал.
Современные терминалы поддерживают гиперссылки через
\033]8;;URL\033\текст\033]8;;\033\. Атакующий может отобразить ссылку с текстом legitimate.example.com, но ведущую на phishing.example.com. По данным Trail of Bits, VS Code при клике запрашивает подтверждение - но это поведение не стандартизировано, и полагаться на него не стоит.Инъекция escape-последовательностей через LLM и MCP
Свежий вектор - terminal injection через LLM-powered CLI-инструменты. Когда приложение отправляет запрос к модели и печатает ответ в stdout без фильтрации, LLM становится проводником для управляющих кодов. По сути - прокси для escape-инъекции с сетевым плечом.По данным EmbracerTheRed, многие LLM генерируют байт ESC (ASCII 27) двумя способами: через tool invocation (Code Interpreter) или напрямую через промптинг. Более того, LLM способны читать управляющие символы и просто отражать их обратно, что делает атаку надёжнее.
Trail of Bits протестировал Claude Code (v0.2.76) и обнаружил: Anthropic не фильтровал escape-последовательности ни в описаниях инструментов, ни в выходных данных MCP-серверов. Атакующий встраивал в ответ MCP-сервера последовательность, делающую текст невидимым, а скрытая инструкция направляла модель загружать пакеты с подконтрольного сервера - прямая атака на supply chain. Человек смотрит в терминал, видит чистый вывод, а модель в это время тянет зависимости с левого репозитория.
BrightSec выделяет два варианта AESI (ANSI Escape Sequence Injection) в MCP:
Direct-fetch AESI - MCP-сервер загружает URL с escape-кодами и передаёт содержимое модели. Payload срабатывает в model-consumable полях:
result.content[].text, result.contents[].text, result.messages[].content.text.Stored AESI - payload записывается через один entrypoint (заметка, комментарий), а детонирует при чтении через другой. Персистентность, развязка write/read и отложенная активация делают этот вариант значительно опаснее. По сути - stored XSS, только для терминала.
Суть пересечения двух техник: prompt injection доставляет payload, а ANSI escape-коды скрывают его от human-in-the-loop ревью. Пользователь видит чистый вывод, модель читает и выполняет скрытые инструкции - Data from Local System (T1005) через агента с доступом к инструментам и файловой системе.
Как обнаружить terminal injection и защитить CLI
Первый инструмент - привычка. Любой нестандартный вывод на чужой машине проверяйте черезcat -v (заменяет управляющие символы видимыми последовательностями) или xxd для hex-дампа. Если автоматизируете post-exploitation - добавьте проверку на \x1b:
Bash:
# Проверка файла на скрытые escape-последовательности
# -P (PCRE) может отсутствовать в BusyBox/embedded; альтернатива: grep -q $'\x1b' или od -c | grep 033
if grep -qP '\x1b' "$file"; then
echo "[!] ESC-байты обнаружены в $file"
cat -v "$file" | grep -n '\^\['
fi
less параметр -R (только SGR для цвета) безопаснее -r (все raw-последовательности). С -r терминал исполнит cursor movement и screen clear; с -R - только цветовое форматирование. Разница между ними - разница между «посмотрел логи» и «логи посмотрели тебя».Для записи и анализа терминальных сессий
script или asciinema сохраняют сырой поток со всеми управляющими символами - повторный просмотр через cat -v покажет то, что было скрыто в реальном времени. На бумаге формула понятна, но escape-инъекция по-настоящему ощущается только когда сам видишь, как cat показывает одно, а cat -v - совсем другое. На HackerLab.pro в категориях web и forensics есть задачи, где нужно разбирать подобные скрытые каналы в данных.Trail of Bits предлагает минимальную защиту: заменять любой байт
0x1b на placeholder перед выводом. Все escape-последовательности в современных терминалах начинаются с этого байта - одна замена нейтрализует весь класс атак. EmbracerTheRed опубликовал функции для Python и Go, преобразующие вывод в caret notation - аналог cat -v для приложений. Решение простое, но я пока не видел ни одного C2-фреймворка, который делает это из коробки.Таблица предусловий и применимости
Для CI/CD и production: мониторинг Event-объектов Kubernetes на наличие
\x1b (покрывает CVE-2021-25743), проверка sideband-сообщений в git-хуках (CVE-2024-52005), encode-by-default для всего вывода LLM в CLI-инструментах.Маппинг terminal injection на MITRE ATT&CK
Terminal injection - не отдельная техника в ATT&CK, но она усиливает существующие:| Техника ATT&CK | Тактика | Как применяется terminal injection |
|---|---|---|
| Command Obfuscation (T1027.010) | Defense Evasion | Escape-коды скрывают payload от human review в логах и выводе |
| Masquerading (T1036) | Defense Evasion | Подмена вывода CLI создаёт ложное представление о системе |
| Clear Command History (T1070.003) | Defense Evasion | Очистка экрана через \033[2J затирает визуальные следы |
| Data from Local System (T1005) | Collection | Утечка через DNS/clipboard/hyperlinks из терминальной сессии |
| Unix Shell (T1059.004) | Execution | Payload в clipboard через OSC 52 выполняется при paste в шелл |
| Malicious File (T1204.002) | Execution | Заминированный лог-файл срабатывает при просмотре в less/cat |
Сигнатурный поиск
\x1b[ в SIEM-правилах генерирует шум из-за легитимного цветного вывода ls --color, grep --color и подобных утилит. Эффективнее мониторить конкретные опасные подмножества: OSC 52 (\x1b]52;), title change (\x1b]0;), cursor movement в контексте логов, screen clear (\x1b[URL='https://codeby.net/threads/bulkhead-avtoobnaruzheniye-path-traversal-pri-container-escape-cherez-semanticheskii-analiz.94739/']2J) в нестандартных для этого приложениях.Terminal injection - одна из самых недооценённых техник в offensive-арсенале. CVSS у описанных CVE - от 3.0 до 7.5, security-команды классифицируют их как low priority. Но CVSS не учитывает контекст: escape-последовательность, внедрённая в лог-файл на скомпрометированном хосте, ослепляет оператора C2 или IR-аналитика, который принимает решения на основе того, что видит в терминале. А видит он ровно то, что ему показали.
С ростом LLM-powered CLI-инструментов поверхность атаки будет только расти. MCP-серверы уже транслируют ответы моделей прямо в stdout без санитизации - повторяя ошибку util-linux десятилетней давности, только на сетевом уровне и с правами агента на машине разработчика. Через год-два эти инструменты станут стандартом, и тот, кто не научился прогонять подозрительный вывод через
cat -v, будет слепым в собственном терминале. На пентестах я стал обращать внимание на [заминированные escape-кодами логи[/URL] чаще, чем ожидал, - потому что escape-коды не ловит ни один EDR, а SIEM-правила на \x1b дают шум из-за легитимного цветного вывода. Detection gap здесь шире, чем для большинства техник из ATT&CK, и закрывать его пока некому. На HackerLab лежит сценарий, где этот primitive нужно собрать в полную цепочку.
AI-выжимка
сгенерировано ИИ
Тезисы статьи скоро
Статья читается полностью. Тезисы со ссылками на разделы появятся позже.
Содержание
Codeby Academy
Практика и мастерство
От основ до продвинутого — программы для практиков Codeby.
Перейти к курсу →
Поиск в обсуждении
Карта ветки
Продолжить чтение
Следующий разбор
CVE-2026-60004: RCE в Gitea от регистрации до shell
Ещё по теме
- Статья
- Статья
- Статья
Комментарии
0