Serious bug in MySQL password handling.

From: Viktor Fougstedt (viktorat_private)
Date: Tue Jan 11 2000 - 08:34:54 PST

  • Next message: Ajax: "Re: Hotmail security hole - injecting JavaScript using <IMG"

    Hi.
    
    While installing MySQL 3.22.29 and testing it out, I discovered a
    very serious bug in it's handling of the GRANT statement.
    
    I have decided to post this message at this time although a fixed
    distribution is not yet available at mysql.com. The reason for this is
    that the subject has been discussed on the MySQL mailing list, and
    that the cat is therefore already out of the bag. Many crackers out
    there are already aware of implications and how to exploit this bug. A
    temporary solution is suggested in this post, and a patch from TCX is
    attached at the bottom.
    
    
    
    Affected:
    
    Known: MySQL 3.22.27, 3.22.29, 3.23.8
    Suspected: All MySQL versions since 3.22.11.
    
    
    Impact:
    
    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.
    
    
    Implications:
    
    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.
    
    
    Story:
    
    I contacted the MySQL mailing list and reported this bug (the MySQL
    mailing list is the official place to report bugs). The first person
    from mysql.com I had a discussion with, said that this was the way it
    was supposed to work. GRANT privileges were, according to him, by
    definition global (even GRANTs for a single database were apparently
    global) and implied the possibility to change anyone's password. I
    almost bought his story and started thinking about changing to another
    RDBMS.
    
    Fortunately, Monty at TCX Datakonsult AB (the company that originally
    wrote MySQL) acknowledged that it was a real and serious bug. No one
    without UPDATE privilige on the mysql.user table should be able to set
    anyone's password. A fix has been made, and new releases will be
    available for download very soon.
    
    
    Recommendation:
    
    It is STRONGLY RECOMMENDED that anyone running MySQL 3.22.11 or later
    UPGRADE to the new versions as soon as they are available at
    www.mysql.com and it's mirrors.
    
    While waiting for the new versions to arrive, or 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, I have received a patch
    for the problem from TCX. It is attached at the bottom of this
    message.
    
    
    
    Details:
    
    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:
    
    I will post an exploit as soon as the patched versions are available
    (which should be tomorrow). The Kiddiez should be forced to do at
    least _some_ work themselves. :-)
    
    
    /Viktor...
    
    
    
    ----------------------------------------------------------------------
    
    
    *** /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,
    
    
    ----------------------------------------------------------------------
    
    
    
    
    
    
    
    --|     Viktor Fougstedt, system administrator at dtek.chalmers.se     |--
    --|                http://www.dtek.chalmers.se/~viktor/                |--
    --| ...soon we'll be sliding down the razor blade of life. /Tom Lehrer |--
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 15:27:29 PDT