I previously wrote about methods for running untrusted code on a Linux workstation, with bare-metal performance and convenient local access to the build tree. Probably the best method for doing this is to use schroot. But by default, processes running under schroot still have access to the host’s X server, and can do things like keystroke logging and screenshot capture.
This is quite a nasty error on my part, it means the system I’ve been using for the last two years doesn’t actually meet one of my main security goals. So I think a post-mortem is in order.
Linux provides a concept of “abstract sockets”, which are named sockets which exist outside of the filesystem. So the set of abstract sockets is shared between processes with different root filesystems.
In March 2008, Adam Jackson added abstract socket support to the X.org server, based on a patch by Bill Crawford. The rationale for this was unclear at the time, but in September when client support was added, Adam Jackson explained to the Xcb mailing list that “the main advantages [of abstract sockets] are that they work without needing access to /tmp”.
So from the original introduction of the feature, it was acknowledged that the rationale was to bypass security controls.
In 2010, Jan Chadima applied the same rationale when he requested that the feature be added to OpenSSH’s X forwarding (bug #1789). He explained that “this is useful when the selinux rules prevents the /tmp directory”. Here we had the first critical evaluation of the security of the feature, from Damien Miller who wrote:
Isn’t the solution for SELinux rules breaking /tmp to fix the SELinux rules? Abstract sockets look like a complete trainwreck waiting to happen: a brand new, completely unstructured but shared namespace, with zero intrinsic security protections (not even filesystem permissions) where every consumer application must implement security controls correctly, rather than letting the kernel do it.
In 2014, Keith Packard proposed to have the X.org server stop listening on regular UNIX sockets by default, relying entirely on abstract sockets. The problem of OpenSSH’s non-compliance was raised, so he suggested:
Perhaps someone with a clue about the security implications of using abstract sockets vs file system sockets might chime in and explain why using abstract sockets is safer than file system sockets…
Cue cricket chirping noise.
The issue is known to other people who have tried to sandbox processes on hosts with an X server. The developers of a browser-sandboxing system called Firejail wrote in February 2016:
The only way to disable the abstract socket @/tmp/.X11-unix/X0 is by using a network namespace. If for any reasons you cannot use a network namespace, the abstract socket will still be visible inside the sandbox. Hackers can attach keylogger and screenshot programs to this socket.
This, thankfully, does not appear to be true. You can use the X server command line parameter -nolisten local to prevent your X server from listening on the abstract socket. The UNIX socket transport (called “unix” in the X command line) will still be enabled, and all applications will use it instead.
For plain xinit, this means having a /etc/X11/xinit/xserverrc containing something like
#!/bin/sh exec /usr/bin/X -nolisten tcp -nolisten local "$@"
For LightDM, you can create a file called e.g. /etc/lightdm/lightdm.conf.d/50-no-abstract.conf with contents:
[Seat:*] xserver-command=X -nolisten local
At least, it works for me. You can test it within the chroot using:
socat ABSTRACT-CONNECT:/tmp/.X11-unix/X0 -
This should report “Connection refused”.