• Чт. Мар 30th, 2023

Грустный охранник. Поиск и эксплуатация уязвимости в драйвере AdGuard для Windows

Грустный охранник.  Поиск и эксплуатация уязвимости в драйвере AdGuard для Windows

В этой статье я расскажу, как я нашел бинарную ошибку в драйвере AdGuard. Уязвимость получила номер CVE-2022-45770. Я покажу, как я изучил блокировщик рекламы и раскрутил уязвимость к локальному повышению привилегий. Попутно мы изучим низкоуровневое устройство Windows.

Информация

Спасибо за консультацию в процессе исследования @denis_skvortkov. в его блог Классные статьи об эксплуатации уязвимостей в антивирусе для Windows. Теперь взгляд Денниса упал на Avast.

Предупреждение

Эта статья носит ознакомительный характер и предназначена для специалистов по безопасности, выполняющих тестирование по контракту. Автор и редакция не несут ответственности за любой ущерб, причиненный в результате использования предоставленной информации. Распространение вредоносных программ, нарушение работы систем и нарушение конфиденциальности преследуются по закону.

Как это все началось

Я мало что понимал в драйверах Windows до того, как прочитал книгу Павла Йосифовича Программирование ядра Windows. Книга начинается с простого драйвера hello world и заканчивается сложным драйвером фильтра. Также рассказывается об отладке драйверов в виртуальной машине с WinDbg на хосте и о типичных ошибках программирования драйверов. После прочтения, конечно, хочется применить свои знания на практике и разобрать какой-нибудь драйвер. Может нам повезет и мы найдем уязвимость?

Почему AdGuard

Адгард — классный блокировщик рекламы, поддерживающий зашифрованный DNS (DoH, DoT, DoQ). Драйвер WDM используется для блокировки запросов рекламы для всех приложений, а не только для браузера. Давайте установим AdGuard на Windows 10 в виртуальную машину и приступим к ее изучению.

Так получилось, что я установил сборку для х86, поэтому будем исследовать 32-битный драйвер.

Поверхность атаки

Первый шаг — убедиться, что драйвер находится на поверхности атаки. То есть непривилегированное приложение может открыть драйвер для взаимодействия — чтения, записи и отправки IOCTL. В этом нам поможет пара строчек на PowerShell с библиотекой NtObjectManager Джеймс Форшоу.

ЧИТАТЬ   Австралия направит 70 военных инструкторов для обучения украинской армии

Для определения артефактов (файлов, ключей реестра) исследуемого продукта использование Microsoft Анализатор поверхности атаки. С его помощью нужно собрать два снимка ОС: до установки изучаемой программы и после, и сделать разницу, которая покажет установленные артефакты. Таким образом, вы можете определить путь устройства в Диспетчере объектов:

\Device\CtrlSM_Protected2adgnetworkwfpdrv

Ошибка открытия драйвера устройства

Драйвер открыть не удалось. Ошибка 0xC000010 STATUS_INVALID_DEVICE_REQUESTИ это не 0xC0000022 ACCESS_DENIED! Это означает, что у нас есть доступ к устройству водителя, но водителю ничего не понравилось в нашем запросе. Такое странное поведение — отличный повод начать реверсировать. Давайте откроем драйвер в IDA и посмотрим на некоторые важные места.

Первое место — код инициализации драйвера в функциях DriverEntry.

Создание драйвера устройства
Функция создания драйвера устройства

Функция IoCreateDevice() Потенциально небезопасно, поскольку не позволяет явно указать DACL. Таким образом, DACL берется либо из INF-файла, либо из потока DACL или процесса, создавшего его. Также обратите внимание, что ресурс создан с неэксклюзивным доступом (EXCLUSIVE_FALSE).

Информация

Рекомендуется использовать IoCreateDeviceSecure()где вы можете явно передать DACL.

Аргумент FILE_DEVICE_SECURE_OPEN настоящее. Если бы его не было, можно было бы обойти строгий DACL, открыв произвольный файл на устройстве. Смотрим дальше.

Флаг DO_DIRECT_IO Говорит, что usermode-буферы для звонков WriteFile() И ReadFile() Будет отображено в основном пространстве, и у нас есть возможность атаковать TOCTOU в случае Двойной выбор В коде драйвера. Если бы на месте этого флага был METHOD_NEITHERбыло бы еще интереснее.

Здесь тоже все хорошо, идем дальше.

На втором месте функция – обработчик открытия драйвера устройства. Найти ее легко. В коде инициализации драйвера необходимо явно назначать обработчики функций OpenFile(), WriteFile() И ReadFile().

Обработчики запросов пользовательского режима в коде драйвера
Обработчики запросов пользовательского режима в коде драйвера

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

Онлайн-декодер OSR IOCTL

Флаг DO_DIRECT_IO Влияет на способ передачи данных из юзермода в ядро ​​только для FileRead() И FileWrite(). за DeviceIoControl() Этот метод жестко встроен в код IOCTL. Для быстрого просмотра этого метода можно использовать инструмент osronline.com.

Мы можем легко найти обработчик для открытия устройства.

Обработчик IRP_MJ_CREATE
Обработчик IRP_MJ_CREATE

Здесь реализован кастомный эксклюзивный доступ к драйверу — PID открывшего его процесса хранится в глобальной переменной hasOwner. При следующей попытке открыть драйвер выдает ошибку STATUS_INVALID_REQUEST.

И что это за ПИД? Кто первым обнаружил драйвер? Это сервисный процесс. AdguardSvc.exe. Можем ли мы повлиять на это? Удивительно да. Убейте его Terminate() Мы не понимаем, но в UI-Processa AdguardUI.exe Есть кнопка “отключить защиту”.

Отключить диалоговое окно AdGuard
Отключить диалоговое окно AdGuard

Когда процесс AdguardSvc.exe закроется, попробуйте снова открыть драйвер устройства.

Get-NtFile() с теми же аргументами возвращает другой результат
Get-NtFile() с теми же аргументами возвращает другой результат

Мы получаем право читать, писать и отправлять IOCTL от непривилегированного пользователя. Отлично! Поверхность атаки определена.

На данном этапе исследования можно отметить две ошибки.

  1. Собственная реализация монопольного доступа к драйверу вместо необходимых аргументов в IoCreateDevice(EXCLUSIVE_TRUE). Не критично.
  2. Архитектура спроектирована таким образом, что привилегированный процесс службы монопольно открывает устройство. Тогда логично было бы повесить на устройство соответствующий DACL, но ведь доступ есть у всех. Крайне важно, поскольку это разорвало бы всю цепочку атаки.

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

Кстати, вы можете проверить DACL устройства с помощью следующей команды:

icacls.exe \\.\Device\<name>

лев:

accesschk.exe -l \\.\GLOBALROOT\Device\<name>

В листинге дизассемблера мы заметили большое количество обработчиков IOCTL. Что можно сделать вместо того, чтобы переворачивать каждое из них?

Фаззинг

Фаззинг драйверов немного сложнее, чем фаззинг приложений пользовательского режима, потому что работа идет не с виртуальным пространством отдельного процесса, а со всей ОС. Отсюда сложность инфраструктуры — установка агента в виртуальную машину и запуск его в QEMU/KVM, как, например, во фьюзере. КАФЛ.



Source