Как стать автором
Обновить
Positive Technologies
Лидер результативной кибербезопасности

Из-за чего весь сыр-бор: про уязвимость Text4Shell

Время на прочтение7 мин
Количество просмотров7.8K

За последнюю неделю в сфере инфобеза стали появляться новости о втором пришествии уязвимости Log4Shell, получившей название Text4Shell. Первым об уязвимости сообщил Alvaro Muñoz, который рассказал о возможности удаленного выполнения произвольных скриптов в продуктах, использующих библиотеку Apache Commons Text.

Apache Commons Text — это open source компонент, используемый разработчиками для управления символьными строками. Уязвимость была выявлена в версиях 1.5–1.9 и связана с небезопасной интерполяцией переменных. По данным сайта maven repository, библиотека Apache Commons Text используется в 2591 проекте, однако данная оценка не учитывает транзитивные зависимости библиотек.

Сама уязвимость была обнаружена еще в марте 2022 года, но команде Apache Commons потребовалось время на ее исправление и выпуск обновлений библиотеки.

Материалы с первоначальной информацией об уязвимости:

· публикация от вендора;

· пост об уязвимости;

· одно из первых упоминаний об уязвимости в СМИ;

· заметка команды GHSL (лаборатория безопасности GitHub).

Уязвимости был присвоен идентификатор CVE-2022-42889 (CWE-94 — Code Injection) и достаточно высокий уровень риска CVSS 9.8.

Когда уязвимость была обнаружена, некоторые эксперты выразили сомнения в том, что она представляет серьезную опасность. Они ссылаются на то, что уязвимость невозможно эксплуатировать в версиях JDK 15+, а также на то, что попадание пользовательских данных в функцию интерполяции переменной маловероятно. Однако при дальнейшем изучении уязвимости были выявлены и другие векторы ее эксплуатации.

Исследования, которые ставят опасность уязвимости под сомнение:

Наша команда PT Application Inspector решила определить уязвимые места в исходном тексте, оценить выпущенный патч от команды разработки и посоветовать шаги, которые помогут защититься от возможных атак. 

Сначала про причины уязвимости

В функциональные возможности библиотеки заложен механизм интерполяции переменных, то есть вставка в строку значений на основе обработки данных по шаблону. В качестве шаблона по умолчанию для данной библиотеки используется строка ${prefix:[options]:data}, где prefix определяет алгоритм обработки данных из options и data. Если у злоумышленника есть возможность задавать шаблон, он сможет проводить следующие виды атак:

Атака

Prefix

Вектор атаки

Удаленное выполнение кода

script

${script:javascript:java.lang.Runtime.getRuntime().exec('calc')}

Сбор информации через запрос к DNS-серверу атакующего

dns

${dns:address|ptsecurity.com}

Раскрытие внутренней сети, через http (https)-запросы

url

${url:UTF-8:https://www.ptsecurity.com}

Чтение произвольного файла

file

${file:UTF-8:/etc/passwd}

Доступ к переменным окружения, содержащим критичную информацию

env

${env: AWS_ACCESS_KEY_ID}

А что же творится внутри библиотеки

Чтобы разобраться с алгоритмом работы библиотеки, создадим простейшее веб-приложение, использующее уязвимый код. Причем вектор попадает в уязвимую функцию из состава http-запроса параметра taint.

В качестве вектора атаки будем использовать самый критичный вариант (code injection):

${script:javascript:java.lang.Runtime.getRuntime().exec('calc')}

В результате атаки должна быть выполнена команда ОС и запущена программа «Калькулятор».

При анализе кода можно выделить два действия, которые выполняются при использовании библиотеки:

  1. Создание экземпляра объекта StringSubstitutor путем вызова функцииStringSubstitutor.createInterpolator.

  2. Манипулирование строкой, в которой хранится параметр http-запроса. Для этого вызывается функцияinterpolator.replace(taint).

В первом случае при вызове функции StringSubstitutor.createInterpolator выполняется автоматическое заполнение таблицы связей между prefix и классом обработки вызова lookup().

Стек вызовов:

В функции addDefaultStringLookups класса StringLookupFactory в конечном виде формируется HashMap (stringLookupMap), который выглядит следующим образом:

…
"dns" -> {DnsStringLookup@785}
"env" -> {FunctionStringLookup@787}
…
"script" -> {ScriptStringLookup@793}
"url" -> {UrlStringLookup@795}
"file" -> {FileStringLookup@799}
…

Уязвимые префиксы (script, dns, url, file иenv) определены по умолчанию и доступны для использования.

Во втором случае для функции replace (replaceIn) класса StringSubstitutor выполняется вызов функций из стека:

В данном стеке можно выделить вызов функции lookup из класса ScriptStringLookup.Ее вызов возможен потому, что для prefix=script установлена связь с классом ScriptStringLookup.

В зависимости от установленного класса в stringLookupMap для соответствующего значения prefix выполняется соответствующий метод lookup. Перечень критичных методов в зависимости от установленного префикса приведен ниже.

prefix

Класс

Критичный метод

Ссылка на код

script

ScriptStringLookup

scriptEngine.eval(script)

ScriptStringLookup.java#L86

dns

DnsStringLookup

InetAddress.getByName(subValue)

StringLookup.java#L89

url

UrlStringLookup

new URL(urlStr)

UrlStringLookup.java#L75

file

FileStringLookup

Files.readAllBytes(Paths.get(fileName))

FileStringLookup.java#L85

env

StringLookupFactory

System::getenv

StringLookupFactory.java#L281

В нашем примере вызывается scriptEngine.eval(script). В JDK до версии 15 scriptEngine ассоциируется со встроенным скриптовым движком Nashorn, который выполняет скрипт, заданный пользователем. В версиях JDK 15+ движок Nashorn был удален. Однако если в проекте используется JDK 15+ и установлены зависимости от другого скриптового движка, например JEXL, то вектор изменится на: ${script:JEXL:''.getClass().forName('java.lang.Runtime').getRuntime().exec('calc')}.

Таким образом, можно выделить три обязательных условия для эксплуатации уязвимости:

  1.  Должна быть определена связь (в stringLookupMap) между prefix и соответствующим классом. С использованием функции StringSubstitutor.createInterpolator будут по умолчанию установлены небезопасные префиксы: script, dns, url, file, env и соответствующие им классы.

  2. Должна быть вызвана функция replace (replaceIn) класса StringSubstitutor, в которой произойдет вызов функции lookup.

  3. При вызове функции replace (replaceIn) пользовательские данные должны записываться в ее аргумент source.

Как разработчик исправил уязвимость

В патче к уязвимости было сделано следующее:

1.Изменен алгоритм заполнения связей prefix и классов (stringLookupMap) в функции addDefaultStringLookups класса StringLookupFactory. Теперь HashMap stringLookupMap заполняется коллекцией defaultStringLookups из экземпляра класса DefaultStringLookupsHolder.

2.Добавлен класс DefaultStringLookupsHolder,и в его конструкторе выполняется контроль ключа системного свойства org.apache.commons.text.lookup.StringLookupFactory.defaultStringLookups:

a. если ключ отсутствует, формируются связи по умолчанию с помощью вызова функции createDefaultStringLookups;

b. если ключ присутствует, формируются связи на основе значений ключа с помощью вызова функции parserStringLoookups.

3.Для случая формирования связей по умолчанию (функция createDefaultStringLookups) не создаются связи для prefix: script, dns, url.

4.Для случая формирования связи на основе значений ключа org.apache.commons.text.lookup.StringLookupFactory.defaultStringLookups в функции parserStringLoookups по значению ключа сформируется список связей между prefix и классом. Значения должны соответствовать перечню, заданному в enum DefaultStringLookup (например, BASE64_DECODER, SCRIPT).

Таким образом, внесены следующие изменения:

  • из списка связей между prefix и классом, который определяется по умолчанию, исключены: script, dns, url;

  • появился механизм определения списка связей между prefix и классом через указание соответствующего перечня prefix в ключе org.apache.commons.text.lookup.StringLookupFactory.defaultStringLookups (например, SCRIPT, URL, DNS).

Следовательно, можно утверждать, что патч для версии 1.10.0 библиотеки Apache Commons Text оставляет по умолчанию небезопасные prefix: file, env. Для включения уязвимых режимов обработки интерполяции (script, dns, url) достаточно записать соответствующие значения в поле org.apache.commons.text.lookup.StringLookupFactory.defaultStringLookups.

Это можно сделать несколькими способами:

  • в параметрах запуска приложения

java -Dorg.apache.commons.text.lookup.StringLookupFactory.defaultStringLookups=SCRIPT,DNS,URL text4j.jar
  • установкой свойства внутри кода с помощью функции System.setProperty:

Что делать дальше

Из результатов рассмотрения уязвимости и варианта исправления от разработчиков можно сделать следующий вывод:

  • библиотека Apache Commons Text  остается уязвимой и после обновления до версии 1.10.0. Все зависит от системного свойства, указанного в окружении приложения;

  • эксплуатация уязвимости этой библиотеки главным образом зависит от попадания пользовательских данных на вход уязвимых функций replace (replaceIn) класса StringSubstitutor. На текущий момент для пакетов, в которые включена библиотека Apache Commons Text, отсутствует публичная информация о том, что существует прямой канал передачи пользовательских данных в уязвимые функции, но расслабляться рано.

Следовательно, библиотека Apache Commons Text остается уязвимой. Эксплуатация уязвимости зависит от способов применения библиотеки и отсутствуют гарантии безопасного использования этой библиотеки внутри собственного продукта или внутри заимствованного пакета.

💡Поэтому предлагаем подготовиться к реагированию на такую уязвимость:

  • настроить правила фильтрации межсетевых экранов на наличие шаблона ${prefix:[options]:data} ;

  • выполнить контроль на наличие в составе проекта уязвимой библиотеки Apache Commons Text и проводить такой контроль динамически (например, с использованием утилиты).

Если библиотека присутствует в составе проекта:

  • обновиться до версии 1.10.0, в которой были изменены настройки по запуску небезопасного функционала по умолчанию;

  • проконтролировать в настройках окружения наличие включенных уязвимых режимов (script, dns, url, file, env);

  • провести статический анализ на возможность передачи входных пользовательских данных в аргумент source функций replace(replaceIn) класса StringSubstitutor. Это можно выполнить автоматически с помощью обновленного PT Application Inspector (версия 4.1.1) или же с использованием правил SemGrep;

  • выполнить санитизацию/экранирование входных пользовательских данных перед попаданием их в аргумент source функции replace (replaceIn) класса StringSubstitutor.

Правило SemGrep — создание StringSubstitutor через createInterpolator:

rules:
  - id: text4shell_via_createInterpolator
	patterns:
  	- pattern-either:
      	- pattern: $INTERPOLATOR.replace(...);
      	- pattern: $INTERPOLATOR.replaceIn(...);
  	- pattern-inside: |
      	import org.apache.commons.text.$PKG;
      	...
      	$INTERPOLATOR = $PKG.createInterpolator(...);
      	...
	message: text4shell $INTERPOLATOR.replace call found
	languages:
  	- java
	severity: WARNING

Правило SemGrep — созданиеStringSubstitutor через конструктор:

rules:
  - id: text4shell_via_createInterpolator
	patterns:
  	- pattern-either:
      	- pattern: $INTERPOLATOR.replace(...);
      	- pattern: $INTERPOLATOR.replaceIn(...);
  	- pattern-inside: |
      	import org.apache.commons.text.$PKG;
      	...
      	$INTERPOLATOR = $PKG.createInterpolator(...);
      	...
	message: text4shell $INTERPOLATOR.replace call found
	languages:
  	- java
	severity: WARNING


Команда PT Application Inspector

Александр Болдырев, Алексей Новгородов, Максим Суслов

Теги:
Хабы:
Всего голосов 5: ↑4 и ↓1+3
Комментарии4

Публикации

Информация

Сайт
www.ptsecurity.com
Дата регистрации
Дата основания
2002
Численность
1 001–5 000 человек
Местоположение
Россия