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

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

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

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

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

Категории

Все категории
Loading categories
vbox_cve_2017_10235 — [CVE-2017-10235] Описание и PoC переполнения буфера устройства E1000 в VirtualBox | Kitploit
Инструменты/GitHubGitHub/fundacion-sadosky/vbox_cve_2017_10235
Безопасность встроенных системАнализ уязвимостейЭксплуатацияФаззингАппаратная БезопасностьЭксплуатация Бинарных Файлов
GitHubfundacion-sadosky/vbox_cve_2017_10235

vbox_cve_2017_10235

[CVE-2017-10235] Описание и PoC переполнения буфера устройства E1000 в VirtualBox

Популярное

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

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

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

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

Смотреть все инструменты →
Поделиться
Репозиторий
36528 лет назадПроверено Kitploit

CVE-2017-10235: Переполнение буфера устройства E1000 VirtualBox

Введение

В следующем документе описывается ошибка, обнаруженная в VirtualBox v5.1.22 (исправлена в v5.1.24) в компоненте эмуляции гостевого устройства DevE1000 (Эмуляция контроллера Ethernet Intel 82540EM), в функции e1kFallbackAddToFrame, что приводит к переполнению буфера на хосте, когда гостевая ОС находится под контролем злоумышленника.

Ошибка была признана Oracle в CPU за июль 2017 года с присвоенным CVE-2017-10235.

Уязвимость была подтверждена как на хосте с Linux (Ubuntu 16.04), так и на хосте с Windows (v8.1) при запуске гостевой Linux (также Ubuntu 16.04), но уязвимость может быть воспроизведена во многих различных комбинациях хоста и гостя. Во всех сценариях предполагается конфигурация сети по умолчанию: только один сетевой адаптер подключенный к NAT типа Intel PRO/1000 MT Desktop (82540EM).

Поскольку управляющие структуры (включая указатели на функции) могут быть перезаписаны данными, контролируемыми злоумышленником, можно с уверенностью предположить, что во многих сценариях может быть достигнуто удаленное выполнение кода. Oracle присвоила этой ошибке низкий балл CVSS, посчитав, что она имеет риск конфиденциальности None и целостности Low, что, по нашему мнению, не отражает полный потенциал компрометации этой ошибки (объяснение возможности RCE приведено ниже).

Описание и эксплуатация ошибки

Код VirtualBox, реализующий эмуляцию контроллера Ethernet Intel 82540EM (в файле src/VBox/Devices/Network/DevE1000.cpp), в функции e1kFallbackAddToFrame, реализует аппаратную сегментацию TCP:

root@kitploit:~

static int e1kFallbackAddToFrame(PE1KSTATE pThis, E1KTXDESC *pDesc,
                                bool fOnWorkerThread)
{
#ifdef VBOX_STRICT
   PPDMSCATTERGATHER pTxSg = pThis->CTX_SUFF(pTxSg);
   Assert(e1kGetDescType(pDesc) == E1K_DTYP_DATA);
   Assert(pDesc->data.cmd.fTSE);
   Assert(!e1kXmitIsGsoBuf(pTxSg));
#endif

   uint16_t u16MaxPktLen = pThis->contextTSE.dw3.u8HDRLEN +
                           pThis->contextTSE.dw3.u16MSS;
   Assert(u16MaxPktLen != 0);
   Assert(u16MaxPktLen < E1K_MAX_TX_PKT_SIZE);

Эта функция правильно проверяет, что максимальная длина TX-пакета (u16MaxPktLen) ниже стандартного максимума в 16288 байт (E1K_MAX_TX_PKT_SIZE), но делает это в виде макроса Assert, который будет отключен в релизной сборке, фактически делая проверку бесполезной для конечного пользователя. Это можно сравнить с аналогичной функцией e1kAddToFrame, которая выполняет проверку с помощью явного if вместо Assert:

root@kitploit:~

static bool e1kAddToFrame(PE1KSTATE pThis, RTGCPHYS PhysAddr,
                         uint32_t cbFragment)
{
   PPDMSCATTERGATHER   pTxSg    = pThis->CTX_SUFF(pTxSg);
   bool const          fGso     = e1kXmitIsGsoBuf(pTxSg);
   uint32_t const      cbNewPkt = cbFragment + pThis->u16TxPktLen;

   if (RT_UNLIKELY( !fGso && cbNewPkt > E1K_MAX_TX_PKT_SIZE ))
   {
       E1kLog(("%s Transmit packet is too large: %u > %u(max)\n",
               pThis->szPrf, cbNewPkt, E1K_MAX_TX_PKT_SIZE));
       return false;
   }

Различие между использованием обычной функции (e1kAddToFrame) и резервной (e1kFallbackAddToFrame) определяется в e1kXmitDesc() и зависит от двух факторов: флаг TSE должен быть включен в дескрипторах данных/контекста (управляется ОС в гостевой машине) и флаг GSO должен быть выключен. Последний зависит от многих факторов, поэтому существует много способов его отключить, но самый удобный — включить режим loopback, который настраивается через регистр управления приемом (биты RCTL.LBM), также управляемый гостевой ОС.

Включение режима loopback приведет к тому, что функция e1kXmitAllocBuf будет использовать буфер aTxPacketFallback (Буфер пакетов передачи, используемый для резервного режима TSE и loopback) для размещения PDM scatter/gather буфера, с упомянутой длиной 16288 байт (E1K_MAX_TX_PKT_SIZE), и сигнализировать, что GSO будет отключен (путем установки NULL в pvUser).

root@kitploit:~

if (RT_LIKELY(GET_BITS(RCTL, LBM) != RCTL_LBM_TCVR))
{

  ...

}
else
{
 /* Create a loopback using the fallback buffer and preallocated SG. */
 AssertCompileMemberSize(E1KSTATE, uTxFallback.Sg, 8 * sizeof(size_t));
 pSg = &pThis->uTxFallback.Sg;
 pSg->fFlags      = PDMSCATTERGATHER_FLAGS_MAGIC |
                    PDMSCATTERGATHER_FLAGS_OWNER_3;
 pSg->cbUsed      = 0;
 pSg->cbAvailable = 0;
 pSg->pvAllocator = pThis;
 pSg->pvUser      = NULL; /* No GSO here. */
 pSg->cSegs       = 1;
 pSg->aSegs[0].pvSeg = pThis->aTxPacketFallback;
 pSg->aSegs[0].cbSeg = sizeof(pThis->aTxPacketFallback);
}

Это приведет к тому, что вызов функции e1kXmitIsGsoBuf (внутри e1kXmitDesc) вернет False, и при включенном TSE в дескрипторе данных поток выполнения пойдет в e1kFallbackAddToFrame (вместо более безопасной функции e1kAddToFrame с правильной проверкой).

root@kitploit:~

/*
 * Add the descriptor data to the frame.  If the frame is complete,
 * transmit it and reset the u16TxPktLen field.
 */
if (e1kXmitIsGsoBuf(pThis->CTX_SUFF(pTxSg)))
{

  ...

}
else if (!pDesc->data.cmd.fTSE)
{

  ...

}
else
{
    STAM_COUNTER_INC(&pThis->StatTxPathFallback);
    rc = e1kFallbackAddToFrame(pThis, pDesc, fOnWorkerThread);
}

Внутри e1kFallbackAddToFrame, при отключенной в релизной сборке вышеупомянутой проверке, MSS может быть произвольно большим (до 64K минус HDRLEN), что позволяет передать произвольно большое DTALEN в e1kFallbackAddSegment:

root@kitploit:~

/*
* Carve out segments.
*/
int rc;
do
{
 /* Calculate how many bytes we have left in this TCP segment */
 uint32_t cb = u16MaxPktLen - pThis->u16TxPktLen;
 if (cb > pDesc->data.cmd.u20DTALEN)
 {
     /* This descriptor fits completely into current segment */
     cb = pDesc->data.cmd.u20DTALEN;
     rc = e1kFallbackAddSegment(pThis, pDesc->data.u64BufAddr, cb,
                 pDesc->data.cmd.fEOP /*fSend*/, fOnWorkerThread);

Функция e1kFallbackAddSegment будет использовать это значение (теперь как аргумент u16Len) для копирования из гостевой памяти в буфер aTxPacketFallback в памяти хоста (через PDMDevHlpPhysRead) без дополнительных проверок этой длины, что вызовет переполнение буфера (емкость буфера 16288 байт при размере памяти до 64К).

root@kitploit:~

static int e1kFallbackAddSegment(PE1KSTATE pThis, RTGCPHYS PhysAddr,
                   uint16_t u16Len, bool fSend, bool fOnWorkerThread)
{
    int rc = VINF_SUCCESS;
    /* TCP header being transmitted */
    struct E1kTcpHeader *pTcpHdr = (struct E1kTcpHeader *)
            (pThis->aTxPacketFallback + pThis->contextTSE.tu.u8CSS);
    /* IP header being transmitted */
    struct E1kIpHeader *pIpHdr = (struct E1kIpHeader *)
            (pThis->aTxPacketFallback + pThis->contextTSE.ip.u8CSS);

    E1kLog3(("%s e1kFallbackAddSegment: Length=%x, remaining payload=%x,
             header=%x, send=%RTbool\n", pThis->szPrf, u16Len,
             pThis->u32PayRemain, pThis->u16HdrRemain, fSend));
    Assert(pThis->u32PayRemain + pThis->u16HdrRemain > 0);

    PDMDevHlpPhysRead(pThis->CTX_SUFF(pDevIns), PhysAddr,
                      pThis->aTxPacketFallback + pThis->u16TxPktLen, u16Len);

Возможное RCE

Чтобы сделать эту уязвимость более предрасположенной к RCE, стоит отметить, что переменная сразу после буфера — это его индекс (u16TxPktLen), используемый для записи в него (как смещение в аргументе PDMDevHlpPhysRead). Контроль этого значения с помощью начального переполнения буфера (вызванного первым дескриптором данных длиной E1K_MAX_TX_PKT_SIZE + 2 байта) позволил бы затем записать (во втором вызове PDMDevHlpPhysRead со вторым дескриптором данных) любой адрес памяти на расстоянии до 64K от буфера, не перезаписывая всю память между ними (что сделало бы атаку более сложной, пытаясь избежать потенциального сбоя).

Рядом с целевым буфером aTxPacketFallback, несколькими строками ниже и в пределах 64K, определена структура g_aE1kRegMap, которая включает вектор указателей на функции, реализующих обработчики чтения и записи (pfnRead и pfnWrite), что было бы идеальной целью для второго переполнения буфера для облегчения RCE.

Ошибка в e1kXmitAllocBuf

Стоит упомянуть (небольшое) осложнение в этом векторе атаки для полноты: существует то, что выглядит как ошибка в функции e1kXmitAllocBuf, где в случае режима loopback cbTxAlloc (Количество байт в следующем пакете) не сбрасывается в ноль, как это делается в обычном случае (в другой ветке его if). Это приводит к зависанию потока в цикле while функции e1kLocateTxPacket (внутри e1kXmitPending):

root@kitploit:~

while (e1kLocateTxPacket(pThis))
{
   fIncomplete = false;
   /* Found a complete packet, allocate it. */
   rc = e1kXmitAllocBuf(pThis, pThis->fGSO);
   /* If we're out of bandwidth we'll come back later. */
   if (RT_FAILURE(rc))
       goto out;
   /* Copy the packet to allocated buffer and send it. */
   rc = e1kXmitPacket(pThis, fOnWorkerThread);
   /* If we're out of bandwidth we'll come back later. */
   if (RT_FAILURE(rc))
       goto out;
}

Похоже, это происходит потому, что e1kLocateTxPacket преждевременно возвращает True в случае, когда cbTxAlloc не равен нулю, и не доходит до кода, который проверяет, равен ли iTxDCurrent значению nTxDFetched (обычный случай, когда все дескрипторы обработаны), что в норме заставило бы функцию вернуть False, фактически завершая упомянутый цикл.

root@kitploit:~

static bool e1kLocateTxPacket(PE1KSTATE pThis)
{
   LogFlow(("%s e1kLocateTxPacket: ENTER cbTxAlloc=%d\n",
            pThis->szPrf, pThis->cbTxAlloc));
   /* Check if we have located the packet already. */
   if (pThis->cbTxAlloc)
   {
       LogFlow(("%s e1kLocateTxPacket: RET true cbTxAlloc=%d\n",
                pThis->szPrf, pThis->cbTxAlloc));
       return true;
   }

Это означает, что первый пакет, отправленный устройству (после установки режима loopback), должен быть тем, который вызывает переполнение, иначе виртуальная машина зависнет (что приведет к DoS, а не к RCE).

Доказательство концепции

Поскольку настройка сетевого устройства далека от тривиальной, и чтобы избежать создания собственного драйвера для него, драйвер E1000 стандартного ядра Linux был модифицирован для генерации дескрипторов (как контекстных, так и данных), которые вызывают переполнение. Это модифицированное ядро доступно для загрузки из этого репозитория. Оно было протестировано в гостевой Ubuntu 16.04, вызывая сбой как на хостах Linux, так и Windows. Подробное описание доступно здесь.

Возможные решения

Уязвимость была исправлена в Changeset 67974 (bugref:8881). Проверки, выполненные как Assert в e1kFallbackAddToFrame, были преобразованы в явные проверки с помощью операторов if, которые теперь остаются активными в релизной сборке (аналогично тому, что уже было сделано в e1kAddToFrame). Кроме того, cbTxAlloc теперь устанавливается в ноль в обеих ветках (режим loopback и обычный режим) в e1kXmitAllocBuf.

Дополнительная (оборонительная) проверка, предлагаемая здесь, но не реализованная в наборе изменений, может заключаться в том, чтобы в e1kFallbackAddSegment (и аналогично в e1kAddToFrame) перед вызовом PDMDevHlpPhysRead явно проверять потенциальное переполнение буфера гостевой памятью (в основном, что u16TxPktLen плюс u16Len меньше длины буфера aTxPacketFallback).

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