Git Product home page Git Product logo

ahnlab-0day's Introduction

This is from a russian market, from the creators of gandcrab.

Привет Форумчане. На связи кодер краба, Мы поведаем в этой небольшой статье об интерестных вещах простым языком, таким образом чтоб каждый мог примерно понять что из себя представляют дыры в ядре ос Windows. Использовать мы будем язык программирования C. В данной статье дизассемблируется драйвер под x64 версию Windows в поисках уязвимости.

Цель: найти 0day уязвимость Продукт: AhnLab V3 Lite ( корейский антивирус ) Результат: Arbitrary Memory Overwrite, приводящий в перспективе к LPE. В качестве PoC используется DoS-эксплойт ахнлаба:

http://filestorage.biz/download.php?file=e...84050d41e254261 Пароль GandCrab

Начнём с некоторых базовых вещей:

  1. Теория

На сегодняшний день в антивирусных продуктах выработалось чёткое правило: Антивирусная программа должна иметь компонент режима ядра. С чем это связано? Дело в том, что контролировать поведение приложений и такие вещи как сетевой траффик значительно проще и эффективнее из режима ядра. В пользовательском режиме, существует масса ухищрений позволяющих обмануть проактивную защиту или прервать работу антивируса. Соотвественно если же часть антивируса находится в виде драйвера в ядре, то антивирус имеет максимальные привилегии которые позволяют ему фильтровать любые действия с ос/железом.

Что такое драйвер? Да по сути это тот же самый PE ( его внутренняя структура схожа с EXE файлом ), только загружается он в контексте режима ядра и по функционалу больше похож на дллку. Точка входа драйвера обычно называется DriverEntry и принимает она два аргумента

Код

NTSTATUS DriverEntry( In struct _DRIVER_OBJECT *DriverObject, In PUNICODE_STRING RegistryPath );

Первый аргумент, это объект драйвера, который позволит задать обработчики для IRP запросов и дать нам возможность общаться с пользовательского режима с драйвером, посредством IOCTL.

IRP: I/O request packet сокращённо, специальные пакеты через которые драйвера могут общаться друг с другом а также с компонентами из пользовательского режима Irp имеет следующую структуру

Код

typedef struct _IRP { PMDL MdlAddress; ULONG Flags; union { struct _IRP *MasterIrp; PVOID SystemBuffer; } AssociatedIrp; IO_STATUS_BLOCK IoStatus; KPROCESSOR_MODE RequestorMode; BOOLEAN PendingReturned; BOOLEAN Cancel; KIRQL CancelIrql; PDRIVER_CANCEL CancelRoutine; PVOID UserBuffer; union { struct { union { KDEVICE_QUEUE_ENTRY DeviceQueueEntry; struct { PVOID DriverContext[4]; }; }; PETHREAD Thread; LIST_ENTRY ListEntry; } Overlay; } Tail; } IRP, *PIRP;

Драйвера могут выстраиваться в цепочки, например мы можешь создать драйвер фильтр для определённого виртуального устройства и фильтровать IRP запросы к нему, либо передавая их дальше, либо отсекая лишнее. Для этого у каждой структуры IRP вылеляет так называемый блок стека для драйвера: IO_STACK_LOCATION которая находится в Irp->Tail.Overlay Когда Irp запрос инициируется для \Device\Tcp, на который мы создали драйвер фильтр ( пусть он будет называться \Device\FilterTcp ), система ввода вывода Windows грубо говоря посмотрев на количество устройств в цепочке, создаст соотвественно два блока стека IO_STACK_LOCATION для нашего фильтра и непосредственно для \Device\Tcp. При этом по правилам в фильтре мы должны были бы вызвать IoGetNextIrpStackLocation и подготовить IRP для следующего драйвера в цепочке, после необходимой фильтрации.

IO_STACK_LOCATION содержит в себе необходимые параметры для IRP запроса, буффер с входными данными, буфер для выходных данных и так называемые коды Irp функций. Драйвер регистрирует обработчики на каждый из данных кодов, список можно найти тут https://docs.microsoft.com/en-us/windows-ha...-function-codes Нас будет интересовать IRP_MJ_DEVICE_CONTROL так как именно он выставляется в IO_STACK_LOCATION при вызове DeviceIoControl с IOTCL кодом операции.

IOCTL: Input Output Control - Это собственно запрос к драйверу в виде 32х битной константы, который пробрасывается через IRP пакет в драйвер.

Далее чтобы собственно обратиться к драйверу, необходимо в нём создать виртуальное устройство через IoCreateDevice и символическую ссылку для этого устройства через IoCreateSymbolicLink. Данная символическая ссылка будет передаваться функции CreateFile в пользовательском режиме и далее через DeviceIoControl мы сможем отправлять IRP запросы драйверу.

  1. Переходим к практике: Ahnlab V3 Security Lite.

Нами был выбран бесплатный корейский антивирус Ahnlab V3 Security Lite, так как он создал порядочно проблем для нашего локера. Итак, была поднята VmWare с Windows 10 x64 с пакетом корейского языка, туда был проинсталлен данный антивирус. Далее после перевода в отладочный режим, мы подключаемся через IDA Pro + Windbg плагин к ядру и начинаем отлаживать.

Уязвимым драйвером является TfFRegNt.sys ( лежит тут: C:\Program Files\AhnLab\V3Lite30\ ) Соотвественно далее пойдёт x64 ассемблер. Соотвественно первые четыре параметра при вызовах функций STDCALL будут передаваться не через стек а через регистры: RCX,RDX,R8,R9 Более подробно об этом можно почитать тут: https://msdn.microsoft.com/ru-ru/library/ms235286.aspx

База модуля TfFRegNt в памяти FFFFF800FBC80000 в данном примере.

Данный драйвер создаёт символическую ссылку: \\.\AntiStealth_V3LITE30F, чтобы это выяснить, смотрим xref'ы на IoCreateSymbolicLink:

user posted image

Переходим в sub_FFFFF800FBC8295C+EB, видим что в rdx кладётся локальная переменная (SymbolicLinkName) представляюшая PUNICODE_STRING SymbolicLinkName. Данная переменная инициализируется вызовом RtlUnicodeString с исходной строкой в rdx, в rdx адрес данной строки копируется из переменной SourceString ( подчёркнуто красным на скрине ).

user posted image

Смотрим что пишется в переменную SourceString, видим что в самом начале функции туда заносится r9

user posted image

Посмотрим что в r9 в качестве параметра передаётся в функцию sub_FFFFF800FBC8295C:

user posted image

Видим глобальную переменную: qword_FFFFF800FBCA87D8 в ней и лежит поинтер на строку.

user posted image

вводим в консоли отладчика db FFFF970AB55297F0 и видим содержимое памяти по данному адресу:

user posted image

И собственно видим AntiStealth_V3LITE30F

Далее, наша задача состоит в том, чтобы найти обработчик IOCTL запросов для IRP, IRP_MJ_DEVICE_CONTROL (числовой код 0xe ). Данный обработчик выставляется через DriverObject, путем копирования адреса функции в элемент массива MajorFunction:

Код

WINDBG>dt nt!_DRIVER_OBJECT +0x000 Type : Int2B +0x002 Size : Int2B +0x008 DeviceObject : Ptr64 _DEVICE_OBJECT +0x010 Flags : Uint4B +0x018 DriverStart : Ptr64 Void +0x020 DriverSize : Uint4B +0x028 DriverSection : Ptr64 Void +0x030 DriverExtension : Ptr64 _DRIVER_EXTENSION +0x038 DriverName : _UNICODE_STRING +0x048 HardwareDatabase : Ptr64 _UNICODE_STRING +0x050 FastIoDispatch : Ptr64 _FAST_IO_DISPATCH +0x058 DriverInit : Ptr64 long +0x060 DriverStartIo : Ptr64 void +0x068 DriverUnload : Ptr64 void +0x070 MajorFunction : [28] Ptr64 long

За этим далеко не надо идти, обработчики выставляются сразу после вызова функции создания символической ссылки, в SetIrpHandlers ( переименовано ) в rcx собственно и лежит тот самый DriverObject

user posted image

Сам этот объект сохраняется в стеке в самом начале DriverEntry:

user posted image

Внутри SetIrpHandlers выставляется один хэндлер на все IRP коды функций, ( переименовано в irp_handler ) В irp_handler, находим сравнение с 0xE ( код IRP_MJ_DEVICE_CONTROL ) и соответствующую ветку которая вызывает нужный нам IRP_MJ_DEVICE_CONTROL обработчик

user posted image

Внутри обработчика по адресу FFFFF800FBC8C534 видим инструкции в духе: cmp [rsp+188h+VARD0], 82000022h

Это собственно и есть проверка обрабатываемых IOCTL, значение 82000022 является IOCTL кодом который передаётся драйверу посредством DeviceIoControl с юзермода.

Обратим внимание на ветку обрабатывающую IOCTL 0x82000010

user posted image

Как видим, обработчик проверяет длинну буффера переданную в DeviceIoControl и значения внутри самого буффера, проверяя первые 8 байт на 0, что намекает, что с юзермода передаётся указатель. Обратите внимание что указатель в InputBufferData никак не проверяется на принадлежность к юзермод адресу. ( ProbeForRead / ProbeForWrite проверок нет ).

Посмотрим к чему это приводит, переходим в ModifyGlobalVar ( FFFFF800FBC87AF4 ) Видим в графе, почти в самом конце такую конструкцию:

user posted image

GlobalVariable это глобальная переменная с адресом FFFFF800FBCA8830. Как видно туда пишется поинтер, из нашего входящего буффера с юзермода. Но перед этим в sub_FFFFF800FBC83DC4 передаётся поинтер на локальную переменную var_14, в которую очевидно в данной функции записывается некое значение, размером в 4 байта которое потом сравнивается с 4 байтами из оффсета 8 нашего входящего буффера.

Мы не будем подробно расписывать в дизасме логику sub_FFFFF800FBC83DC4, это своеобразная защита от фаззинга, данная функция просто парсит из режима ядра PE заголовок файла ntoskrnl.exe и вытаскивает из IMAGE_NT_HEADERS.FileHeader значение TimeDateStamp, которое потом и сравнивается со значением в нашем входящем буффере. Если вы не знакомы со структурой PE файла, подробнее можно прочитать здесь: https://habr.com/post/266831/

Итак, отлично. Мы можем влепить любой адрес какой захотим в эту глобальную переменную, но что же дальше? Посмотрим xref'ы на неё:

user posted image

Предпоследний xref приводит нас сюда:

user posted image

Как видим, наш произвольный адрес используется в качестве указателя на структуру ERESOURCE

Код

WINDBG>dt nt!_ERESOURCE +0x000 SystemResourcesList : _LIST_ENTRY +0x010 OwnerTable : Ptr64 _OWNER_ENTRY +0x018 ActiveCount : Int2B +0x01a Flag : Uint2B +0x01a ReservedLowFlags : UChar +0x01b WaiterPriority : UChar +0x020 SharedWaiters : Ptr64 Void +0x028 ExclusiveWaiters : Ptr64 Void +0x030 OwnerEntry : _OWNER_ENTRY +0x040 ActiveEntries : Uint4B +0x044 ContentionCount : Uint4B +0x048 NumberOfSharedWaiters : Uint4B +0x04c NumberOfExclusiveWaiters : Uint4B +0x050 Reserved2 : Ptr64 Void +0x058 Address : Ptr64 Void +0x058 CreatorBackTraceIndex : Uint8B +0x060 SpinLock : Uint8B

и передаётся в апи ядра винды ExAcquireResourceSharedLite https://docs.microsoft.com/en-us/windows-ha...ourcesharedlite

Давайте попробуем затриггерить вызов этой функции. Пройдясь по xref'ам мы увидим что приведёт нас сюда IOCTL 0x8200001E

user posted image

В ветке от данного IOCTL чекается:

  • размер входящего буфера ( должен быть > 0x10 )
  • указатель на выходной буффер ( не должен быть равен 0 )
  • длинна выходного буффера ( не равна нулю )
  • двойное слово по смещению + 8 от начала входного буффера не должно быть нулём
  • двойное слово по смещению + 8 от начала входного дожно быть меньше ли равно 0xFF
  • двойное слово по смещению + 0xC от начала входного буффера должно быть тройкой

Если все условия сошлись мы попадём в sub_FFFFF800FBC8C140 которое после дополнительных проверок дёрнет функцию FFFFF800FBC87F58 с глобальной переменной в которую мы записали на предыдущем шаге через IOCTL 0x82000010 значение 0xDEADBEEFDEADBEEF, итог:

user posted image

  1. В заключение мы получаем как миниум DoS. Однако, потенциально данный косяк может привести к Write Where уязвимости с возможностью выполнения кода в ядре/повышения прав. Мы пытались создать полноценный эксплойт, однако главная проблема заключается в функции ExAcquireResourceSharedLite. Из ERESOURCE+0x60 тянется спинлок, который система блокирует и начинает ждать. Подделать корректно объект спинлока у нас не вышло, ОС тупо зависает на бесконечном ожидании в ExAcquireResourceSharedLite. Поэтому дальнейшие исследования в данном направлении остаются на читателях статьи. Все эксперты форума приветствуются в попытке создать полноценный эксплойт для данной баги.

  2. Дополнительные мысли для размышления или с чем поиграть:

Имея на руках W-W можно использовать объекты PALETTE, создав их из юзермода и перезаписав через W-W мембер структуры cEntries, подробнее:

https://2017.zeronights.org/wp-content/uplo...20Evolution.pdf

Данный метод позволить используя описанную выше уязвимость в анхлабе читать и писать произвольные значения в км адреса. Однако сначала нужно придумать как обойти зависающую спин блокировку.

ahnlab-0day's People

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.