michael orlitzky

CVE-2018-25078: sys-apps/man-db root privilege escalation via setuid

posted 2023-02-10

Product
Gentoo Linux sys-apps/man-db package
Versions affected
2.8.4 and earlier
Published on
2023-02-10
Fixed in
commits afd4c6fd, 5e5e5187, and 5f96b31d
Bug report
https://bugs.gentoo.org/662438
MITRE
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-25078
Acknowledgements
Lars Wendler (polynomial-c) for the fix, and John Helmert (ajak) for requesting the CVE.

Summary

Prior to version 2.8.5, the Gentoo sys-apps/man-db package installed /usr/bin/mandb with owner:group man:man and setuid. The package also installed a cron job that would execute that file daily as root. Since the man user can modify the executable to do whatever he wants and also strip the setuid bit, he can gain root privileges every time the cron job is run.

Details

Before version 2.8.5, the sys-apps/man-db package would install /usr/bin/mandb setuid and owned by the man user (the same user who owns the cache):

1
2
3
4
5
6
7
8
src_configure() {
  export ac_cv_lib_z_gzopen=$(usex zlib)
  local myeconfargs=(
    --docdir='$(datarootdir)'/doc/${PF}
    --with-systemdtmpfilesdir="${EPREFIX}"/usr/lib/tmpfiles.d
    --enable-setuid
    --enable-cache-owner=man
    ...

This allows the cache to be regenerated by anyone without messing up its permissions, since they will be regenerating it as the man user. However, the situation is exploitable if a privileged user executes the file. As proof, we check that the man user is allowed to strip off the setuid bit, and then replace /usr/bin/mandb with a malicious script:

root # su man -s /bin/sh

man $ chmod 755 /usr/bin/mandb

man $ cat < bad-script.sh > /usr/bin/mandb

If root now executes /usr/bin/mandb, then the malicious script will be run, allowing man to gain root. This is exacerbated by the fact that the same package installs a nightly cron job to do just that (these all run as root):

1
2
3
4
5
6
7
8
9
10
11
#!/bin/sh

# Use same perms/settings as the ebuild.
cachedir="/var/cache/man"
if [ ! -d "${cachedir}" ]; then
	mkdir -p "${cachedir}"
	chown man:man "${cachedir}"
	chmod 0755 "${cachedir}"
fi

exec nice mandb --quiet

Thus there is an opportunity every day for the man user to alter the contents of /usr/bin/mandb, strip off its setuid bit, and gain root.

Resolution

The sys-apps/man-db ebuilds now pass --disable-setuid to man-db's ./configure script so that the executable is installed sans setuid. Since typically only root will be regenerating the cache, he is now responsible for dropping privileges. For example, the modern cron job contains,

1
exec su man -s /bin/sh -c 'nice mandb --quiet' 2>/dev/null

which will safely drop privileges before regenerating the cache.

Appendix

There were two reasons why the cron job was running as root in the first place:

  1. Most cron implementations support placing job entries in /etc/cron.d, but back in 2018, Gentoo still had one (vixie-cron) that didn't. Consequently the only way for a package to install a daily cron job was to put it in /etc/cron.daily, which Gentoo supports by default, but which runs its jobs as root.
  2. Gentoo considers anything under /var/cache to be transient. (That's a mistake, but decreasingly relevant.) If /var/cache/man was removed, then the cron job might have needed to recreate it—and that requires root.

Neither roadblock remains. Vixie-cron was removed a long time ago. Gentoo has also given up support for non-linux platforms, requiring systemd-tmpfiles to create temporary directories. As a result, a tmpfiles.d entry could be used to (re)create /var/cache/man, and a job dropped in /etc/cron.d could launch the man-db regeneration process directly as the man user. That would avoid the awkward su that has been annoying users ever since (bug #717128, bug #832182).