FlawOpen

flawopen.com/Teardowns/cve-2026-102422-shell-quote-comment-line-terminator-injection

● CVE-2026-102422 · CVSS 8.1 · Высокий
Исследования · FlawOpen

CVE-2026-102422: внедрение через токен комментария в shell-quote

До shell-quote 1.11.0 токен комментария, за которым следовала строка с разделителем строки, мог изменить смысл заключённого в кавычки вывода при передаче POSIX-оболочке. Для этого нужны токены, на которые влияет атакующий, вызов quote() и выполнение результата оболочкой; обновитесь до 1.11.0 или новее.

💡 Простыми словами (ELI5)

Комментарий на этикетке может скрыть всё до конца строки. Если следующая часть содержит перевод строки, комментарий заканчивается раньше, и остальной текст снова становится видимым. Эта ошибка позволяла последующему токену вызывать такой эффект в выводе для оболочки.

Ключевые понятия и термины

shell-quote
Пакет JavaScript для разбора и экранирования токенов в стиле оболочки; результат предназначен для синтаксиса оболочки, а не универсального безопасного текста.
Токен комментария
Токен синтаксиса комментария оболочки, действие которого может охватывать следующий текст до разделителя строки.
Разделитель строки
Граница строки, например CR, LF, U+2028 или U+2029, которую оболочка может считать концом комментария.
CWE-78
Недостаточная нейтрализация специальных элементов команды операционной системы.

Связи со стандартами

  • Ссылка CWE для слабого места, описанного в этом руководстве: CWE-78

Анализ первопричины

Уязвимый путь quote() отклонял разделители строк внутри самого токена комментария, но не запоминал, что комментарий уже был выведен. Поэтому последующий строковый токен мог содержать разделитель строки. Если токены под влиянием атакующего проходят через quote(), а результат выполняется POSIX-оболочкой, граница строки завершает комментарий, и оболочка разбирает следующий текст как синтаксис. Исправление запоминает состояние комментария и отклоняет разделители строк во всех последующих строках. Это отличается от CVE-2026-9277, исправление которой отклоняло разделители внутри токена комментария. Разработчики указывают уровень High и CVSS 3.1 8,1; Ubuntu отдельно публикует оценку CVSS 4.0 9,2 Critical. Оценки используют разные версии CVSS и не взаимозаменяемы.

Пошаговый сценарий атаки

Шаг 1

Токены поступают в функцию экранирования

Приложение формирует токены с комментарием и последующей строкой; атакующий должен иметь возможность влиять на эту последовательность.

Шаг 2

Функция выводит синтаксис комментария

До 1.11.0 quote() проверяла текст комментария, но не учитывала следующие за ним строки.

Шаг 3

В последующем токене есть граница строки

CR, LF, U+2028 или U+2029 могут завершить комментарий оболочки.

Шаг 4

Приложение запускает результат через оболочку

Документированная интерпретация касается вызывающих сторон, передающих результат POSIX-оболочке.

Шаг 5

Обновление отклоняет опасную последовательность

Версия 1.11.0 запоминает комментарий и выдаёт ошибку, если последующая строка содержит разделитель.

Исходный код: Уязвимый vs Защищённый вариант

Уязвимый фрагмент проверяет разделители строк в токене комментария, но не сохраняет состояние для последующих строк. Изменение upstream запоминает вывод комментария и отклоняет последующую строку с разделителем. Фрагмент показывает границу разбора и экранирования; это не эксплойт и не команда оболочки.

Фрагмент исходного кода: quote.js — focused upstream diff hunks

УЯЗВИМАЯ РЕАЛИЗАЦИЯ
// quote.js до коммита 6002b2e — фрагмент обработки токена комментария
if ('comment' in s && typeof s.comment === 'string') {
  if (LINE_TERMINATORS.test(s.comment)) {
    throw new TypeError('`comment` must not contain line terminators');
  }
  return '#' + s.comment;
}
БЕЗОПАСНЫЙ ИСПРАВЛЕННЫЙ ВАРИАНТ
// quote.js после коммита 6002b2e — точный фрагмент новой проверки и состояния комментария
var sawComment = false;
return xs.map(function (s) {
  if (sawComment && typeof s === 'string' && LINE_TERMINATORS.test(s)) {
    throw new TypeError('a token after a `comment` must not contain line terminators');
  }
  if ('comment' in s && typeof s.comment === 'string') {
    if (LINE_TERMINATORS.test(s.comment)) {
      throw new TypeError('`comment` must not contain line terminators');
    }
    sawComment = true;
    return '#' + s.comment;
  }
});

Чек-лист по защите системы для инженеров

Источники