michael orlitzky

CVE-2018-6536: Icinga2 privilege escalation via PID file manipulation

Product
Icinga2
Vendor
NETWAYS GmbH
Versions affected
2.8.1 and earlier
Published on
2018-04-15
Author
Michael Orlitzky
Fixed in
commit c6b8668 , version 2.8.2
Bug report
https://github.com/Icinga/icinga2/issues/5991
MITRE
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-6536

Summary

The icinga2 daemon creates its PID file after dropping privileges to a non-root user. That may be exploited by the unprivileged user to kill root processes, since when the daemon is stopped via its init script, root sends a SIGINT to the contents of the PID file (which are under the control of the runtime user).

Details

The purpose of the PID file is to hold the PID of the running daemon, so that later it can be stopped, restarted, or otherwise signaled (many daemons reload their configurations in response to a SIGHUP). To fulfil that purpose, the contents of the PID file need to be trustworthy. If the PID file is writable by a non-root user, then he can replace its contents with the PID of a root process. Afterwards, any attempt to signal the PID contained in the PID file will instead signal a root process chosen by the non-root user.

This is commonly exploitable through init scripts that are run as root and which blindly trust the contents of their PID files. The icinga2 package ships such an init script as etc/initsystem/icinga2.init.d.cmake, containing the (slightly edited) function:

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
stop() {
  printf "Stopping Icinga 2: "

  # Boring stuff removed

  pid=`cat $ICINGA2_PID_FILE`

  if kill -INT $pid >/dev/null 2>&1; then
    for i in 1 2 3 4 5 6 7 8 9 10; do
      if ! kill -CHLD $pid >/dev/null 2>&1; then
        break
      fi

      printf '.'

      sleep 3
    done
  fi

  if kill -CHLD $pid >/dev/null 2>&1; then
    kill -KILL $pid
  fi

  echo "Done"
}

The PID is obtained from the untrusted $ICINGA2_PID_FILE, after which a SIGINT is sent to it as root.

Exploitation

An example exploit involving an init script is,

  1. I run /etc/init.d/icinga2 start to start the daemon.
  2. icinga2 drops to the icinga user.
  3. icinga2 writes its PID file, now owned by the icinga user.
  4. Someone compromises the daemon, which sits on the network.
  5. The attacker is generally limited in what he can do because the daemon doesn't run as root. However, he can write “1” into the PID file, and he does.
  6. I run /etc/init.d/icinga2 stop to stop the daemon while I investigate the weird behavior resulting from the hack.
  7. The machine reboots, because I killed PID 1 (this is normally restricted to root).

Resolution

The icinga2 program gained a new power, icinga2 internal signal, that will send a signal as its unprivileged runtime user. This new ability is used in the init script whenever a signal is sent.