16-летний баг в KVM: Исправлена уязвимость Januscape в ядре Linux, допускающая побег из виртуальной машины

KVM

Исследователь безопасности Хюнву Ким (Hyunwoo Kim) обнаружил уязвимость в подсистеме виртуализации KVM/x86, которая получила идентификатор CVE-2026-53359 и название Januscape. Проблема существует в коде на протяжении 16 лет и представляет собой use-after-free в реализации теневого блока управления памятью (shadow MMU). При определённых условиях гостевая виртуальная машина может выполнить побег на хост, что создаёт серьёзную угрозу для мультитенантных облачных сред. Патч, устраняющий уязвимость, был включён в основную ветку ядра Linux в июне 2026 года.

Уязвимость CVE-2026-53359

Уязвимость затрагивает как архитектуру Intel (VMX), так и AMD (SVM), что делает её одной из первых публично задокументированных кросс-архитектурных уязвимостей побега из KVM. Корень проблемы лежит в функции kvm_mmu_get_child_sp(), которая отвечает за повторное использование теневых страниц. Ошибка заключается в том, что при проверке возможности повторного использования страницы сравнивается только номер гостевого кадра (GFN), но не учитывается роль страницы (role). Роль определяет, является ли теневая страница отображением гостевой таблицы страниц (косвенная) или результатом разбиения большой страницы (прямая). При несовпадении ролей для одного GFN возникает несогласованное отслеживание состояния памяти, что нарушает инварианты обратного отображения (rmap) в ядре.

Условия для эксплуатации возникают в сценариях вложенной виртуализации, когда гостевая ОС (L1) запускает свою собственную гостевую систему (L2). В этом случае гипервизор L0 должен программно создавать теневые страницы для вложенных таблиц EPT/NPT, что задействует устаревший путь теневого MMU. Весь процесс происходит целиком в ядре, без обращения к пользовательскому пространству (например, к QEMU). В результате возникает состояние, при котором одна теневая страница уже освобождена, но другая всё ещё содержит ссылку на неё. При очистке такой структуры ядро записывает фиксированную константу (на x86_64 - 0x8000000000000000) в память, которая могла быть перераспределена под другой объект, что даёт примитив записи после освобождения.

Опубликованный proof-of-concept (PoC) демонстрирует атаку типа "отказ в обслуживании" (DoS), приводящую к панике хоста. Когда записи rmap становятся несогласованными, срабатывает проверка KVM_BUG_ON_DATA_CORRUPTION. В системах с включённым параметром panic_on_oops (типично для корпоративных дистрибутивов вроде RHEL) это вызывает немедленное завершение работы хоста. PoC представляет собой модуль ядра, загружаемый внутри гостевой ВМ. Он строит вложенные таблицы страниц и запускает два потока: один переключает запись в таблице страниц между большой страницей и страницей таблицы, другой непрерывно инициирует страничные ошибки через вложенную гостевую ВМ. Такое состояние гонки расширяет неатомарное окно в логике отслеживания страниц KVM, позволяя надёжно воспроизвести некорректное повторное использование.

Уязвимость затрагивает ядра Linux начиная с коммита 2032a93d66fa (2010 год) и до исправления в коммите 81ccda30b4e8 (июнь 2026 года). Она оставалась необнаруженной более 16 лет. По информации исследователя, Januscape использовалась как zero-day уязвимость в соревновании Google kvmCTF, что подтверждает её практическую опасность. Патч добавляет в условие повторного использования проверку роли теневой страницы (role.word), что предотвращает некорректное переиспользование страниц с разными ролями.

Для облачных провайдеров, предоставляющих клиентам возможность вложенной виртуализации и root-доступ внутри гостевых машин, данная уязвимость представляет прямую угрозу изоляции. Администраторам настоятельно рекомендуется как можно скорее установить обновление ядра, содержащее исправление. В качестве временной меры защиты может быть ограничение использования вложенной виртуализации, если она не является критически необходимой. Выпуск эксплуатационного кода для полноценного побега (с выполнением кода на хосте) пока не опубликован, но, по словам автора, такой эксплойт существует и работает в контролируемой среде. Сложность его создания связана с ограниченным примитивом записи (фиксированное значение в контролируемый слот), что требует тщательного подбора объекта-жертвы в памяти.

Обновление ядра с коммитом 81ccda30b4e8 устраняет первопричину уязвимости, добавляя проверку роли страницы в kvm_mmu_get_child_sp(). Это гарантирует, что существующая теневая страница будет использована повторно только при совпадении как GFN, так и роли. В случае несовпадения ролей создаётся новая теневая страница, что предотвращает цепочку событий, ведущую к повреждению памяти и потенциальному побегу.

Ссылки

Комментарии: 0