Статья Безопасность операционных систем: Атаки на механизмы безопасности ядра

1768869171570.webp

Конфигурационные чек-листы, политики паролей и стандарты «харденинга» - это лишь верхушка айсберга под названием «безопасность ОС». Настоящая битва происходит в тёмных глубинах: в коде ядра, где один пропущенный байт может обрушить все барьеры; в логике гипервизора, чья абсолютная изоляция иногда оказывается миражом; в механизмах контейнеров, где легковесность часто оплачивается хрупкостью границ.

Эта статья - разбор основ, которые предпочитают не трогать. Мы снимаем слои абстракций и показываем, как на самом деле работают и как ломаются ключевые механизмы защиты в ядрах Windows, Linux и macOS. А затем спускаемся ещё ниже - к гипервизорам и контейнерам, чья безопасность полностью зависит от надёжности этих самых ядер.

Речь пойдёт не о теориях, а о конкретных атаках: эксплуатация Use-After-Free в драйверах, побег из виртуальной машины через баг в эмуляции устройства, компрометация кластера Kubernetes через уязвимость в kubelet. И о том, как проектировать системы, зная, что фундамент может дать трещину.

Краткий экскурс: как мы дошли до жизни такой

Давай посмотрим, как эволюционировала защита:
  1. DAC (Discretionary Access Control - Избирательное управление доступом).
    Каменный век. «Владелец файла сам решает, кто может его читать». Классика Linux (rwx) и Windows (ACL в NTFS). Проблема очевидна: если злоумышленник становится владельцем или администратором (root/SYSTEM), игра окончена. Вся безопасность рушится одним махом. Это как доверить охрану сейфа сотруднику, на которого можно надавить.

  2. MAC (Mandatory Access Control - Принудительное управление доступом).
    Железная длань государства. Правила диктует не владелец, а система. SELinux в Linux, AppArmor, SMACK. Цель - ограничить даже root'а. Ты можешь быть суперпользователем, но если политика SELinux говорит «нельзя писать в этот каталог», ты не напишешь. Это мощно, но невероятно сложно. Большинство просто отключают эту «мешающую» систему, сводя её защиту к нулю.

  3. Изоляция (Isolation).
    Парадигма «разделяй и властвуй». Если нельзя полностью доверять субъекту (пользователю, процессу), его нужно изолировать. Сначала на уровне процессов и пользователей. Потом пришла виртуализация - полная эмуляция железа для каждой гостевой ОС. Казалось бы, идеал: виртуальная машина - это железная клетка.
    Но появились контейнеры (Docker, Kubernetes). Они не эмулируют железо, они делят одно ядро ОС между множеством изолированных окружений (через namespaces и cgroups). Это легче, быстрее, дешевле. Но их безопасность зиждется на непоколебимости этого общего ядра. Если падёт оно, падут все контейнеры. Это не клетки, это комнаты в одном здании: взломав фундамент, ты получаешь доступ ко всем квартирам.
Эта эволюция - история гонки вооружений. Каждый новый уровень защиты (MAC, гипервизоры) рождал новый класс атак (логические ошибки в политиках, VM Escape). Мы не стали безопаснее. Мы стали сложнее. И в этой сложности - новые, ещё более опасные дыры.

Что значит «низкоуровневая атака» в нашем контексте?

  • В мире ОС:
    Это атака не на приложение (например, браузер), а на сам механизм, обеспечивающий безопасность приложений. Это не «украсть cookie», а обойти проверку прав доступа в ядре, чтобы прочитать память любого процесса. Это не «поднять ревёрс шелл», а найти уязвимость в драйвере файловой системы, чтобы с уровня пользователя выполнить произвольный код в ядре (Local Privilege Escalation, LPE) и стать богом системы. Примеры? Dirty Pipe в Linux - уязвимость в подсистеме ввода-вывода, позволяющая любому пользователю перезаписывать файлы от имени root. Вечность на исправление.

  • В мире гипервизоров:
    Это VM Escape - побег из виртуальной машины на хост-систему. Представь: ты арендовал сервер у облачного провайдера, работаешь в своей «безопасной» виртуалке. Найдя уязвимость в эмуляции сетевой карты гипервизором (например, старый CVE в VMware), ты можешь сломать изоляцию и получить доступ к хосту, а через него - к виртуальным машинам других клиентов. Гипервизор из надёжной клетки превращается в карточный домик.

  • В мире контейнеров: Это Container Escape.
    Ты не ломаешь эмуляцию железа, ты находишь дыру в логике изоляции самого ядра Linux. Уязвимость в runc (CVE-2019-5736) позволяла из контейнера скомпрометировать хост-систему. Или через неправильно настроенный volume монтирования получить доступ к чувствительным файлам хоста. Это взлом не клетки, а двери в комнате, которую все считали несуществующей.
Низкоуровневая атака - это атака на доверие. На доверие к тому, что ядро честно проверяет права. Что гипервизор честно изолирует гостей. Что контейнерный runtime честно разделяет пространства.
Когда это доверие нарушается, рушится вся пирамида безопасности, построенная сверху. Самый красивый фасад из политик и правил становится бесполезным.


1. Архитектура безопасности современных ядер ОС

Ты загружаешь операционную систему. Видишь красивый интерфейс, окна, иконки. Под этой картинкой - дикий, тоталитарный и до безумия сложный мир ядра. Именно здесь, в этом «кольце 0», принимаются окончательные решения: можно ли процессу прочитать эту память? Открыть этот файл? Отправить этот пакет в сеть?

Безопасность на этом уровне - это не про политики паролей. Это про архитектурные гарантии и их изъяны. Понимать это - всё равно что знать, где в крепости спроектированы потайные ходы, ещё до того, как по ней начнут бить тараном.

1768871476103.webp

1.1. Windows: Мир объектов, или тотальная диктатура с благими намерениями


Объектная модель:
Представь, что каждый процесс, поток, файл, ключ реестра, мьютекс и даже семафор в системе -это объект в святая святых, в пространстве ядра. У каждого объекта есть дескриптор - что-то вроде пропуска в казино. Приложение не работает с объектом напрямую. Оно говорит ядру: «Эй, у меня есть пропуск №12345, хочу почитать файл». Ядро смотрит на пропуск, находит объект и проверяет: а есть ли у процесса, который держит этот пропуск, нужные права?

Права эти записаны в Access Control List (ACL) объекта. Это суровый список «кто что может». Владелец? Администраторы? Система? Каждой группе или пользователю выставляются битовые маски прав: FILE_READ_DATA, PROCESS_TERMINATE, THREAD_SUSPEND. Если в ACL тебя нет - ядро даже не будет с тобой разговаривать. Это мощнейший механизм, но его ахиллесова пята - сложность. Неправильно настроенный ACL (или унаследованный от родительской папки) - и секретный файл становится достоянием общественности.

Integrity Levels и Protected Processes: война с самим собой
Со временем Microsoft поняла, что делить мир только на «пользователей» и «админов» - мало. Так родился Mandatory Integrity Control (MIC). Каждому процессу и объекту присваивается уровень целостности: Low, Medium, High, System. Процесс с низким уровнем (например, браузер в Enhanced Protected Mode) не может писать в объекты с высоким уровнем, даже если в ACL у него есть все права. Это попытка сдержать взломанное приложение в песочнице.

Апофеозом этой паранойи стали Protected Processes (PP) и Protected Processes Light (PPL). Это процессы, к которым запрещён доступ извне даже администратору с DEBUG правами. Попробуй запусти Process Explorer и убить антивирусный процесс MsMpEng.exe - не получится. Эта технология, рождённая для защиты DRM в играх, стала основой Credential Guard, который хранит хэши паролей в процессе, недоступном даже для SYSTEM. Обойти это - холи грейл для многих атак на Windows.

Virtualization-Based Security (VBS): последний рубеж
Когда ты не можешь доверять даже собственному ядру (потому что эксплойты достают до ring0), что делаешь? Правильно, запускаешь ядро внутри виртуальной машины. Так работает Hypervisor-Protected Code Integrity (HVCI) и тот же Credential Guard. Ядро Windows и критические компоненты безопасности запускаются в изолированной виртуальной машине, защищённой гипервизором (Hyper-V). Атаковать их можно только через уязвимости в самом гипервизоре, что на порядки сложнее.

PatchGuard и DSE: война с руткитами на поражение
Ты - злоумышленник. Ты получил права ядра. Твоя цель - остаться там навсегда, спрятаться. Классический способ - запатчить ядро, изменить его код или структуры данных в памяти. Kernel Patch Protection (PatchGuard) - это часовой внутри ядра, который периодически проверяет критические структуры на целостность. Обнаружив изменения - паникует и перезагружает систему, сбрасывая руткит с доски.

Driver Signature Enforcement (DSE) - это контроль на въезде. Только драйверы, подписанные доверенным сертификатом Microsoft, могут быть загружены в ядро. Хочешь загрузить свой руткит? Лиши его доступа к Test Signing, либо найди уязвимость в самом механизме проверки подписи (как это было с CVE-2018-0896).

Итог по Windows: Это цитадель, построенная с расчётом на то, что враг уже внутри. Её безопасность - это слои и слои контроля, где каждый следующий призван защищать от падения предыдущего. Взламывать её - значит играть в шахматы с системой, которая знает все твои прошлые ходы.

1768869996309.webp

1.2. Linux: Монолит с модульной свободой. Или анархия?

Если Windows - это тоталитарная империя с жёсткими правилами, то Linux - это вольный город с невероятно мощными, но опциональными законами. Его безопасность не диктуется свыше по умолчанию. Её строит сам администратор. И в этом - её сила и её главная слабость.

Namespaces и cgroups: песочницы, которые мы заслужили
Всё, что ты знаешь о Docker и контейнерах, выросло из двух фундаментальных механизмов ядра Linux.
  • Namespaces изолируют глобальные системные ресурсы. PID namespace даёт процессам свою нумерацию PID (внутри контейнера процесс может иметь PID 1, будучи на хосте обычным процессом №3040). Network namespace даёт свой стек сетевых интерфейсов. Mount namespace - своё дерево файловых систем. Это не виртуализация, это иллюзия изоляции, создаваемая ядром для каждой группы процессов.
  • Control Groups (cgroups) - это про управление ресурсами и ограничения. Они говорят: «Этой группе процессов можно только 50% CPU, 1 ГБ памяти и не более 10 МБ/с на дисковый ввод». Без cgroups один сбесившийся процесс в контейнере мог бы съесть всю память хоста.
Вместе они создают каркас контейнера. Но: это границы, установленные одним общим ядром. Взломай ядро - и все границы падут.

Capabilities: расчленение всемогущества
В мире Linux root - это не единая сущность. Это набор из 40+ привилегий (capabilities), которые можно включать и выключать по отдельности. Процесс может иметь право CAP_SYS_ADMIN (почти как root), но не иметь CAP_NET_RAW (для создания raw-сокетов). Или иметь CAP_DAC_OVERRIDE (игнорировать права на файлы), но не иметь CAP_SYS_BOOT (для перезагрузки). Это позволяет создавать минималистичные, привилегированные контейнеры, которые могут делать что-то одно (например, биндить сокет на порт 80), не обладая всей полнотой власти.

SELinux и AppArmor: принудительный контроль для тех, кто не боится
Если capabilities ограничивают процессы, то Security-Enhanced Linux (SELinux) и AppArmor контролируют всё: процессы, файлы, сокеты, IPC. Это реализация модели Mandatory Access Control (MAC). SELinux работает на основе меток (контекстов безопасности), которые присваиваются всем объектам в системе. Правила (политики) в тысячах строк кода определяют, может ли процесс с меткой httpd_t читать файл с меткой user_home_t. Главная проблема SELinux - его адская сложность. AppArmor проще: он описывает профили в виде путей к файлам. Но оба инструмента, будучи правильно настроенными, - это бронежилет для системы. Проблема в том, что 90% админов видят в них врага и отключают.

Итог по Linux: Это конструктор безопасности максимальной гибкости. По умолчанию он даёт тебе мощь и свободу зарезать себя об острые углы. Его безопасность - это результат осознанного выбора и кропотливого труда. Здесь нет волшебной таблетки Credential Guard. Есть только grsecurity, PaX (и их потомки), seccomp-bpf и твоя голова. Атаковать Linux - это часто значит не взламывать защиту, а находить системы, где её забыли включить, или где она криво настроена.

1768870548684.webp

1.3. macOS (XNU): Гибрид под диктатурой эстетов

macOS - это особый путь. Её ядро XNU - это гибрид: микроядро Mach (отвечает за низкоуровневые вещи вроде IPC и планирования потоков) и тяжёлый монолит BSD (всё остальное: сетевой стек, файловые системы). Безопасность здесь - это жёсткая воля Apple, не терпящая сопротивления.

Sandbox Framework (Seatbelt): тюрьма для всего
Почти каждое приложение в macOS работает внутри песочницы. Это не опция, это правило. Сетевой доступ? Только через санкционированные API. Доступ к файлам? Только в пределах разрешённых директорий (~/Downloads, ~/Documents) или через диалог выбора файла. Эта песочница, прозванная Seatbelt, - главный ограничитель. Даже если в Safari найдёть RCE, злоумышленник окажется в тесной клетке с сильно ограниченными возможностями. Взлом превращается в поиск способа сломать или обойти саму песочницу.

System Integrity Protection (SIP): запрещено даже для Бога
Ты -root. Ты - администратор. Ты хочешь записать файл в /System/Library/Extensions или модифицировать системный бинарник. В любой другой системе - без проблем. В macOS с включённым SIP - запрещено. SIP (также известный как rootless) - это набор ограничений на уровне ядра, которые защищают критические части системы даже от суперпользователя. Его можно отключить (зажав клавиши при загрузке), но тогда система громко скажет, что она «может быть повреждена». Это философский подход: целостность системы важнее полномочий администратора.

Apple Mobile File Integrity (AMFI) и подпись кода
Ни один код не может быть выполнен в системе, если он не подписан доверенным сертификатом от Apple (или Developer ID). Это правило железобетонно. Попытка загрузить неподписанный драйвер ядра (KEXT) или запустить модифицированное системное приложение приведёт к отказу. Это убивает целый класс руткитов и троянов с места в карьер. Атака здесь - это либо кража доверенного сертификата, либо эксплуатация уязвимости в механизме проверки подписи.

Итог по macOS: Это «безопасность через диктатуру». Пользователю (и администратору) не дают возможности выстрелить себе в ногу. Система жёстко контролирует всё, от запуска кода до доступа к файлам. Взламывать её - значит воевать не со слабой конфигурацией, а с продуманными, глубоко интегрированными в систему механизмами принудительного контроля, которые Apple совершенствует годами. Это сложный, но высокоценный противник.


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


2. Атаки на механизмы ядра

2.1. Общие векторы атак на пространство ядра: Искусство находить трещины в камне

Прежде чем бить в конкретную стену Windows или Linux, нужно понять, из какого материала она сложена и где её структурные слабости.

Драйверы: троянский конь в сердце цитадели
Самый классический и жирный вектор. Зачем ломать дверь, если можно попросить часового внутри открыть её? Драйверы работают в пространстве ядра (ring 0) с максимальными привилегиями, но часто пишутся третьими сторонами (вендорами железа, периферии) с качеством кода... скажем так, неидеальным. Уязвимость в драйвере сетевой карты, принтера или даже виртуального диска - это прямой пропуск в ядро. Пример? Драйверы Capcom.sys или Intel Audio Drivers в прошлом содержали фатальные дыры, позволяющие из пользовательского режима выполнять произвольный код в ядре. Мораль: Безопасность ОС равна безопасности самого слабого загруженного в неё драйвера.

Use-After-Free (UAF) в пулах памяти ядра
Представь, что ядро - это отель. Процессы снимают номера (выделяют блоки памяти), живут в них, а потом освобождают. UAF - это ситуация, когда ядро забывает, что номер уже освобождён, и отдаёт ключ от него новому постояльцу, в то время как старый ещё не вернул ключ и может туда зайти. В контексте ядра это выглядит так:
  1. Объект ядра (например, структура FILE_OBJECT) создаётся и используется.
  2. Ядро освобождает память под этот объект, но где-то в коде остаётся «висячий» указатель на неё.
  3. Атакующий немедленно «захватывает» эту освобождённую память, заполняя её своим контролируемым содержимым (например, шеллкодом или указателями на свои функции).
  4. Когда ядро по «висячему» указателю обращается к, как оно думает, своему старому объекту, оно натыкается на данные атакующего и падает или выполняет его код.
Переполнение буфера в ядре: старая, но грозная классика
Та же старая песня, но на привилегированной сцене. Если драйвер или системный вызов некорректно проверяет размер копируемых в буфер ядра данных, атакующий может записать за его границы. Это может перезаписать соседние критичные структуры данных, изменить указатели или перезаписать код. В современных ОС с KASLR (рандомизацией адресного пространства ядра) и SMEP/SMAP (защитой от выполнения пользовательского кода в ядре) это сложнее, но не невозможно.

Атаки на логику безопасности:
Иногда не нужно ничего переполнять. Достаточно понять логику и обмануть её.
  • TOCTOU (Time-Of-Check Time-Of-Use):
    Ядро проверяет, есть ли у тебя права на файл A. Ты проходишь проверку. В микросекунду между проверкой и самим открытием ты быстро заменяешь файл A на символьную ссылку на файл /etc/shadow. Ядро, думая, что работает с A, открывает тебе тенеевой файл. Классика в Unix-системах.

  • Симлинки и hardlinks:
    Неправильная обработка символьных ссылок в привилегированных контекстах (например, в планировщиках задач cron или системных демонах) - вековый способ эскалации привилегий. Процесс, работающий от root, по ошибке следует по симлинку, созданному пользователем, и перезаписывает системный файл.

2.2. Windows-specific атаки: Охота на часовых и патч-гардов

Здесь играют по правилам, о которых я писал вначале, но ищут в них изъяны.

Win32k.sys - вечная золотая жила
Это гигантский драйвер, отвечающий за весь пользовательский интерфейс. Огромная кодовая база, сложная логика, работа с пользовательскими данными. Он был и остаётся рассадником уязвимостей для локального повышения привилегий (LPE). Атаки через Win32k - это часто сложные цепочки использования bitmaps, palettes или других объектов GDI, приводящие к тому самому UAF или переполнению в ядре.

Атаки на MSR-регистры и SMEP
SMEP (Supervisor Mode Execution Prevention) - аппаратная защита, запрещающая ядру исполнять код, находящийся в пользовательской памяти (например, твой шеллкод). Как обойти? Нужно сделать так, чтобы ядро думало, что исполняет свой код.
  1. Находишь в памяти ядра нужную функцию (обходя KASLR через утечку информации).
  2. С помощью уязвимости достигаешь примитива произвольной записи в память ядра.
  3. Патчишь код легитимной функции ядра (например, в ntoskrnl.exe), подменяя несколько байт так, чтобы она сделала то, что нужно тебе (скажем, вызвала system("cmd.exe")).
  4. Заставляешь ядро выполнить эту патченую функцию. SMEP молчит, потому что код выполняется из памяти ядра. PatchGuard должен бы этому воспрепятствовать, но если атака выполняется быстро и аккуратно, можно успеть до его следующего запуска.
Обход PatchGuard и DSE
Прямая атака на PatchGuard - дело сложное. Чаще ищут уязвимости в механизмах, которые он должен защищать. Например, CVE-2018-0896 позволяла обойти DSE и загрузить неподписанный драйвер, эксплуатируя уязвимость в логике проверки загрузки драйверов с тестовыми подписями в определенных версиях Windows. Другие атаки могут нацеливаться на гипервизор (Hyper-V), который лежит в основе VBS. Уязвимость в гипервизоре (например, CVE-2021-28476 в hvix64.exe или hvax64.exe) - это крах всей модели VBS, потому что доверенная вычислительная база (TCB) оказывается скомпрометированной.

2.3. Linux-specific атаки: Эксплуатация простоты и мощи

В Linux многое построено на доверии и аккуратности. Когда это доверие нарушается, последствия масштабны.

Dirty Pipe (CVE-2022-0847) - шедевр простоты
Недавний, блестящий пример атаки на логику ядра. Уязвимость была в подсистеме pipes и splice(). Из-за ошибки инициализации флагов страниц памяти можно было писать в файлы, открытые только на чтение, обходя все проверки прав. Любой непривилегированный пользователь мог перезаписать root-бинарники вроде su или sudo, моментально получая полный контроль. Гениальность - в использовании легальных, документированных системных вызовов (splice()) для достижения незаконного результата.

Атаки на eBPF: Оборотная сторона суперсилы
eBPF - это мощнейшая технология для безопасного выполнения пользовательского кода в ядре для мониторинга и фильтрации. Но её верификатор, гарантирующая безопасность этого кода, - сложнейший кусок программы. Уязвимости в верификаторе (например, CVE-2021-31440) позволяли обойти проверки и выполнить произвольный код в ядре. Это идеальный пример: сам инструмент безопасности становится вектором атаки.

Обход namespaces и cgroups
Контейнеры - это не ВМ. Если в контейнере есть привилегия CAP_SYS_ADMIN (а она часто есть в «привилегированных» контейнерах), процесс внутри может делать страшные вещи:
  • userfaultfd():
    Этот механизм, предназначенный для миграции процессов, можно использовать для точного управления временем в гонках (TOCTOU) в ядре, что иногда приводит к эскалации привилегий на хосте.

  • /proc/self/mem и /proc/self/map_files/:
    Неправильная изоляция этих файлов может позволить процессу в контейнере читать/писать память других процессов, включая процессы хоста.

  • Mount namespace escape:
    Если контейнеру разрешено монтировать хостовые каталоги (/, /etc) или устройства (/dev/sda1), это прямой путь к побегу.

2.4. macOS-specific атаки: Взлом диктатуры эстетов

Здесь атакуют не слабую конфигурацию, а самые сильные, централизованные механизмы.

Обход SIP через легитимные компоненты
Прямая атака на SIP - это перезагрузка с отключенным SIP. Настоящие атаки хитрее. Они ищут системные процессы или драйверы, которые имеют право писать в защищённые локации. Если такой процесс содержит уязвимость (например, в парсинге входных данных), его можно заставить выполнить работу за злоумышленника. Так работали многие эксплойты нулевого дня: они не ломали SIP, они использовали доверенного посредника, который имел необходимые привилегии.

Эксплуатация уязвимостей в IOKit
IOKit - это фреймворк для драйверов в macOS, написанный на подмножестве C++. Как и любые драйверы, они содержат ошибки. Уязвимости в драйверах графики, сетевых карт или USB-контроллеров - стандартный путь в ядро XNU. Из-за монолитности BSD-части ядра компрометация через драйвер часто даёт сразу огромную власть.

Логические ошибки в Sandbox профилях
Песочница (Seatbelt) описывается профилями в формате SBPL (SandBox Profile Language). Если в этом профиле для критичного системного процесса (например, launchd или системного демона) не прописано достаточно строгое правило, процесс может вырваться из изоляции. Или, наоборот, если приложение имеет слишком широкий профиль (например, многие инструменты разработчика), его скомпрометированная версия может натворить дел. Поиск таких логических дыр - целое искусство.


1768869418598.webp

3. Безопасность гипервизоров

Мы добрались до святого святых современной инфраструктуры - уровня гипервизора. Если ядро ОС - это закон внутри государства, то гипервизор - это закон мироздания, отделяющий одно государство от другого. Он создаёт и контролирует виртуальные вселенные, каждая со своим ядром, своими процессами, своей иллюзией полного контроля над железом.

Доверие к гипервизору - абсолютное. Если ты арендуешь VM в облаке, вся твоя безопасность строится на вере, что сосед по физическому серверу не сможет из своей виртуальной машины прорваться к твоей. VM Escape - это не просто очередная уязвимость. Это крах самой парадигмы. Это вселенский баг, позволяющий вырваться из симуляции.

3.1. Архитектурные основы: как устроена вселенная и кто её смотритель

Прежде чем ломать, нужно понять, что именно мы ломаем.

Тип 1 (bare-metal) vs. Тип 2 (hosted): разница в доверии
  • Гипервизоры типа 1 (ESXi, Hyper-V, Xen, KVM):
    Они работают напрямую на железе, вместо операционной системы. Это как диспетчер аэропорта, который с нуля контролирует все взлётные полосы и терминалы. Их поверхность атаки теоретически меньше - нет гостевой ОС, которую можно скомпрометировать, чтобы добраться до гипервизора. Большинство продакшн-облаков построено на них (KVM в OpenStack, Xen в AWS EC2 Classic, Hyper-V в Azure, ESXi в VMware vSphere).

  • Гипервизоры типа 2 (VMware Workstation, VirtualBox, Parallels):
    Они работают как приложение внутри хостовой ОС (Windows, Linux, macOS). Это как симулятор полёта, запущенный на твоём ноутбуке. Чтобы атаковать такой гипервизор, нужно сначала скомпрометировать хостовую ОС, что часто проще. Хотя и здесь есть свои изысканные атаки.
Аппаратная виртуализация (Intel VT-x, AMD-V): волшебная палочка процессора
Раньше виртуализация была костылём: гипервизор кропотливо эмулировал все инструкции гостя, что было дико медленно. Появление аппаратной виртуализации изменило всё. Процессор научился работать в двух режимах:
  • Root Mode (режим хоста): В нём работает гипервизор. Полный контроль.
  • Non-Root Mode (режим гостя): В нём работают виртуальные машины. Попытка выполнить привилегированную инструкцию (вроде HLT или обращения к контроллеру прерываний) вызывает VM Exit - мягкий переход управления к гипервизору. Он эмулирует результат и возвращает управление гостю (VM Entry).
Это основа изоляции. Но каждая транзиция VM Exit/Entry - это огромный оверхед. Поэтому появилась...

Паравиртуализация (Paravirtualization, PV): гость в курсе, что он виртуальный
Чтобы не платить производительностью за каждый чих, гостевой ОС дают специальные паравиртуализованные (PV) драйверы. Вместо того чтобы эмулировать реальную сетевую карту e1000 с кучей VM Exits, гость через быстрый механизм (как virtio в KVM или vmxnet в VMware) напрямую говорит гипервизору: «Эй, отправь этот пакет». Это быстрее, но расширяет поверхность атаки: теперь в гостевом ядре работает сложный код, который активно общается с гипервизором. Баг в PV-драйвере на стороне гостя - прямой путь к атаке на гипервизор.

3.2. VM Escape: искусство сломать симуляцию

Цель - получить выполнение кода в контексте гипервизора (или хостовой ОС для Type 2). Оттуда - всё: чтение памяти других ВМ, полный контроль над ними, доступ к сети хоста.

Классика: уязвимости в эмуляции виртуального железа
Самый жирный и исторически успешный вектор. Гипервизор должен эмулировать для гостя десятки устройств: BIOS, видеоадаптер (VGA), жёсткий диск (IDE/SATA), сетевую карту (E1000/VMXNET), USB-контроллер.
  • Пример (CVE-2019-5544 и CVE-2019-5545 в VMware ESXi):
    Уязвимости в драйверах vmxnet3 (сетевая карта) и UHCI (USB-контроллер). Скомпрометировав гостевую ОС, атакующий мог отправить специально сформированные пакеты или смоделировать подключение USB-устройства, что приводило к переполнению буфера в гипервизоре и выполнению произвольного кода на уровне хоста ESXi.

  • Почему это работает:
    Код эмуляции устройств - это монструозные легаси-модули, часто унаследованные из проектов вроде QEMU. Написаны на C, полны сложной логики парсинга форматов. Идеальная мишень для фаззинга.
Атаки на общую память: теневая дверь между мирами
Для ускорения работы гипервизор и гость часто используют общие регионы памяти.
  • Balloon Driver (vmware-tools, virtio-balloon):
    Этот драйвер в гостевой ОС помогает гипервизору управлять памятью: «забирать» её у гостя, когда нужно другой ВМ. Он работает через общие страницы памяти. Уязвимость в логике драйвера или протоколе обмена может позволить гостю писать в произвольные области памяти гипервизора.

  • Shared Folders / HGFS (VMware):
    Механизм для обмена файлами между хостом и гостем. Атакуя клиентскую часть в гостевой ОС, можно попытаться добиться выполнения кода на хосте.
Атаки через паравиртуализованные интерфейсы (VMCalls, Hypercalls)
Это системные вызовы гостя к гипервизору. Гость вызывает специальную инструкцию (например, VMCALL в Intel VT-x), которая передаёт управление гипервизору с указанием номера вызова и аргументов.
  • Логические ошибки: Недостаточная проверка аргументов в гипервизоре. Может ли гость запросить копию памяти, принадлежащей другой ВМ? Может ли он передать указатель на память, которую затем гипервизор будет использовать без должной проверки?
  • Уязвимости в KVM: KVM - это модуль ядра Linux, превращающий его в гипервизор. Атака на KVM - это часто атака на ядро Linux хоста. Уязвимость в обработке ioctl от /dev/kvm или в эмуляции специфичных инструкций CPU (например, MOV SS или ICEBP) может привести к побегу. Известная уязвимость VENOM (CVE-2015-3456) как раз была в эмуляции дискового контроллера Floppy Disk Controller (FDC) в QEMU, который используется KVM.
Вложенная виртуализация (Nested Virtualization): ломаем матрёшку
Современные процессоры позволяют запускать гипервизор внутри виртуальной машины. Это нужно для облачных провайдеров, которые хотят продавать клиентам услугу «твой собственный KVM». Атакующий, арендовав такую VM, запускает внутри неё свой гипервизор и пытается атаковать гипервизор первого уровня со стороны гостя второго уровня. Это дико сложно, но расширяет поле для атаки, особенно на логику обработки вложенных VM Exits.

Сценарий реальной атаки: от гостя к кластеру

Допустим, ты злоумышленник, который арендовал VM в облаке.
  1. LPE в гостевой ОС. Используешь уязвимость в ядре гостевого Linux (скажем, Dirty Pipe), чтобы стать root внутри своей VM.
  2. Разведка. Изучаешь окружение: какая сетевая карта (vmxnet3), есть ли vmware-tools или qemu-ga (гостевой агент). Ищешь драйверы, связанные с виртуализацией.
  3. Эксплуатация уязвимости в PV-драйвере. Находишь свежий CVE-2023-XXXX в драйвере vmxnet3. Готовишь эксплойт, который, будучи запущенным от root в гостевой ОС, через этот драйвер вызывает переполнение буфера в гипервизоре ESXi.
  4. VM Escape. Эксплойт срабатывает. Твой код выполняется в контексте гипервизора на физическом хосте.

  5. Завоевание кластера. С уровня гипервизора ты:
    1. Читаешь память других ВМ на этом хосте (извлекаешь ключи, пароли, сессии).
    2. Внедряешь код в эти ВМ.
    3. Получаешь доступ к сети управления гипервизором (например, к vCenter Server). Через неё заражаешь весь кластер.
Это не фантастика. Такие цепочки (как уязвимости в VMware vCenter, приводящие к компрометации всего SDDC) - главный кошмар облачных провайдеров и корпоративных дата-центров.

Ирония в том, что сама технология, призванная обеспечить максимальную изоляцию, становится, будучи скомпрометированной, точкой катастрофического отказа всей системы безопасности.


1768869370344.webp

4. Контейнеры: изоляция, которая не является виртуализацией (и почему это опасно)

4.1. Low-level механика изоляции: Иллюзия, создаваемая ядром

Погрузимся в ту самую «общую канализацию», которой нет у ВМ.

Docker под капотом: что на самом деле происходит, когда ты делаешь docker run -it alpine?
  1. Namespaces (пространства имён) - создают иллюзию изолированного окружения. Docker последовательно вызывает системные вызовы unshare() и clone() с флагами:
    1. CLONE_NEWPID: PID namespace. Процесс в контейнере видит себя как PID 1. На хосте у него свой обычный PID (например, 3040). Но /proc внутри контейнера отфильтрован, чтобы показывать только «внутренние» процессы.
    2. CLONE_NEWNET: Network namespace. Контейнер получает свой собственный сетевой стек: loopback-интерфейс, приватные IP, таблицы маршрутизации и iptables.
    3. CLONE_NEWNS: Mount namespace. Своё дерево файловых систем. Точка монтирования / внутри контейнера - это на самом деле образ (например, overlayfs), наложенный на слой хоста.
    4. CLONE_NEWIPC, CLONE_NEWUTS, CLONE_NEWUSER: Изоляция IPC, hostname и UID/GID соответственно (User namespace - самый сложный и мощный).
  2. Control Groups (cgroups v2) - это про ограничения, а не изоляция. Они гарантируют, что контейнер не сможет съесть все ресурсы:
    1. cpu.max: Лимит CPU.
    2. memory.max: Жёсткий лимит памяти. При превышении - OOM Killer убьёт процесс в контейнере.
    3. pids.max: Максимальное количество процессов.
    4. cgroup.subtree_control: Управление делегированием прав - ключевая фишка v2 для безопасности в Kubernetes.
  3. OverlayFS - волшебство «слоёного пирога».
    1. Lowerdir (read-only): Слои базового образа (alpine:latest). Общие для всех контейнеров на этом образе.
    2. Upperdir (read-write): Тонкий слой, уникальный для каждого контейнера. Все изменения пишутся сюда (Copy-on-Write).
    3. Mergeddir: Виртуальное представление, где все слои сливаются в единую файловую систему, видимую из контейнера.
    4. Уязвимость: Если процесс в контейнере может смонтировать что-то в Upperdir или манипулировать самим OverlayFS (имея CAP_SYS_ADMIN), он может повлиять на другие контейнеры, использующие тот же Lowerdir.
Сетевые модели: выбирай свою дыру
  • bridge (дефолт): Docker создаёт виртуальный мост docker0. Контейнеры в одной сети могут общаться друг с другом. Проброс портов (-p 80:80) - это правила iptables DNAT. Атаки: ARP-спуфинг внутри моста, сканирование соседей.
  • host: Контейнер делит сетевое пространство имён с хостом. Получает прямой доступ ко всем сетевым интерфейсам. Катастрофа для безопасности. Позволяет слушать хостовые порты, следить за трафиком.
  • none: Полная изоляция (только loopback). Безопасно, но обычно бесполезно.
Безопасность образов: цепочка поставок как вектор атаки
Образ - это не бинарник. Это архив слоёв (тарболлов). Проблемы:
  • Устаревшие пакеты в базовом слое (FROM ubuntu:20.04):
    Все уязвимости этого старого дистриба - теперь твои.

  • Злонамеренные образы в Docker Hub:
    docker pull cool-tool:latest может притащить майнер или бэкдор. Подпись образов (DOCKER_CONTENT_TRUST) используется единицами.

  • Утечка секретов в слоях: Классика.
    В Dockerfile пишут: RUN curl -H "Authorization: Bearer $TOKEN" .... Токен навсегда вшивается в слой образа. Достаточно сделать docker history --no-trunc <image>.

4.2. Атаки на контейнеры и оркестраторы: Побег из комнаты и захват этажа

Container Escape: священный грааль
Цель - из контейнера получить шелл на хостовой системе. Пути:
  1. Уязвимости в самом ядре Linux, эксплуатируемые из контейнера.
    Dirty Pipe, старый Dirty COW. Если процесс в контейнере может их эксплуатировать (часто нужно лишь быть root внутри контейнера), он получает привилегии на хосте. Именно поэтому запускать контейнеры с --privileged или с CAP_SYS_ADMIN - самоубийство.
  2. Уязвимости в контейнерном рантайме.
    CVE-2019-5736 (runc) - историческая бомба. Позволяла перезаписать бинарный файл runc на хосте из контейнера, выполнив код при следующем запуске контейнера. Механизм: через /proc/self/exe в контейнере можно было получить дескриптор на исполняемый файл runc хоста и записать в него.

  3. Неправильно смонтированные тома (docker run -v /:/host ...).
    Если контейнеру монтируют корень хоста (/) или директорию с чувствительными данными (/var/run/docker.sock - ядерная кнопка, о ней ниже), побег тривиален: просто запускаешь chroot на смонтированную файловую систему хоста.

  4. Уязвимости в драйверах ядра, доступных из контейнера.
    Если контейнеру даны права для загрузки модулей ядра (CAP_SYS_MODULE) или доступа к сырым устройствам (/dev/mem, /dev/kmem), можно атаковать ядро напрямую.
Угроза номер 1: Docker Socket (/var/run/docker.sock)
Это UNIX-сокет, через который Docker CLI общается с демоном (dockerd). Демон работает от root на хосте. Если ты смонтируешь этот сокет в контейнер (-v /var/run/docker.sock:/var/run/docker.sock), то любой процесс в контейнере, способный писать в этот сокет, получает полный root-контроль над хостом. Он может:
  • Запустить новый контейнер с монтированием / хоста: docker run -v /:/host -it alpine chroot /host.
  • Остановить другие контейнеры.
  • Создавать сети, образы - делать что угодно.
    Это не баг, это фатальная ошибка конфигурации, которую совершают сплошь и рядом.
Kubernetes-specific угрозы: когда оркестратор работает против тебя
K8s - это новый уровень абстракции и новые поверхности атаки.
  1. Компрометация Kubelet:
    Kubelet - это агент на каждой ноде, который слушает API (порт 10250 по умолчанию). Если он недостаточно защищён, атакующий может:
    1. Выполнить команду в любом поде на ноде: /exec, /run.
    2. Считывать логи (/logs).
    3. Создавать новые поды. Исторически, плохая конфигурация аутентификации Kubelet была золотой жилой.
  2. Обход RBAC (Role-Based Access Control):
    Сложные RBAC-правила могут содержать логические ошибки. Например, разрешение "*" на глагол "list" для ресурса "pods" может (в зависимости от версии) позволять также и читать логи этих подов ("pods/log"). Атаки на escalation привилегий внутри кластера - отдельная большая тема.

  3. Уязвимости в сетевых плагинах (CNI):
    Calico, Cilium, Flannel - это мощный код, работающий часто с привилегиями. Уязвимость в них (например, CVE-2020-10749 в Flannel) может привести к отказу сети, перехвату трафика или выполнению кода на нодах.

  4. Supply Chain атаки через Helm-чарты или манифесты:
    Публичный Helm-чарт для «крутого веб-приложения» может в секрете создавать ServiceAccount с правами кластера-администратора и отправлять его токен злоумышленнику.
Проблема цепочки поставок (Supply Chain) в 2023+
Это уже не про «плохие образы». Это про:
  • Взлом реестра образов (Docker Hub, Quay, ECR): Подмена популярных образов (nginx, redis) на вредоносные.
  • Компрометация систем сборки (CI/CD): GitHub Actions, GitLab Runners. Если злоумышленник получает доступ туда, он может встроить бэкдор во все собираемые образы.
  • Атаки на зависимости (Poisoned Packages): Как в экосистеме Python (PyPI) или Node.js (npm), но для базовых образов. Уязвимость в базовом пакете (libssl, glibc) заражает все производные образы.

4.3. Укрепление низкоуровневой безопасности: Как строить комнаты с бронированными стенами

1. Запуск без root (Rootless Containers)
Это фундамент. Docker и Podman поддерживают режим, когда демон и контейнеры запускаются от обычного пользователя. Используется User Namespace для маппинга UID внутри контейнера. Даже если контейнер сбежит, он останется в пределах namespace непривилегированного пользователя на хосте, не сможет читать /etc/shadow или писать в системные директории.

2. Системы hardened ядра для контейнеров
  • gVisor (от Google):
    Это не просто рантайм. Это пользовательский уровень ядра, написанный на Go, который перехватывает системные вызовы контейнера. Даже если контейнер скомпрометирован, он атакует не настоящее ядро Linux, а песочницу gVisor, что значительно сложнее. Плата - небольшой оверхед.

  • Kata Containers:
    Полная противоположность. Это мини-виртуальные машины, каждая со своим собственным микроядром Linux, запущенные на настоящем гипервизоре (KVM, Firecracker). Дают изоляцию уровня ВМ, но с быстрым запуском, как у контейнеров. Идеально для мультитенантных сред с высокой степенью недоверия.
3. Mandatory Access Control для контейнеров
  • SELinux/AppArmor:
    Можно и нужно назначать контейнерам жёсткие политики. Например, политика SELinux для контейнера может явно запрещать запись в домашние директории хоста или доступ к сырым сокетам.

  • seccomp-bpf:
    Профиль seccomp - это белый список разрешённых системных вызовов для контейнера. Стандартный профиль Docker запрещает около 44 опасных вызовов (например, keyctl(), add_key()). Подход нулевого доверия: запрещено всё, что явно не разрешено.
4. Аппаратная изоляция - будущее уже здесь
  • AMD SEV / Intel TDX: Позволяют запускать контейнер (или специальную микро-ВМ) в зашифрованной области памяти, недоступной даже для гипервизора или ядра хоста. Это защита от злонамеренного администратора облака. Kata Containers 2.0 уже поддерживают SEV-SNP.
Контейнеры - это не «лёгкие виртуальные машины». Это механизм упаковки и изоляции процессов, всецело зависящий от безопасности одного-единственного ядра Linux хоста. Их модель безопасности по умолчанию слабее, чем у ВМ.
  • Их главный враг - не zero-day в ядре, а кривые конфиги: privileged: true, смонтированный Docker Socket, избыточные Linux Capabilities.
  • Их главная защита - не надеяться на "по умолчанию". Это активное применение всех доступных механизмов hardening: rootless, seccomp, AppArmor, регулярное обновление образов и нод.


Заключение

Ни одна из рассмотренных систем не является «безопасной» по умолчанию. Каждая - это компромисс:
  • Windows жертвует простотой ради глубины обороны, создавая лабиринт, в котором можно заблудиться, но где одна найденная нить Ариадны ведёт к центру.
  • Linux жертвует единообразием ради гибкости, давая тебе верёвку, на которой можно как подняться, так и повеситься.
  • macOS жертвует свободой ради целостности, запирая тебя в идеально спроектированной, но чужой тюрьме.
  • Гипервизоры жертвуют простотой кода ради абсолютной изоляции, но один баг в этом коде превращает стену между мирами в решето.
  • Контейнеры жертвуют изоляцией ради скорости и плотности, подменяя железобетонные стены гипсокартонными перегородками.
Их объединяет одно: их безопасность зиждется на доверии к низкоуровневому коду. Доверии к тому, что разработчик ядра не ошибся в проверке прав. Что инженер гипервизора корректно обработал все VM Exit. Что автор Dockerfile не смонтировал /var/run/docker.sock.

Главный вывод этой статьи не в том, какая ОС или технология безопаснее. Он в том, что твоя реальная безопасность равна надёжности самого слабого звена в этой цепочке доверия.

Технологии меняются. Сегодня мы говорим о KVM и runc, завтра - о микровиртуализации Firecracker и формально верифицированных микроядрах seL4. Но принципы останутся: сложность - враг безопасности, доверие должно быть проверяемым, а изоляция - абсолютной там, где это критично.

Твоя задача - не гоняться за каждой CVE. Твоя задача - спроектировать систему, в которой эксплуатация одной уязвимости не даст злоумышленнику ключей от всего королевства. Систему, где падение одной стены не обрушит весь замок, а лишь загонит атакующего в следующий, лучше укреплённый коридор.

Игра в архитектора-параноика не заканчивается. Она просто переходит на следующий уровень. Ты теперь знаешь, где искать трещины в фундаменте. Осталось самое сложное - построить на этом знании что-то, что устоит.
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab