COMMAND
MySQL
SYSTEMS AFFECTED
All MySQL affecting versions 3.22.29 and earlier,3.23.8 and earlier
PROBLEM
Viktor Fougstedt found following. While installing MySQL 3.22.29
and testing it out, he discovered a very serious bug in it's
handling of the GRANT statement. This was tested on MySQL
3.22.27, 3.22.29, 3.23.8. Anyone with access to a running MySQL
and GRANT privilege for any database or table in it, can change
any MySQL-password he wishes, including the MySQL superuser's.
If the malicious user has access to run processes on the machine
where MySQL is running, he can hijack the entire database. If he
does not have such access, he can DOS the server by setting the
MySQL superuser's password to a random string. The 'test'-users
installed by MySQL's install scripts have GRANT privileges for
any database whose name begins with 'test', and can therefore be
used to exploit this bug. The 'test' accounts by default have no
passwords set, and no restrictions on where connects can come
from.
This makes all default-configured MySQL very vulnerable (anyone
on the net can change your MySQL superuser password). Be aware,
however, that _any_ user with a GRANT privilege (no matter on
which database) in your MySQL installation can exploit this bug.
You may be vulnerable even if you've removed the 'test' users.
The bug is that the GRANT statement does not properly check
privileges when you give it an IDENTIFIED BY-clause. You can
therefore GRANT someone (including the MySQL superuser) a
privilege you yourself possess, and set her/his password at the
same time using IDENTIFIED BY. Which privilege you pass on does
not matter. It is the side-effect of the IDENTIFIED BY that does
the magic. This can be exploited regardless of your other
permissions. You only need the GRANT privilege for _any_ table or
database to exploit this bug.
For someone having login access to the machine running MySQL,
hijacking the database is trivial once the MySQL superuser
password has been changed using the above method. For someone
without login access, changing the superuser password can be a
simple way of DOS:ing, or of extortion. In the default setup,
MySQL prohibits access to the superuser account from any other
hosts than localhost. Sites that allow superusers to connect from
the net may be vulnerable to hijacking from malicious users
without local access.
Since the password-less 'test'-accounts created by MySQL
installation scripts have GRANT privileges for any database whose
name begins with 'test', they can be used to exploit this bug.
Very nasty.
Exploit: Connect to mysql as any user with grant privileges for
any table. The default test users will do nicely. If no
databases has been created for the test user, do so. Then alter
roots (MySQL's roots, not the real roots!) password with a GRANT.
After the code below has been executed, the password of the MySQL
superuser 'root' will be 'newpassword'.
> mysql -utest -p
Password:
mysql> CREATE DATABASE test_expl;
Query OK, 1 row affected (0.04 sec)
mysql> GRANT select ON test_expl.* TO root@localhost IDENTIFIED BY 'newpassword';
Query OK, 0 rows affected (0.01 sec)
mysql> exit
Bye
SOLUTION
MySQL 3.22.30 has been released for all available platforms. To
quote the CHANGELOG, "Fixed critical problem with the WITH GRANT
OPTION option." While waiting for opportunity to install them:
1) Revoke _all_ GRANT privileges from _all_ users in your
MySQL system except root@localhost. This includes GRANTs
in the mysql.db table.
2) Confirm that your root@localhost password has not been
altered.
For those of you using source distributions here's a patch for the
problem from TCX:
*** /my/monty/master/mysql-3.23.8-alpha/sql/sql_parse.cc Fri Dec 31 13:53:03 1999
--- ./sql_parse.cc Mon Jan 10 21:53:59 2000
***************
*** 1222,1227 ****
--- 1222,1246 ----
tables ? &tables->grant.privilege : 0,
tables ? 0 : 1))
goto error;
+
+ /* Check that the user isn't trying to change a password for
another
+ user if he doesn't have UPDATE privilege to the MySQL database
*/
+
+ List_iterator <LEX_USER> user_list(lex->users_list);
+ LEX_USER *user;
+ while ((user=user_list++))
+ {
+ if (user->password.str &&
+ (strcmp(thd->user,user->user.str) ||
+ user->host.str && my_strcasecmp(user->host.str,
+ thd->host ? thd->host :
thd->ip)))
+ {
+ if (check_access(thd, UPDATE_ACL, "mysql",0,1))
+ goto error;
+ break; // We are allowed to
do changes
+ }
+ }
+
if (tables)
{
if (grant_option && check_grant(thd,
MySQL 3.22.32 fixes a couple of possible security holes in MySQL
3.22 including this one. 3.23.x is also out.