Недавно мы писали про набор обнаруженных уязвимостей в Windows, которые использовались атакующими в направленных атаках, до выхода соответствующего исправления со стороны Microsoft (0day). Одна из таких уязвимостей в компоненте OLE Package manager (Packager.dll) была добавлена нашими вирусными аналитиками в базы как Win32/Exploit.CVE-2014-4114.A. Атакующие могли эксплуатировать эту уязвимость через специальным образом подготовленный документ презентации PowerPoint со встроенным туда OLE-объектом.
В этом посте мы хотели бы остановиться на другой уязвимости из этого списка. Этой уязвимости был присвоен идентификатор CVE-2014-4113 и она присутствует в драйвере win32k.sys всех поддерживаемых версий Windows (2k3+). Уязвимость позволяет несанкционированно (в обход ограничений ОС) исполнять код режима ядра и повышать привилегии запускаемого эксплойтом приложения до максимально возможного уровня (SYSTEM). CVE-2014-4113 была закрыта обновлением MS14-058 в рамках октябрьского patch tuesday, а 64-битная версия эксплойта была добавлена нами в базы как Win64/Dianti.A.
Сам win32k.sys за прошедшие несколько лет был настоящим сокровищем для поиска уязвимостей типа Elevation Of Privilege (или Local Privelege Escalation, LPE). В нашем отчете за 2013 г. было отмечено, что этот компонент ОС занимает второе место по количеству исправленных для него уязвимостей за весь год. Его опережает только продукт Internet Explorer, который состоит из большого количества исполняемых файлов, в то время как в случае с win32k.sys файл только один.
Про сам win32k.sys уже было написано не один раз, он представляет из себя ту часть подсистемы Windows, которая работает в режиме ядра и отвечает за логику и механизмы, связанные с реализацией GUI-интерфейса Windows и ее взаимодействие с пользователем посредством устройств ввода. В далеком прошлом, когда Windows NT только превращалась в то, что история запомнила как Windows 2000 (NT 5.0), Windows XP (NT 5.1) и т. д., а сейчас уже и Windows 10 (NT 6.4), вся GUI-подсистема работала в пользовательском режиме и требовала больших накладных расходов на переключения контекстов процессов при проведении операций, связанных с выводом на экран, обработки сообщений клавиатуры, и т. д.
Затем в NT 4.0 для улучшения производительности и снижения накладных расходов на переключения контекста появился драйвер режима ядра win32k.sys. Логика работы GUI-подсистемы была разделена на ту часть, которая работает в режиме пользователя (известные библиотеки user32.dll и gdi32.dll) и часть режима ядра в виде драйвера win32k.sys. Возможно поэтому в win32k.sys кроется такое большое количество багов, код для драйвера мог местами копироваться из библиотек пользовательского режима. Кроме этого, функции драйвера, которые должны были возвращать указатели, были реализованы таким образом, что они также могли возвращать константы, т. е. значения, которые нельзя было интерпретировать как указатели.
Windows позволяет исполнять код режима ядра только через специальные механизмы (API), поскольку такому коду предоставляется полный доступ ко всем программным и аппаратным ресурсам ОС. Уязвимости win32k.sys так или иначе позволяют атакующим обходить эти ограничения и исполнять код режима ядра через эксплойт.
В случае с CVE-2014-4113 речь идет о том, что можно создать специальную системную структуру для win32k (win32k!tagwnd), спроецировать ее в режим ядра и заставить одну из функций драйвера вызвать callback из этой сформированной структуры. Сама уязвимость заключается в том, что одна из функций драйвера (xxxHandleMenuMessages) не проверяет возвращаемое ей другой функцией (xxxMNFindWindowFromPoint) значение (предполагаемый указатель), и передает его дальше другой функции (xxxSendMessage), которая оперирует неправильным значением. См. blog.trendmicro.com/trendlabs-security-intelligence/an-analysis-of-a-windows-kernel-mode-vulnerability-cve-2014-4113/.
32-битная версия
В случае с 32-битной версией эксплойта, он использует целочисленное переполнение аргумента, который передается в функцию xxxSendMessage из xxxHandleMenuMessages. Сам этот передаваемый аргумент может интерпретироваться функцией xxxHandleMenuMessages как указатель или как отрицательное значение в случае ошибки, которая возвращается функцией xxxMNFindWindowFromPoint. Из-за отсутствия проверки отрицательного значения (ошибки) в xxxHandleMenuMessages, эта функция передает это значение вместо указателя в xxxSendMessage.
В общем случае эксплойт применяет следующие шаги для эксплуатации.
- Используя функцию ntdll!ZwAllocateVirtualMemory выделяет страницу памяти по нулевому адресу.
- На этой странице создает поддельную структуру win32k!tagwnd, инициализируя указатель на callback-функцию эксплойта (шелл-код).
- Создает условия срабатывания уязвимости, т. е. передачи неправильного аргумента (0xfffffffb, -5) функции xxxSendMessage, что приводит к целочисленному переполнению при разыменовании указателя и обращению к памяти по нулевой странице. Т. е., при обращении функции xxxSendMessage к полю указателя на callback-функцию произойдет переполнение (0xfffffffb+0x60) предполагаемого адреса и в результате функция получит указатель на шелл-код.
- В результате эксплуатации управление получает шелл-код, который перезаписывает указатель на маркер доступа в структуре объекта ядра EPROCESS текущего процесса таким образом, что он будет указывать на маркер доступа системного процесса. Позднее это приведет к тому, что созданный эксплойтом процесс унаследует маркер доступа системного процесса и получит максимальные привилегии в системе.
Таким образом подготовка к эксплуатации сводится к выполнению следующей функции.
Рис. Подготовка к эксплуатации уязвимости на 32-битной системе. Для выделения адреса по нулевой странице используется уже ставший популярным вызов ZwAllocateVirtualMemory, которому в качестве адреса передается единица, что приводит к страничному выравниванию резервируемого адреса до нуля. Данная «возможность» была закрыта для Windows 7 обновлением MS13-031, а в Windows 8+ такое обновление включено по умолчанию. В начале функции производится вызов неэкспортируемой функции user32!PtiCurrent, которая возвращает указатель на структуру Win32ThreadInfo. Этот указатель будет использоваться для инициализации поля структуры win32k!_THRDESKHEAD.pti.
64-битная версия
В 64-битном виртуальном адресном пространстве вместо целочисленного переполнения, эксплойт использует выделение памяти по заданному адресу в памяти. Т. е. сама возвращаемая константа используется в качестве адреса при резервировании страницы памяти, которая будет заполнена функцией fnFillmalicioustagWnd (см. скриншот ниже).
Рис. Так выглядит структура tagWND на Windows 7 x64. Красным отмечен указатель на callback-функцию, которая будет указывать на шелл-код.
Рис. Непосредственно функция шелл-кода, которая выполняет подмену указателя на маркер доступа для EPROCESS текущего процесса.
Рис. Функция создания поддельной структуры tagWND. Для заполнения первого поля структуры используется полученный указатель (user32!PtiCurrent) на Win32ThreadInfo.
Рис. Часть функции fnPrepareExploitation. На x64 производится резервирование страницы памяти по указателю возвращаемой константы, т. е. 0xFFFFFFFB.
После окончания подготовительных мероприятий, эксплойт переходит на стадию эксплуатации, т. е. создает условия срабатывания уязвимости. Для этого используются различные манипуляция с элементами управления меню и установкой оконных ловушек, что в конечном итоге заставит уязвимую функцию win32k возвратить константу вместо указателя. После чего выполняется создание процесса функцией CreateProcessA.
Рис. В результате применения эксплойта, дочерний процесс командной строки унаследовал его системный маркер доступа.
Стоит отметить, что эксплуатация этой уязвимости не представляется возможной на Windows 8+ x32 & x64. В 32-разрядной версии защита от выделения памяти по нулевому указателю присутствует по умолчанию, а в 64-разрядной версии исполнение кода в режиме ядра из страницы в пользовательском режиме будет заблокировано механизмом SMEP.
Автор: esetnod32