Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2019-0708 — Концепт-доказательство для CVE-2019-0708 (BlueKeep), позволяющее выполнить RCE без аутентификации на Windows7. | Kitploit
Инструменты/GitHubGitHub/ricseclab/cve-2019-0708
Анализ уязвимостейЭксплуатацияТестирование на ПроникновениеИнструмент Удаленного ДоступаРазработка Полезной НагрузкиЭксплуатация Бинарных Файлов
GitHubricseclab/cve-2019-0708

CVE-2019-0708

Концепт-доказательство для CVE-2019-0708 (BlueKeep), позволяющее выполнить RCE без аутентификации на Windows7.

Репозиторий
150234 лет назадПроверено Kitploit

Популярное

Смотреть все →

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

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2019-0708 (BlueKeep) предварительная аутентификация удаленное выполнение кода (RCE) POC на Windows 7

Ricerca Security, Inc.

Этот репозиторий демонстрирует ошибку удаленного выполнения кода в службах удаленных рабочих столов Windows (RDS).

Здесь представлен код POC и технический отчет об уязвимости BlueKeep, который мы разработали ранее.
Примечание: Наша цель — помочь аналитикам лучше понять критические уязвимости.

Как использовать

Предварительные требования

Наш эксплойт написан на Python 3 и использует библиотеку PyRDP.
Пожалуйста, настройте их в соответствии с руководством по установке PyRDP.

Использование

В настоящее время наш эксплойт нацелен и протестирован на Windows 7 SP 1 (6.1.7601) x64 в Virtual Box.

Если ваш компьютер имеет IP-адрес 192.168.56.1 и вы атакуете RDP-сервер по адресу example.com:1234, введите:

root@kitploit:~
$ python exploit.py example.com -rp 1234 192.168.56.1

Если скрипт успешно эксплуатирует сервер, connect-back shellcode инициирует TCP-соединение от сервера обратно к 192.168.56.1:4444. Поэтому, например, вы должны ожидать соединения с помощью netcat:

root@kitploit:~
$ nc -v -l 4444

Если вы хотите изменить номер порта, к которому сервер подключается обратно, используйте опцию -bp:

root@kitploit:~
$ python exploit.py example.com -rp 1234 192.168.56.1 -bp 4567

Отчет

Уязвимость

В мае 2019 года Microsoft раскрыла критическую уязвимость удаленного выполнения кода CVE-2019-0708 в службах удаленных рабочих столов (ранее известных как терминальные службы). Эта уязвимость является предварительной аутентификацией — это означает, что уязвимость может распространяться как червь и способна вызвать массовые сбои.
Злоумышленник может использовать эту уязвимость, отправляя специально созданные сообщения протокола удаленного рабочего стола (RDP) на целевой сервер и получая произвольное выполнение кода с правами администратора.

Виртуальные каналы RDP

Службы удаленных рабочих столов Microsoft предоставляют пользователю возможность удаленно открывать интерактивные сеансы Windows. Они отображают рабочий стол пользователя Windows, взаимодействуя с клиентом с помощью протокола удаленного рабочего стола (RDP) через порт 3389/TCP.

Протокол RDP может быть расширен с помощью программных расширений, называемых виртуальными каналами. Примеры функциональных улучшений могут включать: поддержку специальных типов оборудования, аудио или других дополнений к основной функциональности.
Эти каналы включают стандартные каналы Microsoft, такие как "rdpdr" (перенаправление), "rdpsnd" (звук), "cliprdr" (общий буфер обмена) и т.д. Пользователи могут писать модули с использованием API RDP для поддержки других каналов. В дополнение к вышеупомянутым каналам Microsoft создает два канала по умолчанию: MS_T120 (используется для самого RDP) и CTXTW (используется в Citrix ICA).

Уязвимость связана с процессом привязки виртуальных каналов MS_T120 через запрос "MCS Connect Initial и GCC Create". Дополнительная информация доступна в ZDI.
Как упоминалось в статье ZDI, все виртуальные каналы, запрошенные клиентом, создаются с помощью termdd!IcaCreateChannel(). Затем указатели на эти структуры каналов хранятся в таблице, которую мы будем называть ChannelPointerTable.
При установке соединения с RDP-клиентом все статические виртуальные каналы, включая MS_T120, инициализируются внутренне сервером RDP Windows и указываются ChannelPointerTable.

Запрос на создание MS_T120 и CTXTW выполняется rdpcore!WDLIB_IcaVirtualQueryBindings().

Рис. 1: Формирование запроса на создание MS_T120 и CTXTW

После передачи запроса в termdd!IcaBindVirtualChannels() структура виртуального канала создается в termdd!IcaAllocateChannel() и регистрируется в ChannelPointerTable.

Рис. 2: Создание и регистрация структуры виртуального канала

Функция termdd!IcaBindChannel() отвечает за регистрацию структуры виртуального канала в ChannelPointerTable. IcaBindChannel
Вот стек вызовов на Windows 7 x64, когда termdd!IcaBindChannel() вызывается с первым аргументом "MS_T120" и третьим аргументом 0x1f.

Рис. 3: MS_T120 привязывается к слоту 0x1f во время начального запроса

Тогда ChannelPointerTable выглядит следующим образом. Обратите внимание, что MS_T120 всегда присутствует в Слоте 0x1F.

Рис. 4: ChannelPointerTable во время начального запроса

Анализ первопричины

Уязвимость типа use-after-free существует в драйвере ядра RDP Windows, termdd.sys.
Проблема в том, что когда клиент указывает канал с именем MS_T120\x00 во время "MCS Connect Initial и GCC Create", termdd!IcaCreateChannel() вызывает termdd!IcaFindChannelByName() и возвращает существующую структуру канала MS_T120 в слоте 0x1F. Затем эта структура канала считается новой записью виртуального канала и сохраняется в другом слоте (в данном примере, Слот 2) во время "MCS Attach User Request".
Вот стек вызовов на Windows 7 x64, когда termdd!IcaBindChannel() вызывается с первым аргументом "MS_T120" и третьим аргументом 0x2.

Рис. 5: MS_T120 также привязывается к слоту 0x2 во время запроса присоединения

Другими словами, структура канала MS_T120 указывается двумя слотами 0x1F и 0x2.

Рис. 6: ChannelPointerTable во время запроса присоединения

Если злоумышленник затем отправляет недопустимые данные в канал MS_T120, termdd.sys закрывает канал с помощью termdd!IcaCloseChannel() и очищает указатель в слоте (в данном примере Слот 2).
Однако тот же указатель в слоте 0x1F не очищается.
Впоследствии, при завершении соединения, вызывается RDPWD!HandleDisconnectProviderUlt(), который, в свою очередь, вызывает termdd!IcaChannelInputInternal() и снова пытается уничтожить освобожденную структуру канала MS_T120, используя указатель в слоте 0x1F. Процедура уничтожения вызывается через указатель vtable внутри структуры канала. Это приводит к состоянию use-after-free.

Рис. 7: Разыменование vtable

Heap Spraying

Как объяснялось в предыдущем разделе, RDPWD!HandleDisconnectProviderUlt() пытается вызвать функцию через указатель vtable в освобожденной структуре канала. Если злоумышленник может контролировать значения в структуре канала, он может перезаписать указатель vtable, что приведет к произвольному выполнению кода с привилегиями ядра.
Однако для этого необходимо преодолеть две трудности.

Первая — как изначально контролировать значения в освобожденной структуре канала. Для этого типично и надежно для злоумышленника выделить память в том же месте, где находится освобожденная структура, поскольку целевая уязвимость — use-after-free.
Однако в данном случае у него нет детерминированного способа выделить свою память в целевом месте по своему желанию.
Это происходит потому, что в ядре одновременно работают множество потоков и выделяют память (фактически одновременно). Где будет выделена его память, зависит от порядка выполнения потоков. Почти во всех случаях он не может быть уверен, удалось ли ему успешное выделение.
Изменение размера кучи
Изменение размера кучи

Вторая — куда установить адреса vtable и указателей в нем. Как мы видели в предыдущем разделе, злоумышленник должен установить адрес vtable. Поскольку он хочет получить произвольное выполнение кода, он должен установить такой адрес, чтобы поддельный vtable содержал адрес, который он хочет выполнить (например, адрес шеллкода или какого-то гаджета). Однако почти наверняка он не может узнать такой подходящий адрес из-за вышеупомянутой случайности кучи ядра и KASLR:

  1. Вероятно, он может выделить память в куче ядра и записать адрес шеллкода в выделенное место. Тем не менее, обычно он не может узнать адрес выделенного места из-за случайности, как отмечено выше.
  2. Маловероятно, но другой вариант — использовать статические (не heap) области памяти, которые случайно содержат адрес полезных гаджетов, например, где-то в секции кода. Однако этот план также не сработает, потому что Windows 7 имеет защиту KASLR, которая рандомизирует адреса этих областей памяти.

Эти факты означают, что злоумышленник не может напрямую получить произвольное выполнение кода, даже если он может контролировать указатель vtable, если только он не использует другую уязвимость, которая утекает адреса в ядре. Более того, как вы можете заметить, "адрес шеллкода или какого-то гаджета" также то, что злоумышленник не может узнать.

Наш эксплойт справляется с этими препятствиями с помощью единственной техники: heap spraying. Heap spraying — это метод, нарушающий эту случайность, путем большого количества выделений большого объема памяти.

Рис. 8: Использование пула кучи до spraying

Рис. 9: Использование пула кучи после spraying

Повторяя специально созданные выделения множество раз, злоумышленник может увеличить вероятность того, что какая-то из выделенных областей окажется в расположении освобожденной структуры канала.

Если большинство объектов в куче ядра — это объекты, подготовленные злоумышленником, то он может даже небрежно указать некоторый адрес в куче как адрес поддельного vtable, потому что указанный адрес с высокой вероятностью будет указывать на его объекты. Отметим, что базовый адрес кучи ядра не рандомизируется KASLR. В основном случайность кучи возникает только из порядка выполнения потоков.

К счастью и, что наиболее важно, в Windows 7 бит NX не включен в нестраничном пуле ядра. Это означает, что злоумышленник может хранить в куче ядра не только поддельный vtable, но и непосредственно шеллкод. Это значительно упрощает эксплуатацию, поскольку нам не нужно использовать return-oriented programming.

Рис. 10: Разрешения записи таблицы страниц (PTE)

Для heap spraying злоумышленнику, очевидно, нужна функциональность, позволяющая выделять память в куче ядра и вводить в нее данные. Основываясь на отчете Unit 42 и эксплойте BlueKeep в Metasploit, мы искали драйверы ядра, которые предоставляют такую функциональность. Мы протестировали множество PDU и в итоге пришли к выводу, что наиболее надежный и полезный способ — отправлять PDU виртуального канала на канал rdpsnd, как это делает эксплойт Metasploit. Для справки, объясним, почему мы не смогли применить три типа PDU, представленные в отчете Unit 42:

  • PDU кэша Bitmap: во-первых, злоумышленник может отправить этот PDU только во время первого рукопожатия. Поскольку use-after-free происходит после завершения рукопожатия, его нельзя использовать для перезаписи vtable. Более того, с помощью этого PDU злоумышленник может выделить только 0x2b5240 байт (< 3MB), что недостаточно для heap spraying.
  • PDU запроса имени клиента: мы думали, что этот PDU перспективен для heap spraying. Однако, насколько мы тестировали, этот PDU нельзя отправить (или получить) несколько раз, по крайней мере, простым способом. Из-за нехватки деталей мы не смогли выяснить, что означает этот результат: что злоумышленнику нужно отправлять специально созданные сложные пакеты для использования этого PDU, или что эта процедура изменилась и не работает в 64-битной среде.
  • PDU обновления прямоугольника (Refresh Rect): этот PDU эффективен для heap spraying в том смысле, что злоумышленник может выделить гораздо больший объем памяти, чем размер данных, которые он фактически отправляет. Однако, поскольку злоумышленник может контролировать только 8 байт данных в памяти, выделенной этим PDU, сложно осмысленно использовать это выделение. Мы опускаем детали, но считаем, что для эффективного использования такого PDU злоумышленник должен иметь возможность контролировать как минимум 13 байт (8 байт для vtable и 5 байт для «jmp $+0x1000») данных.

PDU виртуального канала, как следует из названия, обменивается между клиентом и сервером для передачи данных статическим виртуальным каналам. Что касается того, как обрабатываются данные внутри PDU, это зависит от канала. Среди нескольких известных каналов, которые Microsoft предоставляет в качестве расширений, канал rdpsnd имеет уникальную особенность: он принимает любые входные данные и выделяет для них память. Поскольку этот канал используется по умолчанию в Windows 7, мы можем просто отправлять наши полезные нагрузки на него для heap spraying.

Мы написали proof of concept с учетом вышеупомянутых важных моментов и успешно достигли произвольного выполнения кода.

Рис. 11: Управляемый адрес vtable

Рис. 12: Успешно перезаписан адрес vtable вредоносным, указывающим на шеллкод (ud2)

Выполнение кода

Хотя мы описали, как выполнить шеллкод в предыдущем разделе, на самом деле это еще не все. Шеллкод выполняется в пространстве ядра, в то время как злоумышленнику нужны административные привилегии в "пользовательском режиме". Теоретически они похожи по тому, что можно с ними сделать, но различаются по тому, как можно реализовать желаемые действия. Например, с помощью шеллкода злоумышленнику может потребоваться написать сотни строк ассемблерного кода для вывода списка файлов в каком-то каталоге, тогда как с привилегированной оболочкой он может просто ввести «dir».

Таким образом, цель нашего эксплойта — предоставить злоумышленнику привилегированную оболочку, и для этого требуется еще несколько усилий. Поскольку шеллкод выполняется в пространстве ядра, сначала шеллкод должен найти или создать (привилегированный) поток пользовательского режима, а затем выполнить cmd.exe в этом потоке. На этот раз нам нужно рассмотреть два вопроса: как найти или создать поток и как выделить память в пользовательском режиме для выполнения шеллкода в пользовательском режиме.

Первый вопрос о поиске потока пользовательского режима возникает из-за того, что контекст, в котором выполняется шеллкод, не является обычным контекстом процесса. Если он выполняется в контексте процесса, он может просто использовать инструкцию IRET для возврата в пользовательский режим. Однако в данном случае выполнение IRET приводит к зависанию ядра. Существует несколько способов решить эту проблему, но среди них наиболее общим и полезным методом является асинхронный вызов процедуры (APC) — механизм, предоставляемый Windows для обработки асинхронных событий. APC позволяет программе выполнять функции в контексте указанного потока даже другого процесса. С помощью этого механизма шеллкод может легко и легально создать новый поток пользовательского режима.

При регистрации APC необходимо указать адрес, с которого новый поток пользовательского режима начнет выполнение. Однако до сих пор мы выделяли память только в куче ядра, к которой поток пользовательского режима явно не может получить доступ. Чтобы выполнить шеллкод пользовательского режима, мы должны подготовить другое место в памяти, видимое из пользовательского режима, и сохранить там шеллкод. Таким образом, мы сталкиваемся со вторым вопросом: выделение памяти в пользовательском режиме. Один из возможных и обычных способов решения этой проблемы — создание нового отображения с помощью ZwAllocateVirtualMemory. Однако это немного избыточно, и на самом деле в Windows 7 есть более простой способ: использование KUSER_SHARED_DATA. KUSER_SHARED_DATA — это структура данных, хранящаяся в выделенном отображении, которое отображается как в пользовательском, так и в режиме ядра и находится по фиксированному адресу (0x7FFE0000 и 0xFFFFF78000000000 соответственно). Это функция, аналогичная vsyscall в Linux. Если мы сохраним шеллкод пользовательского режима в этом отображении, все будет работать: шеллкод режима ядра может скопировать шеллкод пользовательского режима в это отображение и зарегистрировать APC без труда, поскольку он знает адрес отображения.

Рис. 13: Шеллкод хранится в выделенном отображении, 0x7FFE0000 (пользовательский режим) и 0xFFFFF78000000000 (режим ядра)

Рис. 14: Тело шеллкода

Таким образом, после получения произвольного выполнения кода нужно сделать много всего, хотя все эти вещи могут быть решены почти прямолинейно. В итоге наш эксплойт достиг своей цели.

Затронутые версии

Эта уязвимость получила номер CVE CVE-2019-0708. Microsoft уже выпустила патч безопасности KB4499175 15 мая 2019 года.
Более подробную информацию об уязвимости, затронутых версиях и смягчении последствий можно найти здесь.

Благодарности

Этот проект был частично поддержан Advanced Technology Lab, компанией Recruit Co.,Ltd. Advanced Technology Lab

Скачать инструмент