РАЗБОР На проверке 

Атаки через escape-последовательности терминала

Сергей Попов
Сергей Попов Red Team · 6,4 тыс. сообщений
Подписаться
60
Режим чтения
Тёмный стол с ноутбуком в свете лампы: терминал tmux показывает искажённый вывод с escape-последовательностями и надписью CVE-2024-28085. Рядом рука в синей перчатке касается щупом плат одноплатног...


На пентесте внутренней инфраструктуры финтеха я получил реверс-шелл и полез читать логи веб-приложения через 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-фреймворка, который делает это из коробки.

Таблица предусловий и применимости​

📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме


Для 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 EvasionEscape-коды скрывают 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)ExecutionPayload в 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 нужно собрать в полную цепочку.
Полезно

Комментарии

0