posted 2017-09-21
The p3scan daemon creates its PID file after dropping privileges to a non-root user. That may be exploited (through init scripts or other management tools) by the unprivileged user to kill root processes, since when the daemon is stopped, root usually sends a SIGTERM to the contents of the PID file (which are under the control of the runtime user). P3Scan itself ships two init scripts vulnerable to this attack.
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.
The creation of the PID file can be traced to the following code in p3scan.c (modulo some whitespace changes):
cuid=getuid();
if (cuid == 0) {
do_log(LOG_DEBUG, "Changing uid (we are root)");
pws = getpwnam(config->runasuser);
// Emergency: main: Unknown user in configuration file
if (pws == NULL)
do_log(LOG_EMERG,
"ERR: Unknown User '%s'",
config->runasuser);
if (setgid(pws->pw_gid))
do_log(LOG_DEBUG,
"setgid returned: %s",
strerror(errno));
if (setuid(pws->pw_uid))
do_log(LOG_DEBUG,
"setuid returned: %s",
strerror(errno));
}
cuid=getuid();
guid=getgid();
pws = getpwuid(cuid);
grp = getgrgid(guid);
do_log(LOG_DEBUG,
"Running as user: %s group: %s",
pws->pw_name,
grp->gr_name);
if ((pd=fopen(config->pidfile, "w+")) != NULL) {
fprintf(pd, "%i\n", getpid());
fclose(pd);
}
This is commonly exploitable through init scripts that are run as root and which blindly trust the contents of their PID files. The p3scan package ships two such init scripts,
both containing essentially the same code:
stop)
# Stop p3scan
if [ -a /var/run/p3scan/p3scan.pid ]; then
kill `cat /var/run/p3scan/p3scan.pid` &>/dev/null
rm -f /var/run/p3scan/p3scan.pid
echo "P3Scan terminated"
P3Scan is unable to run in the foreground, preventing the typical workaround where the init system is allowed to manage its PID file.
An example exploit involving an init script is,
/etc/init.d/p3scan start
to start the daemon.
/etc/init.d/p3scan stop
to stop the daemon
while I investigate the weird behavior resulting from the hack.
To mitigate the risk, the POSIX ps command can be used to verify the PID data. You can get the user of the process whose PID you find with
root # ps -p <pid> -o user=
and you can get the name of the command with
root # ps -p <pid> -o comm=
Init script authors should check the output of those two command
against the expected values before sending a signal to a running
process. That will eliminate the most serious scenarios (where the
attacker kills, for example, the firewall), but still leaves open
the possibility that the attacker can prevent
/etc/init.d/p3scan stop
from terminating his
compromised process by entering junk into the PID file.