Dealing with insecure runpaths

Somehow, lately I’ve had a few more inquiries about runpaths than usual — all for different packages, which makes it quite a bit more interesting. Even though not all the questions regarded Gentoo’s “insecure runpaths” handling, I think it might be well worth it to write a bit more about it. Before going into details about it, though, I’ll point you to my previous post for a description of what runpaths (and rpath) are, instead of repeating it all here.

So, what is an insecure runpath? Basically any runpath set to a directory where an attacker may have control is insecure, because it allows an attacker to load an arbitrary shared object which can then run arbitrary code. The definition of attacker, as I already discussed is flexible depending on the situation.

Since runpaths are, as the name suggest, paths on the filesystem, there are two main starting points that would cause a path to become insecure: runpaths derived from the current working directory, and runpaths derived from any world-writable directory. The ability for an attacker to place the correct object in the correct path varies considerably, but it’s a good rule of thumb to consider the both of them just as bad. What happens for packages built in Gentoo often enough, is for the runpath to include the build directory in its runpath, which could be either /var/tmp or /tmp (if you, for instance, build in tmpfs — please tell me you’re not using /dev/shm for that!).

Depending on how your system is set up, this might not be as insecure as it might sound at first, because for instance in my laptop, /var/tmp/portage is always present, and always owned by the portage user, which makes it less vulnerable to a random user wanting to move there a particularly nasty shared object… on the other hand, assuming that it’s secure enough is the wrong move, full stop.

Before I start panicking people — Portage is smarter than you think and it has been, by default, stripping insecure runpaths for a while. Which means that you don’t have to fret about fixing the issues when you see the warning — but you really should look into fixing them for good. I would also argue that since Portage already strips the runpaths at install time, you shouldn’t just use chrpath to remove the insecure paths after the build, but you should either fix it properly or leave it be, in my opinion. This opinion might be a bit too personal, as I don’t know if pkgcore or paludis support the same kind of fixing.

So let’s go back to the two sources of insecurity in the runpaths. In the case of a current directory being the base for the insecure path, which means having an rpath of either . or ../something, this is usually bad logic in the way the package tries to set its rpath, as instead of the current work directory, the developers most likely wanted to refer to where the executable itself is installed, which is, instead, $ORIGIN literally. Another common situation is for that literal to be treated as a variable name, so that $ORIGIN/../mylibs becomes something like /../mylibs which is also wrong.

But a much more common situation arises when our build directory is injected into the runpath. Something along the lines of /var/tmp/portage/pkg-category/package-0.0.0/image/usr/lib64/mypkgslibs — more rarely it would point to the build directory rather than the image directory. In many cases this happens because the upstream build system does not know about, or mishandles, the DESTDIR variable.

The DESTDIR variable is commonly used to install a software package at a given offset — binary distributions will then generate a binary package out of it, source distributions like Gentoo will then merge the installed copy to the live filesystem after recording which files have been installed. In either case, the understanding behind this variable is that the final location of the executables will not include it. Unfortunately not all build systems do support it, so in some cases we end up doing something a bit more hackish by replacing /usr with ${D}/usr in the definition of the install prefix. The prefix is, though, commonly used to identify where the executables will be at the end, so it would be possible for a build system to have, in the parameters, -rpath ${prefix}/lib/mylibs which would then inject ${D} on the runpath.

As you can see, for most common situations it’s a matter of getting upstream to fix their build system. In other cases, the problem is that the ebuild is installing files without going through the build system’s install phase, which, at least with libtool, would often re-link the object files to make sure the rpath is handled correctly.

Beside this, there isn’t much more I can add, I’m afraid.

Exit mobile version