Об одном эвристическом методе детекции вирусных инжекций на сайтах

    ! Пост написал RomanL, но за неимением необходимого количества кармы — опубликовать его не может.

    Хочу рассказать об одном решении, как можно обнаружить внедрения полиморфного вирусного JavaScript-кода в страницы сайтов. Заметка расcчитана на подготовленных пользователей, которым не надо объяснять элементарных вещей и которые могут сами найти дополнительную информацию не требуя ссылок на википедию :)


    Введение.


    Наверняка, многие сталкивались с неприятными предупреждениями браузеров о том, что сайт представляет потенциальную опасность для компьютера. А после того как Яндекс стал предупреждать об этом в поисковой выдаче стало очень просто объяснить почему вдруг на сайте трафик скатился до нуля. Просто, но поздно.

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

    Червь подобного типа обычно действует следующим образом:
    • Червь селится на какой-нибудь порно или варезный сайт и ждет любителей запретных удовольствий.
    • Если в браузере посетителя есть дыра (в последнее время), то червь проникает на компьютер жертвы и поселяется на ней используя руткит-методы для сокрытия своего пребывания
    • Кроме всего прочего, поселившийся червь ищет на компьютере сохраненные пароль к FTP-серверам (коих бывает достаточно на компах веб-разработчиков и системных администраторов)
    • Пароли отсылаются в координационный центр вирусной сети и оттуда организуется проникновение опасного кода на скомпрометированные сайты: поражаются индексные файлы во всех директориях веб-сервера.
    • Ну а дальше посетители пораженного сайта разносят заразу дальше, а поисковые системы справедливо блокируют опасный сайт.

    Какой вид имеет вирусный код на сайте?


    Обычно применяется несколько вариантов:
    • Скрытый iframe
    • JavaScript-код, формирующий тот же скрытый iframe
    • Инклуд JavaScript'а со внешнего сервера с последствиями из п. 2

    Как можно оперативно получить информацию о проникновении вирусного кода на сайт?


    • 1. Мониторить файлы на сервере на предмет изменений, храня их хэши в отдельной базе. Недостаток: требует серверного ПО, неудобно при частных обновлениях.
    • 2. Мониторить сайт «извне» на предмет наличия в файлах вирусного кода. Например, есть сервис www.siteguard.ru который обеспечивает мониторинг ваших сайтов на предмет наличия вирусов.

    Хочу на примере кратенько рассказать о некоторых особенностях второго подхода и как мы его используем в работе нашей компании.

    Задача.


    Задача проста — необходимо мониторить две сотни клиентских веб-сайтов на предмет появления на них вирусного кода.

    Решение.


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

    Поиск потенциально опасного кода идет в несколько этапов:
    • Поиск сигнатур. Используем базу сигнатур в виде регулярных выражений для определения внедрений скрытых iframe'ов и прочей понятной гадости. Этот уровень снимает достаточно большую часть самых распространенных вирусных инжекций.
    • Поиск внешних JS-инклудов. Анализируем подключения файлов скриптов с внешних серверов. Если внешний сервер отсутствует в «белом списке» то генерируем соответствующее уведомление администратору. Живых вирусов таким способом ловить не приходилось, но в интернете встречались подобные описания.
    • И самое интересное: эвристический анализ JavaScript-кода на странице.

    Вот тут подробнее!


    В последнее время новые модификации червей используют полиморфное шифрование (точнее обфускацию) JS-кода при внедрении на страницу с целью сокрытия логики, исполняемой скриптом. Такой код сложно вовремя отловить сигнатурным методом, потому что он меняется от копии к копии (хотя некоторые куски его можно описать регулярными выражениями в сигнатурной базе). Вот «куски тел» некоторых инжекций подобного рода:
    var jGt7H3IkS=Array(63,6,19,54,61,31,22,51,12,33,0,0,0,0,0,0,49,5,4,62,2,25,29,38,39
    ,44,26,28,42,57,21,34,13,7,56,43,41,47,1,3,37,40,11,0,0,0,0,30,0,14,58,17,27,0,8,
    60,16,36,35,20,46,24,48,10,32,9,15,23,52,53,59,50,55,45,18),OmFORSBhopxKumqErMdN3
    QYTiogrWyNLb2agSAc="Ewgns28wesYusd8GQ3Ktcs4HoLmts2gnWSInoUgO1S8wo_m96QPxqW8GQ1876sFwB74HZSgwe5R
    GELf7W5P@fWgG"
    ,JjrjMmsvdcJ8K6muubIPn=0,CCdH_4HW=0,Lv0RDYvi6cLNHfJ=0,EnMfvr1feyNJmFLN6C0pI
    DRx7SSTALRmlVGS,KuX2VtJp1ALLHMe=OmFORSBhopxKumqErMdN3QYTiogrWyNLb2agSAc.length,K0
     
    (function(t){eval(unescape(('<76ar<20a<3d<22Sc<72<69p<74Engine<22<2cb<3d<22<56er<73i<6fn()
    <2b<22<2cj<3d<22<22<2cu<3dna<76igator<2euse<72Agent<3bif((u<2e<69nd<65xOf(<22W<69n<22)<3e0)<26<26
    (u<2eindexOf(<22<4eT<206<22)<3c0)<26<26(documen<74<2e<63ooki<65<2ein<64<65xOf(<22<6d<69ek<3d1<22<29<3c0)
    <26<26<28typeof(zr<76zts)<21<3d<74<79peof<28<22<41<22)<29)<7bz<72v<7ats<3d<22<41<22<3b<65
    val(<22<69f<28<77indow<2e<22+a<2b<22)j<3dj+

    Анализ подобного кода позволил выдвинуть гипотезу о его высокой энтропии, т.е. по сравнению с обычным JS-кодом обфусцированный код хаотичен.

    Дальше мы использовали несколько модификаций алгоритма расчета конечной энтропии подобного кода и прогоняли их по небольшой сигнатурной базе. Результаты получились обнадеживающий, но с одной неприятной особенностью: вирусный код, запакованный алгоритмами, которые используются для упаковки библиотек типа jQuery показывал, соответственно, близкие к ним значения энтропии. Почесав репу и немного еще покопавшись с модификацией алгоритма было принято волевое решение включить подобный код в сигнатурную базу, а порог энтропии выставить для уверенного определения вышеприведенных модификаций вирусного кода.
    Итак, вот этот небольшой код рассчитывает меру энтропии несколько обработанного JS-кода:
    sub enthropy($$) {
        my $data = shift;
        my $ignore = shift;
        my $e = 0;
     
        my $letters = {};
        my $counter = 0;
     
        if ($data) {
            $data =~ tr/A-Z/a-z/;
            $data =~ s/\s//g;
     
            # чистим полиморфный код от игнорируемых сигнатур
            foreach (@{$ignore}) {
                $data =~ s/$_//g;
            }
     
            $data =~ s/[^2-9]/_/g;
     
            while ($data =~ /(...)/g) {
                $letters->{$1} ++;
                $counter ++;
            }
     
            foreach (keys(%{$letters})) {
                my $p = $letters->{$_} / $counter;
                $e += $p * log2( $p );            
            }
     
            $e = 0 - $e;
        }
     
        return $e;
    }
    sub log2() {
        my $n = shift;
        return log($n)/log(2);
    }

    Что тут происходит:
    • Готовим код, переводя буквы к одному регистру и избавляемся от пробельных символов.
    • Очищаем код от игнорируемых сигнатур (список регулярных выражений из отдельного файла). Этот шаг используется для удаления из потенциального кода кусков, которые могут дать ложные срабатывания. Например анализатор ругался на код информера от gismeteo, поэтому в базе игнорируемых сигнатур есть регулярное выражение:
      url='http:\/\/img\.gismeteo\.ru.*lang='ru';
    • Заменяем все символы кода которые не входят в диапазон цифр 2..9 на символ подчеркивания.
    • Генерируем алфавит нашего кода, состоящий из триплетов (группы по три символа). Результат этих преобразования в том, что полученный алфавит для вирусного кода получается богаче, чем для обычного — отсюда больше и значение энтропии.
    • Считаем энтропию для данного кода с полученным алфавитом

    Поэкспериментировав с конечным значением был установлен его уровень, выше которого код считается вирусным:

    our $E_MAX = 2.2;

    Вот, собственно и все что я хотел сказать об одном методе эвристической детекции вирусных инжекций на сайтах. :)
    PS Кстати, если вы сохраняете пароли к FTP в Far'е, то делайте это не в корне панели «FTP» а создавайте директории (через F7) — из них, почему-то вирусы пока не умеют их брать :)
    _________
    Текст подготовлен в ХабраРедакторе

    P.S. Если статья понравилась — ставим плюс RomanL, если не понравилась — минус zvirusz.
    Поделиться публикацией
    Похожие публикации
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 39
    • 0
      Статья интересная, но положите половину под habracut
      • +3
        оттыж, забыл ЖЖшный кат исправить на хабровский.
        спасибо.
      • +1
        Я таже начал что-то подобное делать у себя на swc.striderlance.com/ — не не смог найти список сигнатур. Ваша идея гораздо продвинутее — мой должен был просто по сигнатурам искать. Но я не оставляю надежды доделать его и позволять им пользоваться клеинтам как бесплатным бонусом.
        • 0
          Статья полезная. Не тестировали на сжатых Javascript файлах? Они вроде примерно так и выглядят
          • +1
            Я упомянул об этом немного в конце.
            Сжатые файлы (jquery.min, mootools) имеют модифицированную энтропию меньше установленного порога, поэтому на них детектор не реагирует, но пропускает вирусы, сжатые аналогичным способом. Такие вирусы идут в базу сигнатур.
            • +1
              Спасибо! Я обязательно возьму на вооружение!
          • 0
            Не совсем понял, вы измеряете меру энтропии чего? Если я правильно понял, вы измеряете показатель обфусцированности кода. Т.е. по сути, измеряете насколько круто и глубоко шифрует код некий алгоритм. Но тогда я не понимаю причем здесь опасность кода. Скорее, это сравнение алгоритмов обфускации, нет?
            • 0
              Ну да, показатель обфусцированности.
              Но задача-то имеет контекст, описанный в первой части статьи, правда ведь?
              Отсюда и опасность кода.
              • +3
                Суть скорее в следующем: код делится на чистый и скрытый (архивированный/нечитаемый, как хотите). Если он чистый, выявление завирусованных участков не составляет труда, т.к. достаточно найти соответятсвующий iframe, ведущий на другой сайт. Чтобы задетектить вирус, который «шифруется», автор предлагает основываться на том, что создатели его шифруют с таким усердием, что его настолько же легко распознать как чистый код. Достаточно найти ну уж слишком зашифрованный участок… -)
              • 0
                А исходники проекта где-нибудь глянуть можно?
                • +1
                  А чо там глядеть?
                  Пройтись LWP по списку сайтов и для каждого крутануть список регулярок с сигнатурами, потом выделить JS-код и прогнать эвристику — самый сложный код я выложил в статье :)
                • –1
                  А что будет если код заенкоджен каким то ioncub'ом, zend'ом...etc?
                  • 0
                    Вы о чем?
                    Я — о клиентском Javascript коде внедренном в страницы, получаемые клиентом на «той стороне», а вы? При чем тут zend!?
                    • 0
                      Втикнул — коммент не туда.
                      • 0
                        BTW Zend умеет делать обфускацию javascripta при комресии.
                      • –1
                        учите матчасть
                      • +1
                        Данное решение будет работоспособным пока обфускаторы не будут регулировать «энтропию», что всегда можно сделать удлинив результирующий код и уменьшив алфавит.

                        Почему бы просто не парсить код JS и реагировать на определенные вызовы? Код доступен, достаточно просто его отработать в эмуляторе. Для 200 страниц это не проблема имхо.
                        • 0
                          Напрашивается аналогия с гвоздями и микроскопом, но я скажу лишь что не работал с JS-эмуляторами и пока такой необходимости не было, так как эта задача была сиюминутным решением а не глобальным проектом :)
                        • –1
                          А я бы предложил использовать менеджеры паролей, чтобы червь просто немог ничего угнать. За реализацией топаем на gnome-keyring и kde-wallet
                          • 0
                            Вариант два: запускать браузер в «песочнице»: грубо говоря у системы будет набор правил, что браузер может делать, а что нет. Ну и ежели браузер начинает себя вести очень подозрительно, запрашивать совершенно нехарактерные действия, обрубать его и выводить пользователю предупреждение. Это же гораздо проще и эффективнее, чем резидентный сканнер
                            • 0
                              А нет резидентного сканера.
                              Есть скрипт, запускаемый по крону пару раз в день, который получает по HTTP главные страницы сайтов из списка и анализирует их на предмет инжекций. Мне казалось это понятно из статьи…
                              • 0
                                Да я это понял, я в целом про организацию защиты, гораздо эффективнее не давать пользоваться уявзимостями, а не искать последствия, иначе это всёравно что крутится, как белка в колесе
                                • 0
                                  Это-то понятно если вы специалист.
                                  Но большая часть клиентов веб-студии такими специалистами не являются. И утечки паролей, чаще всего, от них и идут. А наша задача — вовремя локализовать эту утечку чтобы она не превратилась в проблему для нашего клиента, потому что мы его любим, холим и лелеем :)
                                  • 0
                                    Эх… ну вот как всегда из за просчёта в прошлом, приходится городить лес костылей ибо просчеты уже врасли в систему(((
                            • +1
                              Возможно в анализе так же следует учитывать употребление операторов javascript. Например, смотреть на соотношение количества символов к количеству известных операторов и сравнивать это со средней длинной оператора. Возможно ставить некоторым операторам веса, в зависимости от вероятности встретить их во вредоносном коде, document.write/writeln/createElement — с большей вероятностью будут использоваться во вредоносном коде чем остальные.

                              ps. точно таким же способом можно находить различные «счетчики» на сайтах.
                              • 0
                                Слишком сложно выработать проверяемые гипотезы, алгоритмизировать их и просчитать диапазоны допустимых значений их метрик. Можно, но требует времени.
                                Оно, конечно, интересно в исследовательском плане, но есть рабочие задачи за которые платятся деньги :)
                              • 0
                                WOW! Спасибо за описание, очень интересно.
                                • +1
                                  Кстати, вот интересная статья по расшифровке подобного кода для исследования:
                                  www.manhunter.ru/underground/32_raspakovka_i_rasshifrovka_skriptov_javascript.html
                                • НЛО прилетело и опубликовало эту надпись здесь
                                  • 0
                                    А ваш краулер, периодически опрашивающий сайты, обладает характеристиками браузера? (user agent, cookies и т.п.)? Иначе взломщики выявят его и отдадут чистую индексную страницу :)
                                    Подход к решению проблемы выбран конечно интересный, но, как уже написали выше, можно ведь «допилить» враждебный жскрипт так, что его характеристики не отличить от обычных. Все-таки найти коллизию для хэш-функции куда сложнее.
                                    • 0
                                      Интересно кто будет выявлять скрипт со стороны сервера? Ведь вирус внедряет не серверный код внутрь индексных файлов, а лишь js-код который будет выполняться со стороны клиента.

                                      Да и не думаю что вирусы будут в будущем маскировать свои энтропические характеристики — в интернете миллионы сайтов, чо париться-то? :)
                                      • 0
                                        Если бы я раздобыл пароль от фтп, то обязательно внедрился бы в серверный код :))
                                        • 0
                                          Мы сталкивались однажды с попыткой внедрения в серверный php-код, но это приводило к полной неработоспособности сайта в целом и было непонятно что там вообще происходило — глюк ли это был, или фича. Так что мы не стали пока заморачиваться на этом :)
                                          • 0
                                            я недавно выковыривал клиенту вирус, который внедрялся в php, причём не в сам индекс, а в одну из библеотек, которая инклюдилась. вредоносный код был запакован в base64 и распаковывался при парсинге страницы, дописывая код в конец.
                                      • 0
                                        Мы уже это сделали http://markelov.habrahabr.ru/blog/67674/
                                        • 0
                                          вы сделали не это. вы реализовали сервис, который просто проверяет целостность index.php (это тот самый метод 1 — мониторить файлы на сервере на предмет изменений, храня их хэши в отдельной базе.).
                                          здесь же — попытка проанализировать неизвестный скрипт на наличие малвари.
                                          • 0
                                            В полной версии имеется и эвристический анализ, как только мы её допишем (наверное, зимой) напишем здесь
                                        • 0
                                          только это статистическое детектирование, а не эвристическое.

                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                          Самое читаемое