Компоненты ПО с открытым исходным кодом сейчас встречаются почти в каждом приложении. Это повышает эффективность разработки, но привносит дополнительные риски, в первую очередь связанные с атаками на цепочку поставок. Создавая операционную систему KasperskyOS, мы в «Лаборатории Касперского» задумались: как сделать переиспользование недоверенного кода безопасным? Эта задача особенно актуальна, когда речь идет о системе, на базе которой строятся продукты для отраслей с повышенными требованиями к кибербезопасности.
В этой статье мы расскажем, какие механизмы в KasperskyOS позволяют снизить риски, характерные для распространенных ОС. А также покажем на реальном примере, как системы на базе Linux и KasperskyOS по-разному справляются с киберугрозами.
В качестве примера мы выбрали сценарий вредоносной функциональности, заложенной в open-source-драйвер сетевой карты. В свете роста количества и сложности атак на цепочку поставок сценарий более чем реалистичный. Если вам интересно, почему мы выбрали именно сценарий использования Linux-драйвера и атаку на цепочку поставок, — оставим объяснения под спойлерами.
Почему в примере именно драйвер Linux
Ядро Linux как единая база драйверов и компонентов для новых ОС
Сегодня системы с открытым исходным кодом получили широчайшее распространение и используются практически во всех сферах жизни. Облачные платформы, хостинг-провайдеры, банковские системы, медицинские учреждения, системы управления производствами и множество других сервисов активно используют решения на базе ядра Linux.
Само ядро Linux — это источник инженерных решений, включающий огромную базу исходного кода с реализациями широко используемых структур данных, файловых систем, сетевых протоколов и драйверов устройств. Именно драйверы для ядра Linux рассматриваются как универсальный интерфейс общения производителей оборудования с авторами операционных систем.
Популярность и доступность ядра Linux привели к тому, что его компоненты, в первую очередь драйверы, стали использоваться за пределами Linux-экосистемы. Например, FreeBSD позволяет загружать в свое ядро немодифицированные драйверы Linux. Фреймворк для разработки операционных систем Genode предлагает методологию по переносу драйверов Linux в системы на его основе. Huawei заявляет о поддержке в микроядерной HarmonyOS NEXT контейнеров с драйверами Linux. Даже любительские проекты операционных систем, такие как KolibriOS, используют некоторые драйверы ядра Linux, перенесенные в их кодовую базу, например, для работы со встроенной графикой Intel.
Почему именно атака на цепочку поставок
Краткая история атак на цепочку поставок
За последние несколько лет мы видели немало инцидентов, в которых злоумышленники так или иначе атаковали цепочку поставок. Вот самые заметные из них:
2020 год — взлом компании SolarWinds, которая поставляет решения для мониторинга IT-инфраструктуры. Инцидент продемонстрировал серьезность угрозы, так как в результате атаки скомпрометированными оказались тысячи государственных и частных организаций по всему миру.
2021 год — к аналогичным последствиям привел взлом Codecov, когда была скомпрометирована утилита анализа покрытия кода.
2023 год — зафиксировано заражение распространяемых Microsoft артефактов через атаку на JFrog Artifactory, менеджер, использовавшийся в инфраструктуре корпорации.
2024 год — целый букет инцидентов:
Нашумевший бэкдор в XZ/liblzma, позволяющий обходить аутентификацию OpenSSH, удаленно выполнять команды и скрывать записи в журнале событий.
Загрузка схожих с оригинальными по именованию пакетов в репозиторий PyPI.
Современные атаки на цепочку поставок часто развиваются продолжительное время и используют многовекторный подход. Злоумышленники применяют и социальную инженерию, и перехват доменных имен, и взлом GitHub-аккаунтов разработчиков популярных зависимостей ПО. Причем компрометация происходит через вредоносные изменения сборочной инфраструктуры, подмену динамических библиотек и даже через модификацию исходных кодов оригинального продукта.
Учитывая, что зависимость критической инфраструктуры, банковских организаций, медицинских учреждений и промышленных производств от открытого программного обеспечения растет, вопросы предотвращения и своевременного выявления таких атак становятся особенно актуальными.
Вредоносные модификации в системном ПО
Инцидент с XZ/liblzma показывает, что бэкдоры и намеренно внедренные уязвимости могут присутствовать даже в популярном системном ПО. Вредоносная модификация системного кода особенно опасна — это значительно усложняет выявление угроз и повышает потенциальный ущерб от атак.
Но возможно ли существование бэкдоров в таких проектах, как ядро Linux? Казалось бы, код ядра этой ОС проходит через серьезный процесс ревью: предполагается, что мейнтейнеры из публичных списков рассылки не пропустят зловредный патч в релиз. Но на практике даже опытные разработчики, сведущие в вопросах информационной безопасности, могут не заметить опасные изменения. Одна из причин заключается в том, что язык C, на котором разработано большинство системных программ, включая ядра ОС, позволяет скрывать неочевидное поведение за счет принятых умолчаний, undefined/unspecified поведения, неявных преобразований типов и особенностей препроцессора.
Вообще, возможность намеренного внедрения бэкдоров в операционные системы с открытым кодом обсуждается уже много лет. Еще в 2010 году бывший подрядчик ФБР заявил о том, что агентство когда-то внедрило уязвимости в код OpenBSD. Когда про потенциальную закладку стало известно, лидер проекта Тео Де Раадт провел серьезное ревью проблемных мест в коде. За много лет развития системы код, разумеется, серьезно поменялся, но Де Раадт все равно обнаружил потенциальные уязвимости. Сказать однозначно, были ли эти уязвимости добавлены кем-то намеренно или нет, — нельзя. Однако то, что их не заметили при первичном ревью, когда принимали патчи, — факт.
Сокрытие вредоносных изменений
Как ревьюеры могут не заметить внесение вредоносного кода в проект? Существует целый ряд методик, которые помогают злоумышленникам внедрять скрытые изменения в программное обеспечение. Если интересны конкретные примеры, то можно изучить результаты конкурса Underhanded C Contest. В его рамках требуется создать программу, которая выполняет оговоренную злонамеренную активность в дополнение к вполне безобидной функциональности. Важное условие: злонамеренное поведение должно быть трудно определяемым на ревью кода специалистами по информационной безопасности.
Дополнить картину может известная работа Кена Томпсона Reflections on Trusting Trust, поднимающая вопрос о доверии к инструментам разработки, например компиляторам. Ведь компилятор может инструментировать код, оставляя в нем уязвимости, генерируя пути обхода вызовов аутентификации популярных библиотек (например, PAM).
Кроме того, нередко необходимые BSP (Board Support Package) и драйверы ядра не входят в дерево исходного кода ядра Linux, а поставляются непосредственно производителем оборудования (типичная ситуация для китайских устройств). Таким образом, в них можно встретить уязвимости, которые широкое Linux-сообщество даже теоретически не могло заметить. Причем эти драйверы зачастую имеют невысокое качество, содержат специализированные хаки/оптимизации, используют нестабильные интерфейсы ядра и так далее. Так что уязвимости в них можно встретить и без всякого злого умысла.
Модели угроз современных ОС, «песочницы» для приложений и драйверов
Разумеется, при создании собственной операционной системы мы не собирались изобретать велосипед. Поэтому для начала мы провели анализ распространенных стандартов безопасности операционных систем и безопасной разработки, а также изучили подходы, которые применяют создатели современных коммерчески успешных ОС. В числе прочих мы изучили Android, ChromeOS, GrapheneOS, Whonix, Ubuntu Core, Qubes OS, Genode OS Framework, Legato, HarmonyOS NEXT, OpenBSD, seL4.
Большинство этих систем переосмысляют взгляды на модель угроз персональных и профессиональных устройств. То есть устройств, которые круглые сутки подключены к глобальной Сети и обрабатывают контент из недоверенных источников со сложной структурой, а также позволяют расширять функциональность за счет приложений, разрабатываемых третьими сторонами. При этом такие устройства могут быть атакованы с использованием разнообразных беспроводных технологий, таких как NFC, Bluetooth и Wi-Fi. А носимые девайсы вдобавок могут попасть в руки злоумышленников, то есть для их защиты необходимо рассматривать угрозы, предполагающие еще и физический доступ.
Одним из ключевых решений для минимизации этих рисков является изоляция кода приложений и модулей обработки недоверенных данных в так называемых «песочницах» — специальных средах исполнения с минимальным набором привилегий. Такой подход позволяет эффективно ограничивать возможный ущерб от атак, инкапсулируя вредоносное воздействие в пределах одного процесса или виртуальной машины. Доступ «песочниц» к ресурсам операционной системы предоставляется на основе комплексной политики разрешений, учитывающей требования пользователя, разработчика платформы и администратора.
Наша команда, ответственная за разработку KasperskyOS, пришла к выводу о необходимости распространения этого подхода дальше уровня приложений, на другие, традиционно менее изолированные системные компоненты. В их число входят драйверы устройств и сетевой стек — как раз для снижения рисков, связанных с эксплуатацией потенциальных закладок или уязвимостей в коде Linux-драйверов и подсистем. Это позволит безопасно использовать Linux-драйверы при необходимости ускоренного портирования KasperskyOS на новые аппаратные платформы.
Угрозы и способы их митигации в KasperskyOS
Благодаря микроядерной архитектуре в KasperskyOS удается реализовать эффективные компенсирующие меры, способные снизить влияние множества рисков, характерных для монолитных операционных систем. Давайте рассмотрим часть модели угроз, разрабатываемой для KasperskyOS, связанную именно с заявленным сценарием — закладкой в Linux-драйвере устройства.
При моделировании угроз часто используются схемы потоков данных в системе. На них указывают как активные сущности, например процессы, так и пассивные, представляющие хранилища данных (файлы, записи баз данных, регистры устройств, структуры данных в памяти), непосредственно информационные потоки и границы доверия.
В популярных операционных системах на основе монолитных ядер для драйвера существует только две границы доверия: между пространством пользователя и ядром ОС и между ядром ОС и контролируемым оборудованием. Это не позволяет рассматривать угрозы драйверов в отрыве от угроз ядру операционной системы. При таких ограничениях крайне трудно митигировать угрозу атак на цепочку поставок драйверов, включающую возможность злонамеренной модификации их исходного кода и обнаружения случайных эксплуатируемых уязвимостей. В отличие от мира Windows, где имеется практика подписывания драйверов и их централизованное распространение, в Linux практически единственным эффективным решением оказывается использование харденингов, затрудняющих эксплуатацию. Как показывает опыт, даже самые продвинутые харденинги не способны остановить настойчивого и изобретательного взломщика.
Системы на основе микроядра лишены этих ограничений. Благодаря иной организации архитектуры и разделению компонентов системы в некоторых случаях она допускает использование дополнительных границ доверия и дает возможность более детально и эффективно рассматривать угрозы.
Рассмотрим сокращенную модель угроз абстрактному драйверу устройства. К источникам угроз отнесем непосредственно устройства, взаимодействующие с драйвером, пользовательские процессы, для которых драйвер осуществляет абстракцию над устройством и мультиплексирование его ресурсов, стеки и подсистемы, предоставляющие дополнительные уровни абстракции над драйвером, например сетевой стек.
В таблице ниже представлено несколько угроз с указанием возможных компенсирующих мер, применяемых в KasperskyOS.
Угроза |
Подугроза |
Описание |
Желаемое поведение |
Митигация |
DRV 1*. Отказ в обслуживании |
DRV 1.1. Отказ в обслуживании всей операционной системы вследствие нарушения работы драйвера |
Ошибка в драйвере эксплуатируется для создания исключительной ситуации, например разыменования нулевого указателя. Ядро операционной системы паникует и переходит в ошибочное состояние. В большинстве популярных операционных систем эту угрозу невозможно митигировать |
Нарушение работы драйвера не приводит к отказу в обслуживании всей системы |
Выполнение драйвера в «песочнице» с пониженными привилегиями. Драйвер запускается в отдельном процессе пространства пользователя. API ядра, связанный с выделением физической памяти, должен быть |
DRV 1.2. Отказ в обслуживании зависимых сервисов вследствие чрезвычайно длительной активности в ходе работы драйвера |
Ошибка в логике драйвера эксплуатируется для того, чтобы снизить его отзывчивость, например, за счет исполнения бесконечного цикла. Процессы и подсистемы, использующие драйвер, могут перестать отвечать из-за блокировки в ожидании событий |
Нарушение работы драйвера не приводит к отказу в обслуживании зависимых сервисов |
Запуск драйвера в отдельном процессе. Мониторинг здоровья драйвера: «проверка пульса», сторожевой таймер. В ряде случаев подобные меры позволят перезапустить зависший драйвер и восстановить контекст его работы |
|
DRV 2. Утечка информации |
DRV 2.1. Утечка данных, размещенных в куче |
Вследствие доступа за допустимые границы объекта, неинициализированных значений или иных ошибок в процессе обработки пользовательских данных возможна утечка данных из драйвера и ядра ОС. Такие данные могут содержать секреты, например ключи шифрования, используемые в ядерном Crypto API |
Данные не утекают, или утечка ограничена нечувствительными данными |
Отделение драйвера от ядра, запуск в пространстве пользователя. Благодаря этому драйвер не разделяет общих секретов с ядром ОС. Применение практик безопасного программирования, SDL. Валидация параметров вызовов генерируемым парсером |
DRV 2.2. Утечка данных, размещенных в стеке |
* Коды угроз являются символическими и используются исключительно для внутренних ссылок в рамках данной таблицы и настоящей статьи.
Демонстрационный сценарий
Рассмотрим простой сценарий из модели угроз, приведенной выше: в драйвер сетевой карты намеренно заложили функциональность, приводящую к отказу в обслуживании. В системе присутствует критический пользовательский процесс, осуществляющий контроль оборудования, драйвер сетевой карты и сетевой стек используются для передачи журнала наблюдения за оборудованием. Полная остановка системы может привести к порче дорогостоящего оборудования. Нарушение в передаче журнала событий рассматривается как инцидент невысокого уровня.
На базе этого сценария мы построили экспериментальный стенд с использованием Radxa ROCK 3A. Стенд состоит из двух образов, выполняющих идентичную бизнес-задачу. При этом один образ реализован на простой сборке GNU/Linux, а другой использует KasperskyOS Community Edition. Обе системы имеют идентичную модельную «закладку» с уязвимостью в сетевом драйвере. Отправка специально сформированного пакета вызывает незамедлительный переход драйвера в ошибочное состояние.
Эксперимент демонстрирует сохранение критической функциональности с KasperskyOS и падение ядра ОС на основе GNU/Linux. Отметим, что данный пример реализует защиту от конкретной уязвимости в конкретном сценарии. Для митигации других угроз следует правильно подходить к архитектуре решения и использовать различные паттерны безопасности.
Продемонстрированные в эксперименте варианты атаки на цепочку поставок, потенциально способные оказать серьезное влияние на работоспособность системы и безопасность данных, сейчас актуальны как никогда. Тем не менее, такого рода угрозы редко рассматриваются разработчиками операционных систем, поскольку без возможности изоляции драйверов в песочнице у них нет возможности серьезно повлиять на реализацию этих угроз. Все что им остается — надеяться на добрые намерения разработчиков открытого системного ПО и качество доступных харденингов.
Описание стенда и код можно посмотреть в нашем репозитории на GitFlic. Если вам интересно узнать больше о нашей операционной системе и заложенных в ней принципах безопасности, вы можете воспроизвести наш пример, установив KasperskyOS Community Edition.