posted 2018-04-22
The mysqld daemon creates its PID file after dropping privileges to a non-root user typically named mysql. 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. The MySQL daemon creates its PID file after dropping its privileges, as the following code in src/mysqld.cc (MySQL v5.7.21) shows:
int mysqld_main(int argc, char **argv) {
...
#if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT)
if (locked_in_memory) // getuid() == 0 here
set_effective_user(user_info);
else
#endif
set_user(mysqld_user, user_info);
}
#endif // !_WIN32
...
/* Save pid of this process in a file */
if (!opt_bootstrap)
create_pid_file();
This is commonly exploitable through init scripts that are run as root and which blindly trust the contents of their PID files. The MySQL package itself ships a few such init scripts,
both of which execute code like the following:
stop() {
...
pid=$(cat "$pidfile")
# We use a signal to avoid having to know the root password
# Send single kill command and then wait
if kill $pid >/dev/null 2>&1; then ...
The PID is obtained from the untrusted $pidfile
, and a
SIGTERM
is sent to it as root.
An example exploit involving an init script is,
/etc/init.d/mysqld start
to start the daemon.
/etc/init.d/mysqld stop
to stop the daemon
while I investigate the weird behavior resulting from the hack.
The April 2018 MySQL Critical Patch Update was supposed to fix this issue, but no change has been made to the PID file handling. The PID file is still created as mysql, and the init scripts still call kill on its contents as root. Oracle has been notified of this in an email.
The October 2018 MySQL Critical Patch Update is again supposed to fix this, but all they've done in commit e1fdeb2 is add an Oracle-specific hack into the bundled init scripts. If you're not running SLES or Oracle's own brand of Linux, then it's still up to you to realize (a) that they're putting you at danger, and (b) that you have to find a distribution-specific workaround for it. Oracle has not been notified. I don't have time for this shit.