Репост из блога Esage Lab
Про руткит TDSS - а именно, про ту его версию, которая также известна как TDL3 - написано достаточно много: с момента появления первых прецедентов прошло уже почти полгода. Однако, TDL3 до сих пор является актуальной угрозой, так как на каждое обновление процедур его обнаружения и лечения антивирусами авторы руткита отвечают всё новыми и новыми обновлениями вредоносного кода. Подробно ознакомиться с техническим описанием руткита TDL3 можно по следующим ссылкам (на английском языке):
TDL3 - Why so serious? Let's put a smile on that face...
От BackDoor.Tdss.565 и выше (aka TDL3)
В данной заметке я хотел бы остановиться на нововведениях, которые появились в TDL3 после публикации его первых обзоров. Кроме того, в конце заметки будут приведены результаты тестирования существующих утилит для лечения TDSS.
Инсталлятор
Инсталлятор руткита представляет собой исполняемый файл размером ~87Кб, обработанный несложной утилитой для запутывания кода (VirusTotal Report).
Ниже приведён конфигурационный файл, используемый руткитом после установки в систему:
[main] quote=You people voted for Hubert Humphrey, and you killed Jesus version=3.273 botid=7a91eb86-a6be-4db5-8694-0337dad2c75d affid=20592 subid=0 installdate=22.4.2010 23:42:43 builddate=20.4.2010 16:17:53 [injector] *=tdlcmd.dll [tdlcmd] servers=https://li1i16b0.com/;https://19js810300z.com/;https://lj1i16b0.com/ wspservers=http://7gafd33ja90a.com/;http://n1mo661s6cx0.com/ popupservers=http version=3.741
Как видно, версия руткита - 3.273, последняя на данный момент.
С самых первых версий TDSS радовал нас оригинальной, и в то же время простой, техникой обхода поведенческой защиты, принцип работы которой основан на использовании механизмов службы диспетчера печати Windows. А именно, осуществлялась регистрация вспомогательной динамической библиотеки диспетчера печати (Print Processor) с помощью функции API функции AddPrintProcessor, что, в свою очередь, приводило к выполнению кода, содержащегося в этой библиотеке, в контексте доверенного процесса (а именно, spoolsv.exe). Сложность детектирования подобного поведения для систем типа HIPS заключается в том, что вызов функции AddPrintProcessor в итоге приводит к вызову RPC-функции диспетчера печати, единственный надёжный способ мониторинга которой заключается в контроле недокументированных LPC сообщений. Однако, со временем производители антивирусных защит всё-таки научились корректно препятствовать описанной технике. После этого разработчики руткита начали использовать с той же целью вызов похожей функции, а именно - AddPrintProvidor, вызов которой разработчики проактивных защит контролировать не догадались (sic!):
BOOL AddPrintProvidor( LPTSTR pName, // reserved, must be NULL DWORD Level, // provider information level // (if 1 - function uses a PROVIDOR_INFO_1 structure) LPBYTE pProviderInfo // provider information buffer );
typedef struct _PROVIDOR_INFO_1 { LPTSTR pName; LPTSTR pEnvironment; LPTSTR pDLLName; } PROVIDOR_INFO_1, *PPROVIDOR_INFO_1;
Взглянем на псевдокод той части инсталлятора, которая выполняет обход проактивной защиты:
1 // генерация временного имени, под которым будет сохранена dll библиотека, 2 // загружаемая в контекст доверенного процесса функцией AddPrintProvidor() 3 if (GetTempFileNameW(&PathName, 0, 0, &ExistingFileName)) 4 { 5 // сама dll библиотека представляет собой тот же самый исполняемый файл дроппера, 6 // в котором выставленны соотвествующие значения флагов PE заголовка. 7 if (MoveFileExW(&NewFileName, &ExistingFileName, 9u)) 8 { 9 pDLLName = &ExistingFileName; 10 *(_DWORD *)pProvidorInfo = L"tdl"; 11 pEnvironment = 0; 12 AddPrintProvidorW(L"tdl", 1u, pProvidorInfo); 13 if (GetLastError() == 1722 /* The RPC server is unavailable */) 14 { 15 // Служба диспетчера печати не запущена 16 hScm = OpenSCManagerA(0, 0, 1u); 17 hService = OpenServiceA(hScm, "spooler", 0x14u); 18 if (hService) 19 { 20 // запускаем службу 21 if (StartServiceA(hService, 0, 0)) 22 { 23 SERVICE_STATUS_PROCESS Buffer; 24 memset(&Buffer, 0, 0x1Cu); 25 do 26 { 27 // ... и дожидаемся окончания её запуска 28 if (!QueryServiceStatusEx(hService, 0, &Buffer, 0x24u, &pcbBytesNeeded)) 29 break; 30 Sleep(dwMilliseconds); 31 } 32 while (Buffer.dwCurrentState != 4 /* SERVICE_RUNNING */); 33 pDLLName = &ExistingFileName; 34 *(_DWORD *)pProvidorInfo = L"tdl"; 35 pEnvironment = 0; 36 AddPrintProvidorW(L"tdl", 1u, pProvidorInfo); 37 } 38 CloseServiceHandle(hService); 39 } 40 } 41 42 // ... 43 // print providor и dll библиотека больше не нужны, удаляем их 44 DeletePrintProvidorW(0, 0, L"tdl"); 45 DeleteFileW(&ExistingFileName); 46 47 // ... 48 } 49 }
В дальнейшем, работая уже в контексте доверенного процесса, инсталлятор руткита загружает драйвер режима ядра, имена файла и системного сервиса для которого генерируются случайным образом. После загрузки в память драйвер руткита выполняет заражение уже установленного в системе легитимного драйвера и инициализирует устройство, обслуживающее собственную зашифрованную файловую систему руткита. На эту файловую систему инсталлятор записывает файлы tdlcmd.dll (код трояна, работающий в User Mode), config.ini (конфигурационный файл, расшифрованный текст которого был приведён выше – этот файл формируется инсталлятором «на лету» их тех данных, которые были «зашиты» в инсталлятор на этапе его сборки и конфигурирования) и bcfg.tmp (исходные конфигурационные данные из инсталлятора). После того, как заражение выполнено, драйвер и инсталлятор самоудаляются, а руткит остаётся жить в системе исключительно как «потерянный» фрагмент исполняемого кода: он записан в последние секторы физического диска и не существующий в виде файла.
Основное тело руткита не претерпело существенных изменений за последнее время за тем исключением, что обработчики дисковых перехватов руткита теперь дополнительно фильтруют такие запросы IRP, как IOCTL_SCSI_PASS_THROUGH. Подобные запросы, адресованые драйверу минипорта, некоторое время использовались утилитами для удаления TDSS.
Заражение драйвера
Одним из главных нововведений в версии 3.273 является то, что вместо заражения заранее известного драйвера минипорта дискового контроллера руткит теперь заражает случайный драйвер, выбирая его из числа загруженных в память на момент заражения. Претерпел существенные изменения и код, которым осуществляется заражение драйвера (он по-прежнему записывается поверх секции, в которой хранятся ресурсы).
Во-первых, поиск функций ядра, используемых в этом коде, теперь осуществляется по контрольным суммам, подсчитанным от их имён. В предыдущих версиях руткита адреса нужных функций хранились непосредственно в коде. Это изменение связано с инцидентом, произошедшим после выхода обновления Miscrosoft для уязвимости MS10-015: в результате использования в коде руткита фиксированных адресов функций ядра огромное количество зараженных пользователей после обновления получили «синий экран смерти» на этапе загрузки и, как следствие, неработоспособную систему (Windows blue screen may be result of rootkit infection).
Во-вторых, та часть кода заражения, которая осуществляет загрузку тела руткита из последних секторов физического диска, теперь шифруется методом XOR. Однобайтный ключ шифрования хранится в начале зараженной секции наряду с адресом оригинальной точки входа драйвера. Возможно, разработчики руткита предприняли этот шаг для обхода тех антивирусных утилит, которые использовали модификацию загружаемого драйвера в ходе лечения.
В третьих, для установки нотификатора на создание объектов типа "Control Device Object" в новой версии используется функция IoRegisterPlugPlayNotification, вместо функции IoRegisterFsRegistrationChange, используемой в более ранних версиях:
NTSTATUS IoRegisterPlugPlayNotification( IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory, IN ULONG EventCategoryFlags, IN PVOID EventCategoryData OPTIONAL, IN PDRIVER_OBJECT DriverObject, IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine, IN PVOID Context, OUT PVOID *NotificationEntry ); typedef NTSTATUS (* PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) ( IN PVOID NotificationStructure, IN PVOID Context );
Рассмотрим псевдокод первой части кода заражения системного драйвера:
1 NTSTATUS InfectedDriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) 2 { 3 // получаем указатель на секцию ресурсов зараженного драйвера 4 rsrc = (int)((char *)DriverObject->DriverStart + 5 *(_DWORD *)(DriverObject->DriverStart + 6 *((_DWORD *)DriverObject->DriverStart + 15) + 136)); 7 8 // ищем адрес загрузки ядра 9 __asm { sidt fword ptr [ebp+var_10] } 10 kernel_base = *(_DWORD *)(*(_DWORD *)&v28[2] + 512) & 0xF000 | 11 (*(_WORD *)(*(_DWORD *)&v28[2] + 518) << 16); 12 for (i = kernel_base; *(_WORD *)kernel_base != 'MZ'; i = kernel_base) 13 kernel_base = (kernel_base - 2) & 0xFFFFF000; 14 15 // получаем адрес функции nt!ExAllocatePool() 16 __ExAllocatePool = _getapi(kernel_base, 0xDE45E96Cu); 17 // выделяем память под структуру, в которой будут хранится 18 // значения различных ключевых переменных, а так же 19 // прочитанное с диска тело руткита 20 context = __ExAllocatePool(0, 0x69A60u); 21 memset(context, 0, 0x69A60u); 22 *(_DWORD *)(context + 1968) = kernel_base; 23 *(_DWORD *)(context + 352) = DriverObject; 24 memcpy((void *)(context + 1972), rsrc, 0x395u); 25 26 // вызов оригинальной точки входа зараженного драйвера 27 status = ((char *)DriverObject->DriverStart + *(_DWORD *)(rsrc + 8)))( 28 DriverObject, RegistryPath); 29 30 // расшифровка второй части шеллкода, которая выполняет роль 31 // обработчика PnP событий, и читает с диска тело руткита 32 k = *(_BYTE *)(rsrc + 16); 33 code_len = 433; 34 code_ptr = context + 2456; 35 do 36 { 37 *(_BYTE *)code_ptr++ ^= k++; 38 --code_len; 39 } 40 while (code_len); 41 42 // установка нотификатора для обработки PnP событий 43 // EventCategory = EventCategoryTargetDeviceChange 44 __IoRegisterPlugPlayNotification = _getapi(kernel_base, 0x48399F96u); 45 __IoRegisterPlugPlayNotification(2, 1, &v17, DriverObject, context + 2456, 46 context, context + 348); 47 48 return status; 49 }
Вторая часть кода заражения системного драйвера, исполняемая при появлении определённых PnP событий, выполняет открытие устройства, имя которого передаётся в обработчик. После этого код заражения читает из последних секторов открытого устройства тело руткита, которое хранится в собственной зашифрованной файловой системе руткита под именем "tdl".
Стоит заметить, что начиная с версии 3.27 способ шифрования этой файловой системы несколько изменился: теперь вместо алгоритма RC4 со строкой "tdl" в качестве ключа используется инкрементальный XOR:
1 // функция расшифровки сектора (его размер равен 1024-м байтам) 2 void xor_encrypt(unsigned char *buf, int buf_len) 3 { 4 unsigned char key = 0x54; 5 for (int i = 0; i < buf_len; i++) 6 { 7 *(buf + i) ^= key++; 8 } 9 }
Формат самой файловой системы и логика её хранения на диске не изменились.
TDSS против антивирусных продуктов
В рамках исследования новых версий руткита мы провели тесты различных антивирусных программ с целью выяснения, насколько эффективно антивирусная индустрия противостоит руткиту TDSS на данный момент.
Тестирование производилось на платформе VMware с установленной гостевой операционной системой Windows XP Professional SP3. Были протестированы последние на момент написания данной заметки версии тех антивирусных продуктов, в которых ранее была заявлена способность к детектированию или лечению TDL3. Кроме того, с целью выяснения эффективности используемой в инсталляторе руткита техники обхода проактивных защит, в тестирование были дополнительно включены продукты, обладающие наиболее продвинутыми функциями поведенческого анализа (Comodo, ZoneAlarm, Outpost и Kaspersky Internet Security).
На виртуальную машину устанавливались последние версии тестируемых приложений, после чего осуществлялось обновление антивирусных баз и компонентов. Далее производился запуск инсталлятора руткита, перезагрузка гостевой ОС и полное сканирование системы тестируемым приложением с активацией всех возможных дополнительных настроек.
Название и версия программы | Обнаружение по поведению | Обнаружение активнго заражения | Лечение активного заражения |
Dr.Web Security Space 6.00.0.04080 | N/A | Yes | Yes |
Kaspersky TDSSKiller 2.2.8.1 | N/A | Обнаружен неправильный файл | No |
Norman TDSS Cleaner 1.9.1.0 | N/A | Yes | Yes |
Vba32 AntiRootkit 3.12.4.0 | N/A | No | No |
HitmanPro 3.5.5.98 | N/A | No | No |
MalwareBytes Anti-Malware 1.45 | N/A | No | No |
RootRepeal 2.0.0 Beta | N/A | Только в памяти (без имени зараженного файла) | No |
Comodo Internet Security 3.14 | No | N/A | N/A |
Kaspersky Internet Security 2010 (9.0.0.736) | No | Только в памяти (без имени зараженного файла) | No |
ZoneAlarm Internet Security Suite 9.1.507.0 | No | N/A | N/A |
Outpost Firewall Pro 2009 (3063.452.726.367) | No | N/A | N/A |
Как видно из таблицы, с лечением активного заражения справилось всего две программы. Утилита TDSSKiller смогла обнаружить факт заражения, однако, в качестве зараженного файла был указан atapi.sys, что не соответствовало действительности:
00:41:06:756 1880 Driver "atapi" infected by TDSS rootkit! 00:41:06:772 1880 C:\WINDOWS\system32\DRIVERS\atapi.sys - Verdict: 1 00:41:06:772 1880 File "C:\WINDOWS\system32\DRIVERS\atapi.sys" infected by TDSS rootkit ... 00:41:06:772 1880 Processing driver file: C:\WINDOWS\system32\DRIVERS\atapi.sys
Утилита RootRepeal смогла обнаружить только модифицированный руткитом Driver Object:
STEALTH CODE ------------------- System 0x816f78b4 - Hidden Code System 0x816f7ac8 - Hidden Code [Driver: , IRP: IRP_MJ_CLEANUP] System 0x816f7ac8 - Hidden Code [Driver: , IRP: IRP_MJ_CLOSE] System 0x816f7ac8 - Hidden Code [Driver: , IRP: IRP_MJ_CREATE] ... System 0x816f7ac8 - Hidden Code [Driver: , IRP: IRP_MJ_WRITE]
С поведенческим обнаружением инсталляции руткита в систему не справился ни один продукт.
С учётом относительно свежести тестируемого экземпляра руткита, рано делать какие-либо выводы из этих результатов. Но очевидно, что если в ближайшем будущем ситуация с детектированием TDL3 антивирусными программами не исправится, то он может стать для антивирусной индустрии самым существенным провалом со времён червя Conficker. По нашим данным, ботом TDSS заражено огромное количество машин – его ботнет на данный момент входит в число 10-ти самых крупных.
TDSS Remover
Вскоре после появления новой версии TDL3 мы выпустили обновление своей утилиты TDSS Remover:
Помимо лечения самой последней версии руткита, в новой версии утилиты была добавлена функция сохранения зашифрованной файловой системы руткита, а также утилита, которая извлекает отдельные файлы руткита из сохраненной файловой системы. Файловая система руткита автоматически сохраняется в файл с именем TDL3_volum.bin одновременно с сохранением зараженных файлов:
Для извлечения файлов, хранящихся на зашифрованной файловой системе, используется утилита TDL3_extract.exe, которой в качестве параметра передаётся путь к файлу TDL3_volume.bin:
C:\> TDL3_extract.exe dump_28.04-05.56.44\TDL3_volume.bin Extracting data from file dump_28.04-05.56.44\TDL3_volume.bin... 0x00000001 0x000001ac config.ini 0x00000002 0x00005f8c tdl 0x0000001b 0x00000360 rsrc.dat 0x0000001c 0x0000007f bckfg.tmp 0x0000001d 0x00005000 tdlcmd.dll Press any key to quit...