TL;DR: R*PATH
has an unfortunate history of introducing new ways of
running untrusted (attacker-controlled) libraries. RPATH
/RUNPATH
is usually avoidable and should be avoided.
Firstly, it might be worth reviewing the non-security reasons for why
we want these binaries flagged: distros (Eg. Debian Wiki on
RPATH) don't like that it takes precedence over the local
LD_LIBRARY_PATH
and /etc/ld.so.conf
settings/configs and
conventions. It makes it harder to reason about libraries through more
visible means than running readelf
for every binary we want to think
about, and it complicates life for maintainers who are juggling a lot
of intertwined dependencies through these other mechanisms which are
easily broken by RPATH
/RUNPATH
. But there are exceptions - here's
Debian's policy:
The only time a binary or shared library in a Debian package should set RPATH
or RUNPATH
is if it is linked to private shared libraries in the same package.
Secondly, relative RPATH
(i.e. foo.so
rather than
/usr/lib/foo.so
) can go quite wrong: your user might be running from
a working directory (Eg. /tmp
) that's subject to attacker control
who could plant a malicious version of /tmp/foo.so
. This was an
observation made in Debian bug #754278 against openjdk.
This might sound benign, but it can lead to privesc particularly on
setuid/setgid programs like IBM's DB2 privesc CVE-2014-0907:
simply run the suid binary from a directory primed to abuse whatever
RPATH
is set, and your code will run in the context of the
privileged program. Other examples include Slackware's packaging of
llvm CVE-2013-7171, Gentoo's packaging of Imagemagick
CVE-2005-3582, SuSE's packaging of CVSup CVE-2004-2133, ..).
It's been such a steady trickle that Apple's equivalent avoids relative RPATH in "restricted" (suid/sgid) binaries.
Thirdly, the special $ORIGIN
variable (calling executable path) has
been one way of avoiding an absolute path, while anchoring the search
somewhere. But it's had issues: glibc CVE-2011-0536 failed to
expand it properly which gave attackers an equivalent of the relative RPATH case where the author of an executable using $ORIGIN
thought they were getting something slightly more robust.
With that in mind, you probably really do want checksec.sh
to flag
RPATH
/RUNPATH
- it's not always bad, but checksec.sh
doesn't
have the bigger picture available to make that call for you like the
distro linters can, so it needs manual review.
Addendum: Breaking the links: Exploiting the linker by Tim Brown was mentioned in the replies to your Twitter conversation, this is a great paper on attack surface more generally for POSIX style linkers and astute readers should definitely take a look. The most pertinent thing there not covered already is this:
On GNU/Linux at least, when the DT_RPATH
or DT_RUNPATH
exists within the ELF headers of a binary then these will be honoured first when looking for shared libraries. Additionally, the keyword $ORIGIN
within this header is expanded to be the path of the directory where the object
is found, while both . and the empty directory specification are honoured, even for binaries with the setUID bit set. From an attackers pespective, setUID binaries with DT_RPATH
are particularly nice, since we can make use of hard links to manipulate the runtime linker into using an $ORIGIN
which we can control.
... as observed elsewhere in the paper, most systems ignore user-supplied environment variables such as LD_LIBRARY_PATH
on setuid binaries most of the time, but as you can see a careless RPATH
gives the attacker an equivalent gift.
2nd addendum: ContextIS have a great writeup covering RPATH attacks.