
PoC для CVE-2024-38063 (RCE в tcpip.sys)
Это (довольно нестабильный) PoC для CVE-2024-38063, RCE в tcpip.sys, исправленной 13 августа 2024 года. Я не нашел и не сообщал об этой уязвимости, это сделал Wei.
pip3 install scapy
Измените поля в скрипте:
iface <- Если у вас несколько адаптеров, выберите, какой использовать для отправки пакетов. Например, "eth0" в Linux или "Hyper-V Virtual Ethernet Adapter" в Windows. Если собираетесь использовать интерфейс по умолчанию, оставьте пустым.ip_addr <- IP-адрес целевой системы (IPv6)num_tries & num_batches <- Сколько различных пачек пакетов отправлять. Больше = больше повреждений кучи + выше вероятность срабатывания уязвимости.mac_addr <- Оставьте пустым, если только scapy не жалуется, что не может найти MAC-адрес. См. раздел устранения неполадок ниже.Запустите скрипт:
python3 cve-2024-38063.py
Самый простой способ воспроизвести уязвимость — использовать bcdedit /set debug on на целевой системе и перезагрузить машину/ВМ. Это делает драйвером сетевого адаптера по умолчанию kdnic.sys, который очень охотно объединяет пакеты. Если вы пытаетесь воспроизвести уязвимость на другой конфигурации, вам нужно добиться, чтобы система объединяла отправленные вами пакеты. Подробнее читайте в разделе устранения неполадок ниже.
Вы можете прочитать этот отличный анализ уязвимости от Marcus, если интересуетесь техническими деталями. Детали, написанные ниже, предназначены для краткого обзора, а не для серьезного технического анализа.
NET_BUFFER, который содержит буферизованные данные пакета. По смещению 0x30 также находится поле текущего смещения, указывающее, насколько далеко был разобран пакет. На этом этапе значение смещения обычно равно 0x28, что означает, что заголовок IPv6 разобран, но больше ничего.tcpip!Ipv6pReceiveDestinationOptions ошибка разбора приводит к вызову tcpip!IppSendErrorList. Эта функция вызывает tcpip!IppSendError для каждого объекта пакета в связном списке (начиная с текущего).tcpip!IppSendError имеет побочные эффекты. Он «откатывает» буферизованные данные пакета к началу и сбрасывает поле текущего смещения в ноль.0x8C). Это означает, что драйвер продолжит разбирать заголовки расширений других пакетов в связном списке, даже если они были «откачены» в IppSendError.0x28.Ipv6pReceiveFragment. Функция разбирает заголовок расширения фрагмента и предполагает, что поле смещения пакета будет не менее 0x28 при вычислении длины данных без заголовков в пакете путем вычитания 0x30 из текущего значения смещения. Затем это значение сохраняется в объекте сборки, предназначенном для сборки фрагментированного пакета.IppSendError. Значение смещения будет равно нулю и увеличено до 8 где-то раньше в Ipv6pReceiveFragment. При вычислении размера данных без заголовков произойдет переполнение вниз, и значение станет равно 0xffd8 (вычитание выполняется в 16 битах).Ipv6pReassembleDatagram, где оно используется для вычисления длины выходного буфера собранного пакета. Однако все вычисления выполняются в 32 битах, и есть проверка на то, что общая длина не превышает 0xFFFF, что и происходит в этом случае.Ipv6pReassemblyTimeout, где оно также используется таким же образом. Однако здесь вычисления выполняются в 16 битах и происходит целочисленное переполнение. Это приводит к переполнению буфера при последующем копировании данных в буфер.Чтобы вызвать Ipv6pReassemblyTimeout, отправитель фрагмента должен быть неактивен в течение 1 минуты. Тогда наша стратегия такова:
IppSendError, а затем фрагментированный пакетIpv6pReceiveFragment и создать новый объект сборки с длиной данных фрагмента, представляющей собой большое 16-битное значениеIpv6pReassemblyTimeout.Ipv6pReassemblyTimeout и вызвать переполнение буфера в куче.Пакеты в скрипте рассылаются в большом количестве, чтобы повысить вероятность их объединения. Основная полезная нагрузка довольно проста:
Мы также вручную устанавливаем поля hop limit и flow label в IPv6-заголовке. Напомним, что буферизованные данные пакета сбрасываются из-за уязвимости. Это означает, что при обработке фрагментированного пакета IPv6-заголовок будет интерпретироваться как данные заголовка фрагмента. Поле hop limit в IPv6-заголовке будет интерпретироваться как один из битов поля id в заголовке фрагмента. Изменяя его, мы гарантируем, что уязвимость сработает для нескольких различных фрагментов и вызовет несколько различных повреждений, увеличивая вероятность краха (поскольку это PoC). Поле flow limit IP-заголовка будет интерпретироваться как поля смещения и индикатора продолжения заголовка фрагмента. Устанавливая его в 1, мы указываем, что будет еще заголовки (следовательно, можно вызвать Ipv6pReassemblyTimeout позже) и что смещение равно нулю (поскольку это первый пакет с таким идентификатором).
Ipv6pReassemblyTimeout, требует, чтобы исходный фрагментированный пакет был отправлен как одноадресный.Если не работает, это может быть из-за:
Ether(raw(sr1(IPv6(dst={your_dest_ip})/ICMPv6EchoRequest()))).src, но это иногда не работаетЕсли ничего не помогает, вы можете подключить отладчик ядра и проверить несколько вещей:
- Вызывается ли tcpip!Ipv6pReceiveDestinationOptions -> tcpip!Ipv6pProcessOptions -> tcpip!IppSendErrorList?
- Поставьте точку останова на tcpip!Ipv6pProcessOptions и проверьте, равен ли [rcx] нулю постоянно. Если да, то пакеты по какой-то причине не объединяются.
- Поставьте точку останова на tcpip!Ipv6pReceiveFragment и проверьте, равен ли [rcx+0x30] нулю. Если нет, то уязвимость по какой-то причине не сработала.