
(1) IQVW32.sys до версии 1.3.1.0 и (2) IQVW64.sys до версии 1.3.1.0 в драйвере диагностики Ethernet от Intel для Windows позволяют локальным пользователям вызвать отказ в обслуживании или, возможно, выполнить произвольный код с привилегиями ядра через специально сформированные IOCTL-вызовы (a) 0x80862013, (b) 0x8086200B, (c) 0x8086200F или (d) 0x80862007.
(1) IQVW32.sys до версии 1.3.1.0 и (2) IQVW64.sys до версии 1.3.1.0 в драйвере диагностики Ethernet для Windows от Intel позволяют локальным пользователям вызвать отказ в обслуживании или, возможно, выполнить произвольный код с привилегиями ядра через специально сформированный вызов IOCTL (a) 0x80862013, (b) 0x8086200B, (c) 0x8086200F или (d) 0x80862007.
Этот репозиторий содержит описание данной уязвимости, а также эксплойты proof-of-concept, работающие на 64-битных Windows 7 SP1 и Windows 10 20H2. Файл драйвера можно найти в каталоге Driver Files. Если вы обнаружите опечатки в описании/статье или захотите увидеть более подробное описание некоторых деталей, создайте тикет в репозитории! Я исправлю их как можно скорее.
Мотивация написания эксплойта именно для этого драйвера устройства заключается исключительно в том, что в настоящее время он используется в реальных атаках для загрузки неподписанного руткита злоумышленника. Используя метод BYOVD (Bring Your Own Vulnerable Driver), вредоносное ПО может проверить, работает ли оно с повышенными привилегиями, сбросить копию уязвимого драйвера устройства, загрузить драйвер, а затем использовать его для получения выполнения кода в режиме ядра для загрузки руткита. Мне не удалось успешно реверс-инжинирить образец вредоносного ПО, поэтому я взял на себя задачу создать эксплойт.
Образцы, обнаруженные в реальных атаках: https://bazaar.abuse.ch/sample/84ed7fec67de5621806dbb43af5167a5fc60ab7f2403448519dc0eca2b8f9022/ https://bazaar.abuse.ch/sample/0925b8985b19d7925d68186d666b0050a4cb3f2a577d64765d770a57a2eab9ae/ https://bazaar.abuse.ch/sample/e8b7f42d544fe8b954c4021315cff2fdd44d67d11704009cdf3037d34e0c0a93/
Драйвер устройства, а именно iqvw64e.sys, предназначен для выполнения диагностики сетевых адаптеров. Он позволяет компоненту пользовательского режима взаимодействовать с драйвером устройства для выполнения множества процедур ядра, предоставляя несколько кодов управления вводом-выводом (также известных как IOCTL), при этом «подчиненный» код управления вводом-выводом передается во входном буфере пользователя во время взаимодействия. Код управления вводом-выводом, который будет использоваться для достижения уязвимого кодового пути, — 0x80862007. Помимо основного кода управления, в этом анализе будут рассмотрены упомянутые «подчиненные» коды управления вводом-выводом: код 0x33 для вызова функции memmove и код 0x30 для вызова функции memset по соответствующим путям кода. Это описание не будет охватывать детали процедуры DriverEntry, так как на странице документации Microsoft есть достаточно информации для подробного объяснения.
Для начала мы хотим узнать, как вообще можно взаимодействовать с этим конкретным драйвером устройства. Наиболее распространенный способ связи с драйвером устройства — использование функции с именем DeviceIoControl. Общая идея этой функции заключается в том, что мы можем передать действительный дескриптор драйвера, созданный с помощью CreateFileA, передать код управления вводом-выводом, соответствующий нужной нам процедуре ядра, передать структуру (или буфер), которую она ожидает, и она вернет данные в наш выходной буфер. Хотя такие процедуры иногда могут быть необходимы (например, для доступа к регистрам, специфичным для модели в целях разгона), они также представляют серьезную угрозу безопасности. Но... как?
В случае CVE-2015-2291 уязвимость может быть запущена непривилегированным пользователем. Из-за отсутствия проверок санитизации и того, что для эксплуатации уязвимости не требуются права администратора, это представляет угрозу безопасности. Под этими двумя недостатками скрывается возможность полного управления вызовами функций memset и memmove, предоставляемыми интерфейсом кодов управления вводом-выводом. Помните упомянутую ранее функцию DeviceIoControl, как мы можем передать структуру, которая будет использоваться в процедуре ядра? Вот как это всё сходится воедино.
Давайте сделаем шаг назад. Сначала мы хотим получить дескриптор драйвера, связанный с уязвимым драйвером устройства. Однако даже до этого нам нужно найти соответствующий именованный объект устройства. Они предоставляются пользовательскому пространству через символическую ссылку (обычно жестко заданную), которую можно найти с помощью WinObj, часть набора [SysInternals]. Хотя мы могли бы использовать утилиту дампа строк для извлечения символической ссылки или альтернативно выполнить реверс-инжиниринг драйвера устройства, я просто загрузил драйвер устройства и нашел его с помощью WinObj. Найденная символическая ссылка, относящаяся к драйверу устройства, — \\.\GLOBALROOT\Device\Nal. Чтобы получить дескриптор драйвера, нам нужно вызвать функцию CreateFileA и получить от нее действительный дескриптор драйвера для использования позже в процессе. Код для этого процесса выглядит следующим образом:```C
if (h_nal == (HANDLE)-1)
{
printf("\n[-] Unable to obtain a driver handle to the Nal device driver. Error: %d (0x%x)", GetLastError(), GetLastError());
unused = getchar();
return 1;
}
printf("\n[+] Obtained a driver handle to the Nal device driver. Handle Value: 0x%p", h_nal);
Мы будем использовать дескриптор драйвера позже в процессе эксплуатации. А пока начнем подготовку нашего эксплойта. Следующим шагом будет загрузка библиотеки `ntdll.dll` с помощью функции [LoadLibraryA](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya) для получения [дескриптора модуля](https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types), чтобы мы могли динамически найти необходимые функции. Хотя библиотека `ntdll.dll` уже может быть загружена в наш процесс, нам всё равно необходимо получить дескриптор этой библиотеки, который мы сможем использовать. Функции, которые нам нужны для эксплуатации: [NtQuerySystemInformation](https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation) для утечки базового адреса NT-ядра (со средним уровнем целостности процесса) для использования на более поздних этапах эксплуатации, и функция [NtQueryIntervalProfile](http://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FProfile%2FNtQueryIntervalProfile.html) для запуска уязвимости. Что касается кода для загрузки библиотеки `ntdll.dll`, он выглядит следующим образом:```C
h_ntdll = LoadLibraryA("C:\\Windows\\System32\\ntdll.dll");
if (!h_ntdll)
{
printf("\n[-] Failed to load the \"ntdll.dll\" API library. Error: %d (0x%x)", GetLastError(), GetLastError());
unused = getchar();
return 0;
}
printf("\n[+] Loaded the \"ntdll.dll\" API library. Handle Value: 0x%p", h_ntdll);
Теперь, когда мы получили дескриптор библиотеки, мы начнем с поиска функции NtQueryIntervalProfile. Для начала нам потребуется определение типа для этой функции, так как она недокументирована. Хотя вы можете найти определение типа в интернете, я привел его здесь для упрощения доступа:```C
typedef unsigned int(__stdcall* NtQueryIntervalProfile)(
unsigned int ProfileSource,
PULONG Interval
);