posted 2018-04-15
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).
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:
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.
An example exploit involving an init script is,
/etc/init.d/icinga2 start
to start the daemon.
/etc/init.d/icinga2 stop
to stop the daemon
while I investigate the weird behavior resulting from the hack.
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.