bubblewrap icon indicating copy to clipboard operation
bubblewrap copied to clipboard

locally-written launch script unexpectedly allows sandboxed app to overwrite its own .desktop file

Open axet opened this issue 5 months ago • 6 comments

I'm running Telegram binary software under bwrap with limited access to file system. But one in a while I see telegram some how replace my starter desktop file with it's own script which causing next start of telegram become unprotected by bwrap.

Here is full bwrap command:

axet     3531936  0.0  0.0   3576  2092 ?        S    16:42   0:00 bwrap --dev-bind / / --tmpfs /home/axet --ro-bind /home/axet/bin /home/axet/bin --ro-bind /home/axet/.config /home/axet/.config --ro-bind /home/axet/.local/share/wineprefixes /home/axet/.local/share/wineprefixes --bind /home/axet/.cache/telegram-desktop /home/axet/.cache/telegram-desktop --bind /home/axet/.local/share/TelegramDesktop /home/axet/.local/share/TelegramDesktop --bind /home/axet/Downloads /home/axet/Downloads --bind /home/axet/Soft/telegram /home/axet/Soft/telegram --bind /tmp /tmp --bind /home/axet/Soft/telegram /home/axet/Soft/telegram /home/axet/Soft/telegram//Telegram

As you can see I only granted access only to some folders but somehow telegram changes ~/.local/share/applications and remove my telegram.desktop script adds its own script:

# ll .local/share/applications/ | grep teleg
-rwxrwxr-x+ 1 axet axet    612 авг  3 20:10 org.telegram.desktop._65f73dc27c0fb30cb1424daa92f46251.desktop

I have no idea how this happens. Maybe because simple mistake in launch scripts. Maybe dbus allow to create/replace old desktop files.

My startup scripts based on homebin shell script and dependent telegram sh script:

  • https://gitlab.com/axet/homebin/-/blob/debian/homebin
  • https://gitlab.com/axet/homebin/-/blob/debian/homebin.d/web/telegram

axet avatar Aug 05 '25 13:08 axet

possible security issue

If you believe you have found a security vulnerability, please do not report it in public where anyone with malicious intent would be able to see it and benefit from it!

smcv avatar Aug 05 '25 14:08 smcv

Maybe dbus allow to create/replace old desktop files

D-Bus itself does not, but services on the D-Bus session bus do.

If you give an application like Telegram unrestricted access to the D-Bus session bus, that is nearly always a sandbox escape (arbitrary code execution outside the sandbox): it can make something like systemd --user or flatpak-session-helper or gnome-session execute arbitrary code on its behalf by just asking nicely.

Similarly, access to various other sockets in $XDG_RUNTIME_DIR, like $XDG_RUNTIME_DIR/systemd/private, is enough for arbitrary code execution.

Making a meaningful security boundary requires a deny-by-default approach, so granting access to only limited things. --dev-bind / / is a huge red flag from that point of view. Please take a look at the bubblewrap invocation in something like Flatpak and you'll see how complicated it is to have a container where "desktop things" can still work - but all of that complexity is necessary, and Flatpak wouldn't have it if it wasn't needed. (In particular, Flatpak uses https://github.com/flatpak/xdg-dbus-proxy to filter access to the D-Bus session bus.)

smcv avatar Aug 05 '25 14:08 smcv

As you can see I only granted access only to some folders

Yes, and in particular they do not include ~/.local/share/applications. I agree that with this command-line, it shouldn't be possible for Telegram to overwrite files in ~/.local/share/applications directly.

However, it can certainly overwrite files in that directory indirectly, by asking a component outside the sandbox to do the overwriting on its behalf. Sending requests on the D-Bus session bus is one possible way; there could be others. I don't know which indirect mechanism Telegram used. dbus-monitor --session, gdbus monitor --session or busctl monitor --user could tell you whether it is sending requests on the D-Bus session bus, but the session bus is unlikely to be the only privileged side-channel you have left open.

If you want a meaningful security boundary, then unrestricted access to the D-Bus session bus, /tmp and $XDG_RUNTIME_DIR is not possible: you will have to prevent or filter these accesses (and perhaps others). bubblewrap provides a toolkit for sandboxing apps, but it is not, itself, a ready-to-use sandbox. Higher-level components like Flatpak are closer to providing a ready-to-use sandbox based on bubblewrap.

My startup scripts based on homebin shell script and dependent telegram sh script

Sorry, we do not have the resources to provide detailed technical support for your own locally-written scripts. It's up to you to design a security policy, and then construct a bubblewrap command-line that implements your security policy.

If you can construct a test-case where bubblewrap allows something that you believe should have been denied by the command-line arguments that you gave it, please report that privately to [email protected], flatpak-security at lists.freedesktop.org or https://github.com/containers/bubblewrap/security/advisories/new.

smcv avatar Aug 05 '25 14:08 smcv

First looks found correlations to D-Bus service:

org.freedesktop.DBus Interface -> ListNames()

Returns one name with:

org.telegram.desktop._65f73dc27c0fb30cb1424daa92f46251

which is exactly the same name of desktop file created in application folder:

org.telegram.desktop._65f73dc27c0fb30cb1424daa92f46251.desktop

I'll keep investigating.

axet avatar Aug 05 '25 14:08 axet

I have new bwrap script without --dev-bind and with dbus disabled (no /run/user/1000/bus access), but telegram still somehow managed to escape bwrap to replace my desktop launcher script with its own with no bwrap call.

bwrap --ro-bind / / --tmpfs /home --tmpfs /home/axet --tmpfs /tmp --tmpfs /var/tmp --tmpfs /run/user/1000 --unshare-pid --unshare-uts --unshare-cgroup-try --ro-bind /tmp/.X11-unix /tmp/.X11-unix --ro-bind /run/user/1000/pipewire-0 /run/user/1000/pipewire-0 --ro-bind /run/user/1000/pipewire-0.lock /run/user/1000/pipewire-0.lock --ro-bind /run/user/1000/pipewire-0-manager /run/user/1000/pipewire-0-manager --ro-bind /run/user/1000/pipewire-0-manager.lock /run/user/1000/pipewire-0-manager.lock --dev /dev --dev-bind /dev/dri /dev/dri --dev-bind /dev/snd /dev/snd --ro-bind /home/axet/bin /home/axet/bin --ro-bind /home/axet/.config /home/axet/.config --ro-bind /home/axet/.local/share/wineprefixes /home/axet/.local/share/wineprefixes --bind /home/axet/.cache/telegram-desktop /home/axet/.cache/telegram-desktop --bind /home/axet/.local/share/TelegramDesktop /home/axet/.local/share/TelegramDesktop --bind /home/axet/Downloads /home/axet/Downloads --bind /home/axet/Soft/telegram /home/axet/Soft/telegram --bind /home/axet/encfs/cfg.d/gamehere /home/axet/encfs/cfg.d/gamehere /home/axet/Soft/telegram/Telegram
ll ~/.local/share/applications/ | grep tele
-rwxrwxr-x 1 axet axet    612 сен 21 12:35 org.telegram.desktop._65f73dc27c0fb30cb1424daa92f46251.desktop

axet avatar Sep 21 '25 13:09 axet

It happened again. This time under strace. But for some reason strace cut in the middle. And I can not see full log. But what is worrying me is that telegram got executed out of bwrap script by bwrap parent process:

axet       56312   56309  0 17:44 pts/0    00:00:00 /bin/bash /home/axet/bin/telegram
axet       56361   56312  0 17:44 pts/0    00:00:00 /bin/bash /home/axet/bin/gamehere /home/axet/Soft/telegram/Telegram
axet       56399   56361  0 17:44 pts/0    00:00:00 bwrap --ro-bind / / --tmpfs /home --tmpfs /home/axet --tmpfs /tmp --tmpfs /var/tmp --tmpfs /run/user/1000 --unshare-pid --unshare-uts --unshare-cgroup-try --ro-bind /tmp/.X11-unix /tmp/.X11-unix --ro-bind /run/user/1000/pipewire-0 /run/user/1000/pipewire-0 --ro-bind /run/user/1000/pipewire-0-manager /run/user/1000/pipewire-0-manager --ro-bind /run/user/1000/pipewire-0-manager.lock /run/user/1000/pipewire-0-manager.lock --ro-bind /run/user/1000/pipewire-0.lock /run/user/1000/pipewire-0.lock --dev /dev --dev-bind /dev/dri /dev/dri --dev-bind /dev/snd /dev/snd --dev-bind /dev/ntsync /dev/ntsync --ro-bind /home/axet/bin /home/axet/bin --ro-bind /home/axet/.config /home/axet/.config --ro-bind /home/axet/.local/share/wineprefixes /home/axet/.local/share/wineprefixes --bind /home/axet/.cache/telegram-desktop /home/axet/.cache/telegram-desktop --bind /home/axet/.local/share/TelegramDesktop /home/axet/.local/share/TelegramDesktop --bind /home/axet /home/axet --bind /home/axet/ /home/axet/ --bind /home/axet/Soft/telegram /home/axet/Soft/telegram --bind /home/axet/encfs/data.d/gamehere /home/axet/encfs/data.d/gamehere /home/axet/Soft/telegram/Telegram
axet       56400   56399  0 17:44 pts/0    00:00:00 bwrap --ro-bind / / --tmpfs /home --tmpfs /home/axet --tmpfs /tmp --tmpfs /var/tmp --tmpfs /run/user/1000 --unshare-pid --unshare-uts --unshare-cgroup-try --ro-bind /tmp/.X11-unix /tmp/.X11-unix --ro-bind /run/user/1000/pipewire-0 /run/user/1000/pipewire-0 --ro-bind /run/user/1000/pipewire-0-manager /run/user/1000/pipewire-0-manager --ro-bind /run/user/1000/pipewire-0-manager.lock /run/user/1000/pipewire-0-manager.lock --ro-bind /run/user/1000/pipewire-0.lock /run/user/1000/pipewire-0.lock --dev /dev --dev-bind /dev/dri /dev/dri --dev-bind /dev/snd /dev/snd --dev-bind /dev/ntsync /dev/ntsync --ro-bind /home/axet/bin /home/axet/bin --ro-bind /home/axet/.config /home/axet/.config --ro-bind /home/axet/.local/share/wineprefixes /home/axet/.local/share/wineprefixes --bind /home/axet/.cache/telegram-desktop /home/axet/.cache/telegram-desktop --bind /home/axet/.local/share/TelegramDesktop /home/axet/.local/share/TelegramDesktop --bind /home/axet /home/axet --bind /home/axet/ /home/axet/ --bind /home/axet/Soft/telegram /home/axet/Soft/telegram --bind /home/axet/encfs/data.d/gamehere /home/axet/encfs/data.d/gamehere /home/axet/Soft/telegram/Telegram
axet       56401   56400 14 17:44 pts/0    00:00:22 /home/axet/Soft/telegram/Telegram

As you can see PID 56400 (bwrap) is a parent for 56401 native telegram process running not under bwrap. Log for 56400 cut in the middle with following:

getdents64(5, 0x561ccf5f20c0 /* 10 entries */, 32768) = 240
close(3)                                = 0
close(100)                              = 0
close(101)                              = 0
getdents64(5, 0x561ccf5f20c0 /* 0 entries */, 32768) = 0
close(5)                                = 0
wait4(-1, 

I do not see any of: fork, execv, 56401, dbus references in the log.

Begging of the 56401 log is:

set_robust_list(0x7fa7e481e520, 24)     = 0
rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
close(3)                                = 0
rt_sigprocmask(SIG_UNBLOCK, [CHLD], NULL, 8) = 0
execve("/home/axet/Soft/telegram/Telegram", ["/home/axet/Soft/telegram/Telegra"...], 0x7fff7a176b78 /* 70 vars */) = 0

I guess something forking and make telegram free. Anyway I do not think bwrap should be doing that.

axet avatar Nov 18 '25 15:11 axet