Bug #44311 privilege escalation
Submitted: 16 Apr 2009 9:41 Modified: 29 Jul 2009 21:27
Reporter: Peter Laursen (Basic Quality Contributor) Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Security: Privileges Severity:S2 (Serious)
Version:5.1.33 OS:Windows (Vista 32 bit)
Assigned to: CPU Architecture:Any
Tags: qc

[16 Apr 2009 9:41] Peter Laursen
Description:
I have a user with *only* SELECT privilege to specific tables.  The user can successfully SELECT from all tables in the database.

How to repeat:
SELECT CURRENT_USER;

/*
current_user      
------------------
testuser@localhost
*/

SHOW GRANTS FOR testuser@localhost;

/*
Grants for testuser@localhost                              
-----------------------------------------------------------
GRANT USAGE ON *.* TO 'testuser'@'localhost'               
GRANT SELECT ON `test`.`booltest` TO 'testuser'@'localhost'
GRANT SELECT ON `test`.`bltest` TO 'testuser'@'localhost'  
*/

SHOW TABLES FROM test;

/*
Tables_in_test 
---------------
_tmp_newtable  
a              
bltest         
booltest       
chsettest      
chtest         
modetest       
numbers        
sorttest       
t1             
t2             
tab1           
tablename1     
tablename1_copy
utf8test 
*/

SELECT * FROM booltest;

/*

    id       b     t  
------  ------  ------
     1       0       0
     2       1       1
     3       0       0
     4       1       1

*/ 

Suggested fix:
Last statement "SELECT * FROM booltest;" should return a privileges related error.
[16 Apr 2009 9:44] Peter Laursen
oops .. my mistake when posting!

"SELECT .. FROM booltest" is OK of course, but

SELECT * FROM chsettest;

/* returns

    id  txt   
------  ------
     1  (NULL)
     2  b     
     3  a     
     4  a     
     5  (NULL)
*/  

There is no SELECT privilege to 'chsettest' table for this user.
[16 Apr 2009 9:47] Peter Laursen
However trying FROM another DATABASE ON same SERVER

SELECT * FROM test2.bittest

/*
Error Code : 1142
SELECT command denied to user 'testuser'@'localhost' for table 'bittest'
*/
[16 Apr 2009 10:49] Valeriy Kravchuk
So, had you tried the same trick with another database besides test? Do you have privileges escalation outside of test?
[16 Apr 2009 10:56] Peter Laursen
I do not see any escalation outside 'test' database, but have not checked all possible combinations of statements and tables of course!

btw: SHOW CREATE TABLE shows same pattern

SHOW CREATE TABLE test.chsettest; -- success
SHOW CREATE TABLE test1.bittest;  -- SELECT command denied to user 'testuser'@'localhost' for table 'bittest'
[16 Apr 2009 11:17] Valeriy Kravchuk
Please, check the results of:

select user, db from mysql.db;

Do you have test database mentioned there?
[16 Apr 2009 11:29] Peter Laursen
I do not see any escalation outside 'test' database, but have not checked all possible combinations of statements and tables of course!

btw: SHOW CREATE TABLE shows same pattern

SHOW CREATE TABLE test.chsettest; -- success
SHOW CREATE TABLE test1.bittest;  -- SELECT command denied to user 'testuser'@'localhost' for table 'bittest'
[16 Apr 2009 11:32] Peter Laursen
-- as root
SELECT USER, db FROM mysql.db;

/*

USER    db     
------  -------
        test   
        test\_%
        
 */
[16 Apr 2009 11:37] Peter Laursen
Additionally I can tell I dropped an anonymous user yesterday (I would normally not have such, but as usual the config wizard failed to 'apply security settings' when I originally installed the server.  I believe this server was originally installed as 5.1.31 or 5.1.32 and later upgraded)
[16 Apr 2009 11:51] Peter Laursen
full details from mysql.db table:

Host    Db       User    Select_priv  Insert_priv  Update_priv  Delete_priv  Create_priv  Drop_priv  Grant_priv  References_priv  Index_priv  Alter_priv  Create_tmp_table_priv  Lock_tables_priv  Create_view_priv  Show_view_priv  Create_routine_priv  Alter_routine_priv  Execute_priv  Event_priv  Trigger_priv
------  -------  ------  -----------  -----------  -----------  -----------  -----------  ---------  ----------  ---------------  ----------  ----------  ---------------------  ----------------  ----------------  --------------  -------------------  ------------------  ------------  ----------  ------------
%       test             Y            Y            Y            Y            Y            Y          N           Y                Y           Y           Y                      Y                 Y                 Y               Y                    N                   N             Y           Y           
%       test\_%          Y            Y            Y            Y            Y            Y          N           Y                Y           Y           Y                      Y                 Y                 Y               Y                    N                   N             Y           Y
[16 Apr 2009 12:36] Peter Laursen
I hav e exactly same situation wiht 5.0.77

-- as root
GRANT USAGE ON *.* TO 'testuser'@'localhost' ;              
GRANT SELECT ON `test`.`uctf8test` TO 'testuser'@'localhost';
SELECT USER, db FROM mysql.db
/* returns

user    db     
------  -------
        test   
        test\_%
        
*/

-- as testuser
SELECT * FROM utf8test; 
-- success!

This machine is 6-8 weeks only only.  5.0, 5.1 and 6.0 servers were installed after a few days.  I upgrade regularly when I see upgrades at the server - so those where upgrades were released later have been upgraded.

If this is caused by the Windows config wizard failure to 'apply security settings', then the related bug(s) with the wizard should be 'promoted' to 5.0 and 5.1 and given priority (I think they are currently set to be fixed in 6.0 only)
[16 Apr 2009 13:16] Peter Laursen
OK .. maybe only now I understand Valeriy's rist question to me.  It is only reproducable inside 'test' database!

-- as root
CREATE DATABASE newthing;
USE newthing;
CREATE TABLE t1(id INTEGER);
CREATE TABLE t2(id INTEGER);
GRANT USAGE ON *.* TO 'newuser'@'localhost';              
GRANT SELECT ON `newthing`.`t1` TO 'newuser'@'localhost';

-- as newuser
USE newthing;
SELECT * FROM t1; -- success
SELECT * FROM t2; -- SELECT command denied to user 'newuser'@'localhost' for table 't2'
SELECT * FROM test.booltest; -- success

.. so any user seems to have privilege to all tables in `test` database. This is not how I understand it should be! "SELECT user, db .." returned an empty string and not a wildcard (%) for both 'user's. And GRANT informed nothing about it either!

Also databases of the pattern test_% (like `test_100`) are affected (what is expected from the output of "SELECT .. FROM db, user"). If I delete from mysql.db, flush privileges and connect again as newuser I get expected results.

Issues as I understand this:

1) an empty string for user in mysql.db table is treated like a wildcard (%)
2) SHOW GRANTs and what user really can do are in conflict
3) possibly it all started with config wizard failure.  But even if that is the case this is not the primary issue!
[16 Apr 2009 13:18] Peter Laursen
typo!

.. maybe only now I understand Valeriy's FIRST question to me.  It is only reproducable inside 'test' and 'test_%' databases!
[16 Apr 2009 16:18] Valeriy Kravchuk
Item 3 is irrelevant as I've got the same rows for test and test_% in mysql.db on Linux (no wizard at all, but mysql_install_db script used). 

Actually, this is known and, I assume, intended behavior of test database. Check http://dev.mysql.com/doc/refman/5.0/en/default-privileges.html. Are you sure you had removed both anonimous accounts mentioned there?
[16 Apr 2009 16:33] Peter Laursen
There was only one anonymous account in both 5.0.77 and 5.1.33!  I do not remember it it was <empty>@localhost or <empty>@%.  It has been removed by deleting from mysql.users.

users now in 5.1.33:

SELECT user, host FROM mysql.user

/*
user      host     
--------  ---------
root      127.0.0.1
newuser   localhost
root      localhost
testuser  localhost
*/

I do not quite understand what passage from docs you refer to.  I see this "The other is for connections from any host and has all privileges for the test database and for other databases with names that start with test."

.. yes BUT 'testuser' is not a user created during installation. I created.  And she not only have privileges to specific tables.
[16 Apr 2009 16:48] Peter Laursen
I tried (as root)

INSERT INTO `db`(`Host`,`Db`,`User`,`Select_priv`,`Insert_priv`,`Update_priv`,`Delete_priv`,`Create_priv`,`Drop_priv`,`Grant_priv`,`References_priv`,`Index_priv`,`Alter_priv`,`Create_tmp_table_priv`,`Lock_tables_priv`,`Create_view_priv`,`Show_view_priv`,`Create_routine_priv`,`Alter_routine_priv`,`Execute_priv`,`Event_priv`,`Trigger_priv`) VALUES ( '','test2','','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N');
FLUSH PRIVILEGES;

and as testuser

SELECT * FROM test2.bittest
-- SELECT command denied to user 'testuser'@'localhost' for table 'bittest'

.. so seems that it is only affecting databases starting wiht 'test..'.

1) I do not find that the behavior here is as what is documented.  Not for 'test' user and not for 'test_77' and 'test_99' etc databases (undocumented full access for all users to all 'test_%' databases is a very serious flaw in my opinion. 

2) I find it wrong that <empty> fields in the db database are used like that. If 'test' user has access then put the value 'test' in the user field.

3) SHOW GRANTS does not tell that any user can read everything from 'test', 'test_77' etc. databases
[29 Jul 2009 5:28] Sveta Smirnova
Thank you for the report.

Really this behavior is documented at http://dev.mysql.com/doc/refman/5.1/en/default-privileges.html:
----<q>----
Two anonymous-user accounts are created, each with an empty user name. The anonymous accounts have no password, so anyone can use them to connect to the MySQL server.

    * On Windows, one anonymous account is for connections from the local host. It has no global privileges. (Before MySQL 5.1.16, it has all global privileges, just like the root accounts.) The other is for connections from any host and has all privileges for the test database and for other databases with names that start with test.
    * On Unix, both anonymous accounts are for connections from the local host. Connections must be made from the local host by specifying a host name of localhost for one of the accounts, or the actual host name or IP number for the other. These accounts have all privileges for the test database and for other databases with names that start with test_.
----</q>----

See also http://dev.mysql.com/doc/refman/5.1/en/mysql-secure-installation.html
[29 Jul 2009 14:11] Peter Laursen
I think the reply misses the point.  I am not an anonymous user and not a 'test' user either. I am 'testuser'@'localhost'.

1)
I find it both undocumented and wrong that 'testuser'@'localhost' can access `test` database. Am I missing something here?

2) 
And additionally I think that SHOW GRANTS should tell *effective privileges* to all objects. Not only the privileges that were explcitly granted.

I am opning again.  Not to be pedantic. But the reply does not take my test case into consideration (or I fail to understand the point).
[29 Jul 2009 21:27] Sveta Smirnova
Thank you for the feedback.

> I find it both undocumented and wrong that 'testuser'@'localhost' can access `test`
> database. Am I missing something here?

Please see http://dev.mysql.com/doc/refman/5.1/en/request-access.html and following output right after installing:

mysql> select user, host, db from mysql.db;
+------+-----------+---------+
| user | host      | db      |
+------+-----------+---------+
|      | %         | test    | 
|      | %         | test\_% | 
+------+-----------+---------+
2 rows in set (0.40 sec)

Probably "A blank User value in the db table matches the anonymous user. " should be changed to "A blank User value in the db table matches the any user.", but this is documented.

Regarding to second option:

> And additionally I think that SHOW GRANTS should tell *effective privileges* to all
> objects. Not only the privileges that were explcitly granted.

I think this can be good addition. Set to verified feature request.