
Подробный технический анализ и реализация эксплойта для CVE-2015-0057, уязвимости use-after-free в win32k.sys, охватывающей 32-битные и 64-битные системы Windows от XP до 8.1.
Автор: Aaron Adams
Перевод: 55-AA
Примечание переводчика: некоторые части статьи переведены вольно, при возникновении вопросов обращайтесь к оригиналу.
Термины:
В начале этого года я столкнулся с интересной уязвимостью в win32k.sys (CVE-2015-0057) и реализовал стабильный эксплойт для 32-битных и 64-битных систем, работающий от XP до Windows 8.1 (с некоторыми исключениями). В этой статье подробно описано, как я это сделал на обеих платформах, а в конце добавлены некоторые дополнительные материалы. Также описано использование с низким уровнем привилегий (low integrity) на Windows 8.1 с включенным SMEP.
Статья длинная, я постарался предоставить как можно больше деталей, чтобы показать сложность эксплуатации этой уязвимости, а не скрывать их. Конечно, некоторые детали я опустил. Надеюсь, эти подробности окажутся полезными.
10 февраля 2015 года Microsoft опубликовала подробности MS15-010. Эта ошибка была впервые обнаружена Уди Яво (Udi Yavo) из enSilo. Уди дал отличный анализ в своём блоге breaking malware: "one bit rule-bypassing windows 10 protections using single bit". Рекомендую внимательно прочитать эту статью для глубокого понимания ошибки, хотя в этой статье я приведу как можно больше деталей, касающихся препятствий, которые необходимо преодолеть при срабатывании уязвимости. Эксплуатация этой уязвимости очень интересна, многие детали взяты из блога Уди, ниже его заявление:
Разумное раскрытие: хотя этот блог технический, мы не раскрываем никакого кода и полных деталей, чтобы предотвратить воспроизведение эксплойта техническими специалистами.
В качестве дополнительного бонуса за использование этой уязвимости мы получили эволюцию покемона: Технический эльф. Думаю, я должен отдать должное Уди за обнаружение этой ошибки, публикацию соответствующих сведений в блоге и деталей эксплуатации – это было невероятно полезно.
Раньше я никогда не эксплуатировал уязвимости win32k.sys и не был знаком с пользовательскими обратными вызовами (user-mode callbacks) и многими связанными API, поэтому я также благодарю известных исследователей безопасности, которые предоставили в открытом доступе новые ресурсы, таких как Skywing, Tarjei Mandt, Alex Ionescu и j00ru. Эти люди заслуживают похвалы за предоставление такого количества технической информации. Я активно использовал статью Tarjei Mandt Win32k.sys exploitation paper.
Во время написания этого эксплойта один отличный reverse-инженер реализовал стабильный эксплойт для CVE-2015-1701, где пример кода пользовательского обратного вызова был очень полезен. Спасибо автору.
Стоит отметить, что мой анализ проводился на Windows 7, так как это, похоже, единственная версия, где все структуры в win32k.sys имеют соответствующие символы. Большинство этих символов можно использовать для структур в других версиях Win32k.sys. По непонятным причинам Microsoft удалила эти символы из Windows 8.
Наконец, хочу сказать, что мой метод эксплуатации довольно сложен. Вполне возможно, что существует более простой способ, который я не нашёл. Я был бы рад услышать о других подходах. В любом случае, надеюсь, всё это будет полезно для изучения уязвимостей win32k.sys.
Ниже в дизассемблере win32k!xxxEnableWndSBArrows мы видим, как выглядит эта ошибка – довольно изящная:
Неисправленная версия:
.text:FFFFF97FFF1B157D mov r8d, r13d
.text:FFFFF97FFF1B1580 mov rdx, r14
.text:FFFFF97FFF1B1583 call xxxDrawScrollBar ; вызывает usermode callback
.text:FFFFF97FFF1B1588 jmp short loc_FFFFF97FFF1B1519
[...]
.text:FFFFF97FFF1B1519 mov eax, [rbx] ; обращение к указателю tagSBINFO без проверки
.text:FFFFF97FFF1B151B mov ebp, 0FFFFFFFBh
.text:FFFFF97FFF1B1520 xor eax, esi
В приведённом выше коде Win32K!xxxdrawscrollbar может при определённых условиях вызвать callback в пользовательское пространство. В пользовательском коде указатель tagSBINFO может быть освобождён атакующим. При возврате к приведённому выше коду, инструкция по адресу 0xFFFFF97FFF1B1519 будет обращаться к недействительному указателю.
Исправленная версия:
.text:FFFFF97FFF1D69C3 xor r8d, r8d
.text:FFFFF97FFF1D69C6 mov rdx, rbp
.text:FFFFF97FFF1D69C9 call xxxDrawScrollBar ; вызывает usermode callback
.text:FFFFF97FFF1D69CE cmp rbx, [rdi+0B0h] ; проверка, корректен ли указатель tagSBINFO
---.text:FFFFF97FFF1D69D5 jz short loc_FFFFF97FFF1D69E4; если корректен, продолжаем обычный поток
| .text:FFFFF97FFF1D69D7 mov rcx, rbp
| .text:FFFFF97FFF1D69DA call _ReleaseDC
| .text:FFFFF97FFF1D69DF jmp loc_FFFFF97FFF1D6958 ; переход на выход из функции
|->.text:FFFFF97FFF1D69E4 mov eax, [rbx] ; безопасно используем корректный указатель tagSBINFO
.text:FFFFF97FFF1D69E6 xor eax, r14d
В исправленной версии мы видим проверку указателя tagSBINFO перед использованием. Соответствующая информация о структуре будет приведена позже.
При реализации этого эксплойта было выполнено несколько повреждений (corruptions). Уязвимость срабатывает в одном из них.
Техническая первопричина этой ошибки – Use-After-Free (UAF) в куче десктопа (Desktop Heap). Сначала это меня смутило, так как я не был знаком с механизмом пользовательских обратных вызовов win32k.sys и не понимал, как он работает. Я предположил, что это состояние гонки (race condition) с блокировкой, приводящее к UAF. На самом деле блокировка той структуры использовалась правильно, и поток выполнения соответствовал ожиданиям. Короче говоря, реальная причина такова:
Всё. Если не учитывать пользовательские обратные вызовы, этот этап довольно прост.
Но как именно мы производим повреждение и зачем? Как упоминалось в блоге Уди, вы можете установить или сбросить 2 бита в определённом месте, которое системный код считает полем WSBflags в структуре tagSBINFO. Это нестандартный подход к UAF, но в статье была подсказка, как это сделать, что я объясню в следующих разделах. Сначала разберёмся, как можно управлять этими битами.
Структура tagSBINFO (одинакова для 32 и 64 бит):