четверг, мая 18, 2006

Утилита AVZ v4.16: некоторые "забавные" особенности

В списке доработок и модификаций данной версии утилиты имеются следующие строки:
[+] Доработан avz.sys - введена защита от атаки на драйвер посылкой ему IRP пакетов сторонними приложениями
[+] Доработан AVZ Guard - введено несколько новых контуров защиты процесса AVZ от типовых методик закрытия
процесса, применяемого распространенными троянскими программами

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

Рассмотрим сначала "защиту от атаки на драйвер посылкой ему IRP пакетов сторонними приложениями".

Драйвер утилиты AVZ с именем avz.sys служит двум основным целям:
- обеспечивает поиск перехватчиков системных сервисов в таблице SSDT (KiServiceTable);
- производит блокирование работы kernel mode руткитов.

Чтобы обеспечить выполнение указанных функций, основной модуль утилиты, avz.exe, динамически загружает драйвер avz.sys посредством интерфейса к Service Control Manager (SCM), после чего осуществляет обмен с драйвером путем пересылки IRP пакетов. В частности, таким способом avz.exe может получить адрес таблицы SDT (KeServiceDescriptorTable), адрес таблицы SSDT (KiServiceTable), общее число системных сервисов в таблице SSDT, получить текущий адрес системного сервиса по его номеру, установить новый (и, в том числе, восстановить в оригинал) адрес системного сервиса по его номеру и т.п. Этот обмен информацией до сих пор (до версии 4.16) осуществлялся в полностью открытой форме, что позволяло любому стороннему приложению воспользоваться загруженным драйвером avz.sys в своих целях, и, в частности, воспрепятствовать попыткам AVZ его обнаружить и обезвредить, "завалив систему" в BSOD в тот момент, когда утилита подгружает драйвер. Этому также способствует то обстоятельство, что для выполнения одной из функций драйвера, а именно установки нового адреса системного сервиса по его номеру, avz.exe передает драйверу как номер, так и новый адрес системного сервиса, что, по сути, является довольно опасным с точки зрения безопасности системы действием, т.к. любое злонамеренное приложение может в качестве адреса передать, к примеру, нулевое значение, что неминуемо приведет к падениею системы в BSOD. Некоторые время назад я указал на наличие этой уязвимости в AVZ и написал довольно простое приложение, которое демонстрировало ее, после чего автор, Олег Зайцев, решил "исправить оплошность" и доработал процедуру обмена в новой версии утилиты.

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

Как показало мое небольшое исследование, Олег решил обойтись лишь дополнительной проверкой источника IRP пакета, передаваемого драйверу. "Надежный", или "доверенный" источник (к примеру, модуль avz.exe), прежде чем воспользоваться одной из вышеперечисленных функций драйвера, должен передать драйверу специальный пакет, где в качестве аргументов используются идентификатор "доверенного" процесса, а также 3 числа, сформированные специальным образом. Драйвер сначала проверяет эти числа в соответствии с некоторым алгоритмом, а потом, если проверка успешна, записывает во внутреннюю переменную полученный идентификатор "доверенного" процесса. В дальнейшем только процесс с указанным идентификатором может обращаться к функциям драйвера, всем же остальным процессам выдается код ошибки (STATUS_NOT_IMPLEMENTED). Приведем кусочек кода драйвера avz.sys, где осуществляется эта проверка на "надежность" источника:
.text:00011069            call    ds:IoGetCurrentProcess
.text:0001106F cmp ebx, 222024h ; DeviceIoControl.IoControlCode == 'Set "trusted" process Id'?
.text:00011075 jnz short loc_110A3 ; No, skip...
.text:00011077 cmp [esp+18h+var_8], 10h ; DeviceIoControl.OutputBufferLength == 16?
.text:0001107C jb short loc_110A3 ; No, skip...
.text:0001107E mov ecx, [ebp+0Ch] ; UserBuffer.argC
.text:00011081 mov eax, [ebp+8] ; UserBuffer.arg8
.text:00011084 mov edx, [ebp+4] ; UserBuffer.arg4
.text:00011087 lea eax, [ecx+eax+5] ; arg8 + argC + 5
.text:0001108B imul eax, ecx ; * argC
.text:0001108E cmp edx, eax ; arg4 == (arg8 + argC + 5) * argC ?
.text:00011090 jnz short loc_110A3 ; No, skip...
.text:00011092 cmp eax, 5DCh ; (arg8 + argC + 5) * argC > 1500
.text:00011097 jbe short loc_110A3 ; No, skip...
.text:00011099 mov eax, [ebp+0] ; UserBuffer.arg0 == "trusted" process Id
.text:0001109C mov _dwProcessId, eax ; Set "trusted" process Id
.text:000110A1 xor esi, esi
.text:000110A3
.text:000110A3 loc_110A3:
.text:000110A3 call PsGetCurrentProcessId
.text:000110A8 cmp eax, _dwProcessId ; CurrentProcessId == "trusted" process Id?
.text:000110AE jnz loc_112A1 ; No, return STATUS_NOT_IMPLEMENTED code
.text:000110B4 cmp ebx, 222000h ; DeviceIoControl.IoControlCode == 'Get SDT address'?
.text:000110BA jnz short loc_110DC ; ...etc.

Как видим, автор утилиты в качестве "головоломки" предлагает решить нам следующую систему с 3-мя неизвестными:
x = (y + z + 5) * z
x > 1500

Положим y = 0, тогда получаем систему с 2-мя неизвестными:
x = (z + 5) * z
x > 1500

Для простоты перепишем ее в качестве следующего неравенства с одним неизвестным:
(z + 5) * z > 1500

Возьмем z = 40, тогда:
x = (z + 5) * z = 45 * 40 = 1800

Итак, в результате мы имеем: x = 1800, y = 0, z = 40. Теперь эти цифры, наряду с идентификатором текущего процесса, можно использовать в качестве параметров, передаваемых в драйвер avz.sys!

Вот такой немного примитивной проверкой автор AVZ решил обмануть взломщиков своей программы! :) Для демонстрации указанной уязвимости я немного переделал свой "эксплойт", добавив в него код для установки собственного процесса в качестве доверенного. Его исходный текст и исполнимый модуль вы сможете найти здесь: AVZDriverCrash. Будьте, однако, внимательны и осторожны: этот "эксплойт" укладывает систему в BSOD в тот момент, когда вы посредством кнопки "Пуск" в AVZ начинаете исследование системы и дело доходит до пункта "1.2 Поиск перехватчиков API, работающих в KernelMode"!. Если же во время запуска "эксплойта" на выполнение драйвер AVZ уже загружен - произойдет немедленный BSOD!

У вас, возможно, возник вопрос, а зачем все эти танцы с бубнами вокруг установки и проверки в драйвере идентификатора доверенного процесса?. Не проще ли было сделать это ровно один раз или не делать вообще - ведь утилита, будучи запущенной, динамически загружает драйвер, а потом динамически выгружает его? Так вот, дело обстоит несколько сложнее. Все эти "игры" с идентификаторами процессов нужны для тех случаев, когда пользователь загружает несколько экземпляров утилиты одновременно - в результате все эти экземпляры обслуживаются одним единственным (загруженным первым) драйвером! Когда последний экземпляр AVZ завершает работу, он и выгружает драйвер! Хорошо, скажете вы, а почему бы тогда не запретить возможность запуска нескольких экземпляров утилиты - ведь это сразу бы решило все проблемы! Однако же и тут есть подводный камень: к сожалению, практически вся утилита (за исключением момента, когда идет сканирование файловой системы) написана в одном потоке (thread), ее GUI в этом смысле не отделен от функциональной части, и - даже более того - повсюду используются т.н. модальные окна, что вообще препятствует одновременному отображению более чем одного окна! Таким образом, программа, позиционируемая как "утилита для исследования системы" превращается в очень неудобный для всестороннего исследования системы инструмент, имея, тем не менее, все функциональные составляющие для такого исследования! В результате, для того чтобы с помощью утилиты получить более-менее исчерпывающую картину состояния системы, приходится загружать одновременно несколько (иногда 6-8) ее экземпляров!

Итак, с IRP пакетами мы как-будто разобрались, теперь можно перейти ко "второму блюду нашего меню", т.е. к тому, что в "AVZ Guard введено несколько новых контуров защиты процесса AVZ от типовых методик закрытия процесса, применяемого распространенными троянскими программами".

Я готов поверить в то, что троянские программы в своей массе довольно примитивны и пользуются, в основном, довольно простыми и хорошо описанными методиками - это касается, естественно, и "типовых методик закрытия процесса". Режим AVZGuard, введенный в версии 4.15 утилиты AVZ, одной из своих главных задач как раз и поставил защиту процесса avz.exe от возможного терминирования злонамеренными программами. Насколько она успешна, можно судить о той критике, которой подвергся этот режим, на форуме VirusInfo. Понимая, что простой защиты может быть не всегда достаточно (а, может быть, и вняв указанной критике), автор утилиты ввел ряд модификаций в драйвер avzsg.sys, являющийся "сердцем" режима AVZGuard. Я не буду сейчас подробно останавливаться на этих доработках (возможно, я сделаю это позднее) - скажу лишь то, что самый простой метод терминирования процесса avz.exe, о котором я рассказал еще при обсуждении версии 4.15, успешно работает до сих пор! Да, речь идет о терминировании с использованием стандартного "Диспетчера процессов" (taskmgr.exe), входящего в состав операционной системы! Тогда, во время обсуждения, автор сослался на то, что трояны не будут применять таких методик, потому что это, якобы, "довольно сложно" для них! Насколько это на самом деле просто, вы можете убедиться сами: для демонстрации этой возможности я написал небольшую программу, исходный текст и исполнимый модуль которой вы сможете найти здесь: AVZGuardKill. Программа должна быть запущена на выполнение до установки в AVZ режима AVZGuard или же вообще до запуска AVZ (как это бывает с большинством злонамеренных программ), чтобы произошло терминирование процесса avz.exe. Кроме того, программа будет ждать именно установки режима AVZGuard, чтобы показать, что именно в этом режиме она эффективна против AVZ! Не забудьте также, что после терминирования AVZ драйвер avzsg.sys будет оставаться активным и продолжать препятствовать запуску других программ, в связи с чем вам ничего не останется, как просто завершить работу компьютера (перегрузиться)!

И еще один момент: старая ошибка, о которой говорилось еще для версии 4.15, так и осталась существовать в новой версии! Суть ее в следующем: если вы запустите AVZ на выполнение и сразу же после этого установите режим AVZGuard, то провести полное исследование системы с поиском kernel mode руткитов вам уже не удастся! Дело тут в том, что AVZ в режиме AVZGuard препятствует, в частности, загрузке драйверов "чужими" процессами, блокируя доступ к реестру, но разрешая все эти действия только своему и доверенным процессам, запущенным пользователем. Учитывая же тот факт, что AVZ делает динамическую установку и загрузку драйвера не самостоятельно, а, как я уже отмечал выше, через интерфейс к SCM, становится понятно, что AVZ активно препятствует выполнению порученной им же функции "недоверенному" процессу SCM - иными словами, AVZ в данном случае опосредованно запрещает загрузку своего собственного драйвера, т.е. "бьет своих, чтобы чужие боялись!" :) Чтобы избежать этого, нужно включать режим AVZGuard только после исследования системы посредством кнопки "Пуск", т.е. после предварительной загрузки драйвера avz.sys!

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



<< Home

This page is powered by Blogger. Isn't yours?