On Practical Qt Security

At 30C3, Ilja van Sprundel gave a talk on X Security. In this talk, he also discussed Qt security matters, specifically how running a setuid binary which links against Qt is unsafe due to exploitable bugs in the Qt code base (citing the infamous setuid practice in KPPP). While his points are valid, he misses the greater picture: Qt was not designed for use in setuid applications! Consequently there are a lot of ways the security of a Qt application can be compromised when it runs as root. So I went on to discuss this issue with QtNetwork maintainer Richard Moore, and we both agree that in contrary to Iljas claim, we do need to dictate policy. So here it goes:

Do not ship Qt applications that require setuid. While the same is probably true for any other toolkit, we have only discussed this for Qt in more depth. Actually, Rich has prepared a patch for Qt 5.3 that will quit if you try to run an application setuid unless you ask it to. This should make it harder to shoot yourself into the foot.

While making QtCore and QtNetwork safe for setuid use is possible, they currently are not. If you absolutely have to (and you really shouldn’t), at least unset QT_PLUGIN_PATH and LD_LIBRARY_PATH in main(). The latter is required because even though LD_LIBRARY_PATH is ignored by the linker for setuid binaries, it is used internally by QtNetwork unconditionally to look for OpenSSL. Of course, you also need to follow all the other best practices (note that even this list is incomplete, e.g. it doesn’t mention to close FDs).

However, there are also situations where a Qt application running as user can be unsafe, so to those who ship their own Qt build to their customers, there are even more policies:

  • Never build Qt so its prefix is a publicly writable directory, such as /tmp: Suppose you build a in-source (developer) build in /tmp/qt, then Qt will go ahead and look for plugins in /tmp/qt/plugins. A malicious user could simply provide a fake style there that next to calling the style which the user would expect (e.g. via QProxyStyle) executes arbitrary malicious code. The same goes for Image IO plugins, which are handled in QtCore.
  • Never build Qt so its prefix is a home directory: This one is more tricky and a lot harder/unlikely to exploit, but it’s a valid attack vector nonetheless: Suppose Joe Coder compiles Qt in-source on /home/joe/dev/qt. Now every customer needs to make sure that a local user by the same name is a really nice person.

So in conclusion, a better summary of the above would be:

Never distribute binaries built with a prefix that is a non-system directory!

If you already have this setup, but need a hotfix, there is hope: libQtCore.so contains strings starting in qt_plugpath= and qt_libspath=. Both are padded to 1024 bytes. Adding a binary null after the first / keeps Qt from looking for loadable code in user accessible locations.

TL;DR: The bugs Ilja points out are valid, but only affect applications that don’t follow good practice. We will attempt to make it harder for developers to make these mistakes, but writing suid applications isn’t something that will ever be recommended, or easy to do safely. Apart from the suid issue however, there are more traps lingering if you provide your own Qt and build it in an unsafe way.

Further reading: Google+ discussion on the topic.
Acknowledgements: Richard Moore for contributing vital information to this document, Thiago Macieira for proof-reading.

Update: Clarified the wording to ensure it’s clear that a prefix is meant. Thanks, Ian.

Update 2: As Rich and David Faure pointed out, KPPP is dropping permissions before calling Qt code, and KApplication already has a setuid safeguard in place.

Update 3: Richs setuid check has been merged.

10 Comments on “On Practical Qt Security

  1. You had me scared there for a second! Just ran a `strings` on the Qt I distribute and it checks out fine.

    Where you build Qt is irrelevant if you have a Qt prefix set, as you indicate in the italic text. Obviously it would be ridiculous if you weren’t allowed to build Qt in a home directory.

    • I was speaking of developer builds, which is a common way to build a shipped Qt. Sorry if that wasn’t clear.

  2. An update on this, KPPP actually does things right and drops the setuid before it starts doing anything dangerous such as creating the QApplication.

    • However, Ilja also explained why this is still problematic. After all, access to priviledges resources may stll be available to the application (as that’s the whole point of making it setuid), which could still be exploited.
      In any case, don’t we have PolicyKit for such things nowadays?

      • They aren’t – he needs to look at the code. It’s basically a helper app that’s suid it just happens to be in the same binary and launched via fork.

        KPPP long predates polkit btw!

  3. “The bugs Ilja points out are valid, but only affect applications that don’t follow good practice.”

    You’re admitting that there are security issues, but instead of fixing them, you’re trying to shift the blame.

    If QT doesn’t support setuid safely, it should check at run-time whether the process is running setuid, and abort with an error message (or at the very least emit a big fat warning) if it is.

    • Which is exactly what Qt now does, and what KDE has always done, as mentioned in the blog post.

    • “You’re admitting that there are security issues, but instead of fixing them, you’re trying to shift the blame.”

      Quite the reverse – we never supported setuid, and we’ve shown that the only application that found to be setuid has understood this and taken the steps to be secure by avoiding hitting any Qt code whilst having elevated privileges. In fact unlike Ilja we’ve actually gone back to the kppp code and checked it. If he’d done that, he’d have seen that the kppp example doesn’t work for his talk.

      Even though we haven’t found any other applications that are suid we’ve added a check to prevent the (so far hypothetical) developer who is both knowledgeable enough to write suid code and foolish enough to use a big development framework when doing so.