
Доказательство концепции для CVE-2021-28476 — произвольное разыменование указателя в Hyper-V vmswitch.sys, позволяющее гостевой системе вызвать отказ в обслуживании хост-системы и потенциально выполнить произвольный код.
Гостевая виртуальная машина Hyper-V может вызвать состояние отказа в обслуживании (Denial-of-Service) на своём хост-компьютере из-за небезопасного разбора входных данных функцией VmsIfrInfoParams_OID_SWITCH_NIC_REQUEST в vmswitch.sys. Сбой вызван попыткой функции разыменовать управляемое гостем значение как адрес виртуальной памяти хоста. Эта ошибка может быть использована для получения RCE при некоторых условиях, а именно когда запрос передаётся нижележащему аппаратному устройству с указателем на данные, контролируемые атакующим, однако мне неизвестно о публичной демонстрации этой техники.
11 мая 2021 г.
https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-28476
Произвольное разыменование (Arbitrary Dereference)
Обратная разработка/аудит кода
Эта ошибка была найдена во время моего первоначального изучения драйвера vmswitch. При просмотре документации на MSDN было отмечено, что структура NDIS_SWITCH_NIC_OID_REQUEST содержит указатель, и я вспомнил, что видел ссылку на имя этой структуры в имени функции. Была проведена некоторая обратная разработка, чтобы понять, как может работать эта функция, учитывая, что гость не должен иметь никакого представления о виртуальном адресе хоста, но реальных проверок безопасности не было. Затем модифицированный драйвер Linux Hyper-V был использован для отправки поддельного запроса на хост, что немедленно вызвало сбой.
Драйвер vmswitch.sys содержит ряд функций для обработки RNDIS OID-запросов, включая некоторые, специально предназначенные для регистрации входящих сообщений. Одна из них, VmsIfrInfoParams_OID_SWITCH_NIC_REQUEST, обрабатывает запросы типа OID_SWITCH_NIC_REQUEST. Согласно документации MSDN, этот запрос предназначен для разрешения пересылки OID-запросов с гостевой машины на нижележащий физический сетевой адаптер через внешний сетевой адаптер расширяемого коммутатора Hyper-V.
OID-запросы используют хорошо определённую, общую структуру сообщений для своего внешнего уровня, которая включает "InformationBuffer", предназначенный для хранения данных, специфичных для типа OID-сообщения. В случае OID_SWITCH_NIC_REQUEST, InformationBuffer имеет следующее определение (обратите внимание, что оно немного отличается от документированной структуры, но соответствует тому, что функционально присутствовало в коде):
typedef struct _NDIS_SWITCH_NIC_OID_REQUEST {
NDIS_OBJECT_HEADER Header; //структура размером u32
u32 Flags;
NDIS_SWITCH_PORT_ID SourcePortId; //тип u32
NDIS_SWITCH_PORT_NIC SourceNicIndex; //тип u32
NDIS_SWITCH_PORT_ID DestinationPortId; //тип u32
NDIS_SWITCH_PORT_NIC DestinationNicIndex; //тип u32
PNDIS_OID_REQUEST OidRequest; //**должен** быть указателем на пересылаемую структуру NDIS_OID_REQUEST
} NDIS_SWITCH_NIC_OID_REQUEST, *PNDIS_SWITCH_NIC_OID_REQUEST;
В рамках начального разбора этого объекта, VmsIfrInfoParams_OID_SWITCH_NIC_REQUEST попытается распаковать пересылаемый запрос NDIS_OID_REQUEST из инкапсулирующего _NDIS_SWITCH_NIC_OID_REQUEST. Однако, если гость Hyper-V предоставит недопустимое расположение памяти в качестве значения OidRequest, разыменование вызовет чтение за пределами границ и приведёт к сбою ядра корневого раздела.
Псевдокод затронутой функции выглядит следующим образом:
void VmsIfrInfoParams_OID_SWITCH_NIC_REQUEST(void* unknown1, u64 unknown2, _NDIS_SWITCH_NIC_OID_REQUEST* request, u32 buffer_size) {
NDIS_OID_REQUEST* forwarded_request;
if((unknown1 != 0) && (buffer_size != 0)) {
if(buffer_size > 0x20) {
forwarded_request = (NDIS_OID_REQUEST*)request->OidRequest; //разыменование и сбой происходят здесь
}
}
...
//обрезано
...
return;
}