«Новое – это хорошо забытое старое» мигом вспомнилась мне поговорка, когда при оценке защищенности очередного веб-приложения успешно прошла атака XXE. Немедленно захотелось написать об этом пару слов, ибо:
— XML-транспорт занял одно из доминирующих мест по распространенности в распределенных приложениях (в т.ч. клиент-серверных веб-приложениях, кстати);
— ни один из 5 запущенных сканеров (специально не поленился и проверил) эту, в принципе достаточно простую с технической точки зрения, уязвимость не нашел;
— ни разу не слышал от коллег упоминаний о нахождении и/или эксплуатации этой уязвимости.
Итак, к делу.
Уверен, что возможность внедрения выражений XPath, равно как и внедрения XML-тэгов ни для кого не является новостью. Это первое, с чего хочется начать проверять целевое приложение, использующее xml. Однако есть еще один вектор атаки, о нем подробнее.
Начнем издалека. Стандартом определены два уровня правильности документа XML:
1) Well-formed документ. Well-formed документ соответствует всем общим правилам синтаксиса XML, применимым к любому XML-документу. И если, например, начальный тег не имеет соответствующего ему конечного тега, то это неправильно построенный документ XML.
2) Valid документ. Valid документ дополнительно соответствует некоторым семантическим правилам. Это более строгая дополнительная проверка корректности документа на соответствие заранее определённым, но уже внешним правилам. Обычно такие правила хранятся в специальных файлах — схемах, где самым подробным образом описана структура документа, все допустимые названия элементов, атрибутов и многое другое.
Основные форматы определения правил валидности xml-документов – это DTD и XML Schema. Остановимся на DTD. Есть два варианта связать документ с его схемой: либо указать ссылку на схему, либо поместить схему прямо в сам xml-документ, в его начало. Вот два примера:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE order SYSTEM "order.dtd"> <order> <product>1234</product> <count>1</count> </order> <?xml version="1.0"?> <!DOCTYPE order [ <!ELEMENT count (#PCDATA)> <!ELEMENT product (#PCDATA)> <!ELEMENT order (product, count)> ]> <order> <product>1234</product> <count>1</count> </order>
Кроме того, в xml есть такое понятие как внешние сущности (external entities). Их можно использовать, например, для добавления в xml-документ содержимого внешних файлов. Например, можно определить внешнюю сущность current-date:
<!ENTITY current-date SYSTEM "http://www.getcurrenttime.com/timestamp.xml">
и перенести логику по вычислению текущей даты и времени на внешний сервис. Соответственно, использовать можно так:
<?xml version="1.0"?> <!DOCTYPE order [ <!ELEMENT count (#PCDATA)> <!ELEMENT product (#PCDATA)> <!ELEMENT date (timestamp)> <!ELEMENT timestamp (#PCDATA)> <!ELEMENT order (product, count)> <!ENTITY current-date SYSTEM "http://www.getcurrenttime.com/timestamp.xml"> ]> <order> <product>1234</product> <count>1</count> <date>¤t-date;</date> </order>
Теперь вернемся к парсерам. Парсеры бывают валидирующими и невалидирующими. Вот что сказано в стандарте [http://www.w3.org/TR/REC-xml#include-if-valid] по поводу поведения парсера при наличии в документе ссылок на внешние сущности:
«When an XML processor recognizes a reference to a parsed entity, in order to validate the document, the processor must include its replacement text. If the entity is external, and the processor is not attempting to validate the XML document, the processor may, but need not, include the entity’s replacement text…»
То есть валидирующий парсер обязан включить содержимое внешнего файла. Действительно, для того, чтобы проверить структуру документа, парсер должен разрешить все внешние связи. А вот невалидирующий может включить содержимое внешнего файла по желанию.
На этом месте все уже должны догадаться об атаке.
Если в POST-запросе передается xml, можно делать такое:
<!ENTITY xxe SYSTEM «file:///dev/random» >]><inpu>&xxe;</input>
Делаем DoS.
<!ENTITY xxe SYSTEM «file:///path-to-tomcat-application/WEB-INF/web.xml» >]><input>xxe;</input>
Включаем содержимое xml-файла. Валидирующий парсер, очевидно, по итогам разбора выдаст ошибку. Таким образом, этот трюк пройдет только с невалидирующим парсером. Плюс к этому, значение параметра, в который вы внедряете ссылку на внешнюю сущность, должно возвращаться обратно (иначе вы никак не получете содержимое вставленного файла).
<!ENTITY xxe SYSTEM «file:///etc/passwd» >]><input>&xxe;</input>
Включаем содержимое обычного файла. Тут все очень зависит от парсера и содержимого файла:
— есть парсеры, которые требуют, чтобы внешние файлы были well-formed xml-документами (самый неудобный случай);
— остальные парсеры требуют, чтобы итоговый документ был well-formed (очевидно). В этом случае в подключаемом файле не должны встречаться специсимволы xml, которые нарушат синтаксис документа. Соответственно, этот трюк может сработать даже с валидирующим парсером (структура документа же нарушится).
[ad name=»Responbl»]
Кроме того, необходимыми условиями срабатывания атаки являются:
— парсер должен уметь разрешать ссылки на внешние сущности; для многих простых парсеров это условие не выполняется;
— парсер должен иметь настройки, позволяющие ему разрешать ссылки на внешние сущности; например, известный парсер xerces изначально имел «удобные» настройки по умолчанию, но с некоторой версии (вот бы узнать, с какой) они эти настройки изменили, так что теперь по умолчанию xerces не будет разрешать такие ссылки; зато libxml имеет «удобные» настройки по умолчанию до сих пор.
Ссылки по теме:
1. Первое упоминание об атаке: http://www.securityfocus.com/archive/1/297714 .
2. Отличная презентация с конференции OWASP AppSec Germany 2010: XML External Entity Attacks.
3. Еще одна прекрасная презентация по теме с конференции OWASP Poland 2010: XML DTD Attacks.
1 comments On XXE атаки на простых примерах.
Pingback: Как внедрить инъекцию через JSON/XML-заглушки для API - Cryptoworld ()