Sad guard. Ищем и эксплуатируем уязвимость в драйвере AdGuard для Windows

Содержание статьи

  • Как все начиналось
  • Почему AdGuard
  • Поверхность атаки
  • Фаззинг
  • Подготовка
  • DIBF
  • Реверс драйвера
  • По следам фаззера
  • Еще немного реверса
  • Примитивы
  • Проблемы 1 и 2. KASLR
  • Проблема 3. Сравнение с index
  • Эксплуатация
  • Демонстрация (видео)

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

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:

DeviceCtrlSM_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().

Об­работ­чики usermode-зап­росов в коде драй­вера

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

OSR Online IOCTL Decoder

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

Без тру­да находим обра­бот­чик откры­тия девай­са.

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

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

И что это за PID? Кто открыл драй­вер рань­ше всех? Это сер­висный про­цесс AdguardSvc.exe. Можем ли мы на него воз­дей­ство­вать? На удив­ление — да. Убить его через Terminate() нам не хва­тит прав, но у UI-про­цес­са AdguardUI.exe есть кноп­ка «Вык­лючить защиту».

Ди­ало­говое окно отклю­чения AdGuard

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

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

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

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

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

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

    icacls.exe \.Device<name>

    Ли­бо:

    accesschk.exe -l \.GLOBALROOTDevice<name>

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

     

    Фаззинг

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

    Источник: xakep.ru

    Ответить

    Ваш адрес email не будет опубликован. Обязательные поля помечены *