Содержание статьи
info
За консультацию в процессе исследования спасибо @Denis_Skvortcov. В его блоге крутые статьи на тему эксплуатации уязвимостей в антивирусах для Windows. Сейчас взгляд Дениса пал на Avast.
warning
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
Как все начиналось
Я мало что понимал в виндовых драйверах до того, как прочитал книгу Павла Йосифовича Windows Kernel Programming. В книге все начинается с простого драйвера в духе Hello World и заканчивается сложным драйвером‑фильтром. Также рассказывается про отладку драйверов в виртуальной машине с WinDbg на хосте и про типичные ошибки программирования драйверов. После прочтения, конечно же, хочется применить знания на практике и разобрать какой‑нибудь драйвер. Может, нам повезет и мы найдем уязвимость?
info
Статья рассчитана на тех, кто немного разбирается в реверс‑инжиниринге сишного кода. В ней не будет подробного разбора процесса реверса. За более детальным описанием реверса обратись к моей первой статье «Разборки на куче. Эксплуатируем хип уязвимого SOAP-сервера на Linux».
Почему AdGuard
AdGuard — классный блокировщик рекламы, поддерживающий шифрованный DNS (DoH, DoT, DoQ). Чтобы блокировать рекламные запросы всех приложений, а не только браузера, используется WDM-драйвер. Давай установим AdGuard на Windows 10 в виртуальной машине и начнем его изучать.
Так получилось, что я установил сборку для x86, поэтому исследовать мы будем 32-битный драйвер.
Поверхность атаки
Первым делом нужно убедиться, что драйвер находится на поверхности атаки. То есть непривилегированное приложение может открыть драйвер для взаимодействия — чтения, записи и отправки IOCTL. В этом нам поможет пара строк на PowerShell с библиотекой NtObjectManager за авторством Джеймса Форшоу.
Для определения артефактов (файлов, ключей реестра) исследуемого продукта прекрасно подходит утилита от Microsoft Attack Surface Analyzer. С ее помощью нужно собрать два снапшота ОС: до установки исследуемой программы и после, а также создать дифф, который покажет установленные артефакты. Таким образом можно определить путь девайса в Object-Manager:
\Device\CtrlSM_Protected2adgnetworkwfpdrv
Драйвер открыть не получилось. Ошибка 0xC000010 STATUS_INVALID_DEVICE_REQUEST
, и это не 0xC0000022 ACCESS_DENIED
! Значит, доступ к девайсу драйвера у нас есть, но драйверу что‑то не понравилось в нашем запросе. Такое странное поведение — отличный повод приступить к реверсу. Давай откроем драйвер в IDA и посмотрим на несколько важных мест.
Первое место — инициализирующий код драйвера в функции DriverEntry
.
Функция IoCreateDevice(
потенциально небезопасна, так как не позволяет явно указать DACL. Таким образом, DACL берется либо из .INF-файла, либо из DACL-треда или процесса, который его создает. Также отметим, что девайс создается с неэксклюзивным доступом (EXCLUSIVE_FALSE
).
info
Рекомендуется использовать IoCreateDeviceSecure(
, куда можно явно передать DACL.
Аргумент FILE_DEVICE_SECURE_OPEN
присутствует. Если бы его не было, то было бы можно обойти строгий DACL, открыв произвольный файл на этом девайсе. Смотрим дальше.
Флаг DO_DIRECT_IO
говорит о том, что usermode-буферы для вызовов WriteFile(
и ReadFile(
будут мапиться в пространство ядра и у нас есть возможности для атаки TOCTOU в случае double fetch в коде драйвера. Если бы на месте этого флага был METHOD_NEITHER
, было бы еще интереснее.
Здесь тоже все нормально, двигаемся дальше.
Второе место — функция — обработчик открытия девайса драйвера. Найти ее просто. В коде инициализации драйвера необходимо явно назначить обработчики функций OpenFile(
, WriteFile(
и ReadFile(
.
На скриншотах IDA ты видишь названия переменных и функций, придуманных мной во время реверса. Конечно же, символа от бинаря нам никто не даст.
OSR Online IOCTL Decoder
Флаг DO_DIRECT_IO
влияет на метод передачи данных из юзермода в ядро только для FileRead(
и FileWrite(
. Для DeviceIoControl(
метод зашит в код IOCTL. Для быстрого просмотра метода можешь использовать ресурс osronline.com.
Без труда находим обработчик открытия девайса.
Здесь реализован кастомный эксклюзивный доступ к драйверу — PID открывшего его процесса сохраняется в глобальную переменную hasOwner
. Следующая попытка открыть драйвер возвращает ошибку STATUS_INVALID_REQUEST
.
И что это за PID? Кто открыл драйвер раньше всех? Это сервисный процесс AdguardSvc.
. Можем ли мы на него воздействовать? На удивление — да. Убить его через Terminate(
нам не хватит прав, но у UI-процесса AdguardUI.
есть кнопка «Выключить защиту».
Когда процесс AdguardSvc.
закроется, снова попробуем открыть девайс драйвера.
Получаем права на чтение, запись и отправку IOCTL от непривилегированного пользователя. Отлично! Поверхность атаки определена.
На данном этапе исследования можно отметить две ошибки.
- Своя реализация эксклюзивного доступа к драйверу вместо нужных аргументов в
IoCreateDevice(
. Некритично.EXCLUSIVE_TRUE) - Архитектурно задумано так, что сервисный привилегированный процесс эксклюзивно открывает девайс. Тогда было бы логично повесить на девайс соответствующий DACL, а по факту доступ имеют все. Критично, так как это сломало бы весь attack chain.
Исследование можно было заканчивать после неудачной попытки открыть девайс драйвера, но мы внимательно отнеслись к коду ошибки и получили первую зацепку.
Кстати, проверить DACL девайса ты можешь и с помощью такой команды:
icacls.exe \\.\Device\<name>
Либо:
accesschk.exe -l \\.\GLOBALROOT\Device\<name>
В дизассемблерном листинге мы заметили большое количество обработчиков IOCTL. Что можно сделать вместо того, чтобы реверсить каждый?
Фаззинг
Фаззинг драйверов несколько сложнее фаззинга юзермодных приложений, потому что работа происходит не с виртуальным пространством единственного процесса, а со всей ОС целиком. Отсюда усложнение инфраструктуры — установка агента в виртуальную машину и запуск ее в QEMU/KVM, как, например, в фаззере kAFL.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»