Why screen lockers on X11 cannot be secure

Today we released Plasma 5.2 and this new release comes with two fixes for security vulnerabilities in our screen locker implementation. As I found, exploited, reported and fixed these vulnerabilities I decided to put them a little bit into context.

The first vulnerability concerns our QtQuick user interface for the lock screen. Through the Look and Feel package it was possible to send the login information to a remote location. That’s pretty bad but luckily also only a theoretical problem: we have not yet implemented a way to install new Look and Feel packages from the Internet. So we found the issue before any harm was done.

The second vulnerability is more interesting as it is heavily related to the usage of X11 by the screen locker. To put this vulnerability into context I want to discuss screen lockers on X11 in general. In a previous post I explained that a screen locker has two primary tasks:

  1. Blocking input devices, so that an attacker cannot interact with the running session
  2. Blanking the screen to prevent private information being leaked

From the first requirement we can also derive a requirement that no application should get the input events except the lock screen and that the screen gets locked after a defined idle time. And from the second requirement we can derive that no application should have access to any screen content while the screen is being locked.

With these extended requirements we are already getting into areas where we cannot have a secure screen locker on X11. X11 is too old and too insecure to make it possible to fulfill the requirements. Why is that the case?

X11 on a protocol level doesn’t know anything of screen lockers. This means there is no privileged process which acts as the one and only screen locker. No, a screen locker is just an X11 client like any other (remote or local) X11 client connected to the same X server. This means the screen locker can only use the core functionality available to “emulate” screen locking. Also the X server doesn’t know that the screen is locked as it doesn’t understand the concept. If the screen locker can only use core functionality to emulate screen locking then any other client can do the same and prevent the screen locker from locking the screen, can’t it? And yes that is the case: opening a context menu on any window prevents the screen locker from activating.

That’s quite a bummer: any process connected to the X server can block the screen locker. Even more it could fake your screen locker. How hard would that be? Well I asked that question myself and needed about half an hour to implement an application which looks and behaves like the screen locker provided by Plasma 5. This is so trivial that I don’t see a point in not sharing the code:

#include <QGuiApplication>
#include <QQuickView>
#include <QQmlContext>
#include <QScreen>
#include <QStandardPaths>
#include <QtQml>

class Sessions : public QObject
{
    Q_OBJECT
    Q_PROPERTY(bool startNewSessionSupported READ trueVal CONSTANT)
    Q_PROPERTY(bool switchUserSupported READ trueVal CONSTANT)
public:
    explicit Sessions(QObject *parent = 0) : QObject(parent) {}
    bool trueVal() const { return true; }
};

int main(int argc, char **argv)
{
    QGuiApplication app(argc, argv);

    const QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
        QStringLiteral("plasma/look-and-feel/org.kde.breeze.desktop/contents/lockscreen/LockScreen.qml"));

    qmlRegisterType<Sessions>("org.kde.kscreenlocker", 1, 0, "Sessions");
    QQuickView view;
    QQmlContext *c = view.engine()->rootContext();
    c->setContextProperty(QStringLiteral("kscreenlocker_userName"),
                          QStringLiteral("Martin Graesslin"));
    c->setContextProperty(QStringLiteral("kscreenlocker_userImage"), QImage());
    view.setFlags(Qt::BypassWindowManagerHint);
    view.setResizeMode(QQuickView::SizeRootObjectToView);
    view.setSource(QUrl::fromLocalFile(file));
    view.show();
    view.setGeometry(view.screen()->geometry());
    view.setKeyboardGrabEnabled(true);
    view.setMouseGrabEnabled(true);

    return app.exec();
}

#include "main.moc"

This looks like and behaves like the real screen locker, but it isn’t. A user has no chance to recognize that this is not the real locker. Now if it’s that simple to replace the screen locker why should anyone go a complicated way to attack the lock screen? At least I wouldn’t.

And is there nothing which could be done to protect the real locker? Well obviously a good idea is to mark the one and only screen locker as authentic. But how to do that in a secure way on X11? We cannot e.g. show a specific user selected image. This would conflict with another problem with screen lockers on X11: it’s not possible to prevent that other windows grab screen content. So whatever the screen locker displays is also available to all other X11 clients. Also the window manager cannot help like preventing fullscreen windows to open fullscreen as can be seen in the code fragment above: it’s possible to bypass the window manager. Built in feature by X11.

Many of these issues could be considered as non-problematic using the old pragma of “if it runs, it’s trusted”. While I personally disagree, it just doesn’t hold for X11. If only clients of one user were connected to the X server one could say so. But X11 allows clients from other users and even remote clients. And this opens a complete new problem scope. Whenever you use ssh -X you open up your local system to remote attack vectors. If you don’t control the remote side it could mean that the client you start is modified in a way to prevent your screen from locking or to install a fake locker. I know that network transparency is a feature many users love, but it’s just a security night mare. Don’t use it!

Overall we see that attacking a screen locker or preventing that it opens up is really trivial on X11. That’s an inherent problem on the architecture and no implementation can solve them, no matter what the authors tell how secure it is. Compared to these basic attack vectors the vulnerability I found is rather obscure and it takes a considerable amount of understanding how X11 works.

Nevertheless we fixed the issue. And interestingly I chose to use the technology which will solve all those problems: Wayland. While we don’t use Wayland as the windowing system we use a custom domain-specific Wayland-based protocol for the communication between the parts our screen locker architecture. This uses the new libraries developed for later usage in kwin_wayland.

As we are speaking of Wayland: how will Wayland improve the situation? In the case of Plasma the screen locker daemon will be moved from ksmserver to kwin, so that the compositor has more control over it. Screen locking is a dedicated mode supported by the compositor. Whether a context menu is open or not doesn’t matter any more, the screen can be locked. Also the compositor controls input events. If it has the knowledge that the screen is locked, it can ensure that no input goes to the wrong client. Last but not least the compositor controls taking screenshots and thus can prevent that clients can grab the output of the lock screen.

17 Replies to “Why screen lockers on X11 cannot be secure”

  1. It would be useful to be able to know when a screensaver was active, so that you could run a cleanup/file transfer/scanner job while your machine was otherwise idle. Is there a flag somewhere in proc or sys?

      1. I wonder if you could set up a system where each user would have their own separately encrypted home which would be locked with that dbus signal on screen lock and unlocked on on login

  2. I’m running KDE because its the default DE for Qubes OS. The graphics virtualization in Qubes would appear to resolve all or most of your concerns about screen lockers and X11 security in general. Isolating X11 from these kinds of exploits is one of the ways Qubes hardens the hypervisor/VMs for the PC desktop threat model.

    Since the rest of Linux (and Windows) is insecure due to its marriage of security responsibilities with a monolithic kernel and large attack surface, ingrained virtualization is the most effective way to deal with “all of the above”. You get to keep all that lovely complexity that desktop environments promote, but its all managed behind simple and small VM interfaces.

  3. I am going to have to completely disagree with your statement about network transparency for GUIs. A security nightmare? Perhaps, but no more then:

    1. Network file servers
    2. Running software you didn’t compile yourself
    3. Running software you didn’t write yourself
    4. Using hardware you didn’t build yourself
    etc…

    Yes, when you allow remote graphics clients, you have increased the attack surface of your computing environment
    and you should be aware of that. Whether or not doing this is going to be a good or bad idea is going to depend on the particular circumstances involved. The only completely “secure” computer is one that is never used. All other scenarios require risk/benefit analysis.

    1. Um. The fact that “X is worse than Y” does not mean that Y isn’t bad.

      The way “network transparency” will be done in Wayland is *way* simpler and more secure than how it works in X11 where the remote system can basically do *anything* to your display, including displaying a fake lock screen. In “remote wayland” the worst thing it’ll be able to do is the push the wrong pixels to its own framebuffer.

      So there :).

  4. where should i report bugs with screenlocker to?

    it locked my screen while i was watching a movie using mplayer (started from konsole)

    1. That is a “bug” in mplayer. AFAIR the mplayer developers just gave up implementing all the different protocols to notify the screensaver of a running movie player. For a long time, there wasn’t an unified way to tell all screensavers this intend, so mplayer just removed the code alltogether. See options –stop-screensaver and –heartbeat-cmd of mplayer (and mplayer2, and mpv)

Comments are closed.