禁用CREATE FUNCTION权限还不够,因攻击者可通过INSERT到mysql.func、SELECT INTO DUMPFILE写入.so或利用默认可写plugin_dir绕过;必须配置plugin_dir=/nonexistent/path、secure_file_priv=/tmp、disable-plugin=udf三者并实测验证。

为什么禁用 CREATE FUNCTION 权限还不够
很多管理员以为只要 REVOKE CREATE FUNCTION 就安全了,但实际攻击链常绕过它。比如:已有账号被误授 INSERT 权限到 mysql.func 系统表,攻击者可直接写入函数记录;或利用 SELECT ... INTO DUMPFILE 把恶意 .so 写进 plugin_dir 后手动触发加载。更隐蔽的是,MySQL 8.0+ 中若未显式设置 plugin_dir,服务可能 fallback 到默认路径(如 /usr/lib64/mysql/plugin),而该目录若被设为可写,CREATE FUNCTION 即使被回收也形同虚设。
必须修改 my.cnf 的三个关键配置项
权限回收只是软件层控制,真正阻断 UDF 加载要靠启动时硬限制:
-
plugin_dir=/nonexistent/path—— 让CREATE FUNCTION ... SONAME必然报错ERROR 1126 (HY000): Can't open shared library -
secure_file_priv=/tmp(绝不能设为空或/)—— 限制SELECT ... INTO OUTFILE和LOAD DATA INFILE落地路径,切断 payload 写入插件目录的常见通道 -
disable-plugin=udf(MySQL 8.0.29+)—— 启动时彻底屏蔽 UDF 加载机制,比allow-suspicious-udfs=0更底层有效
如何验证 UDF 是否真被禁用
改完配置重启后,别只信日志,要实测:
- 执行
SHOW VARIABLES LIKE 'plugin_dir';,确认返回值是预设的无效路径 - 用高权限账号尝试
CREATE FUNCTION test_udf RETURNS STRING SONAME 'xxx.so';,应明确报错而非静默失败 - 查
SELECT name FROM mysql.func;,结果应为空;若非空,说明旧 UDF 仍注册着,需手动DROP FUNCTION test_udf; - 运行
ps aux | grep mysqld,确认启动命令里含--disable-plugin=udf(如果用了该参数)
最容易被忽略的“隐性提权入口”
UDF 不是唯一路径。很多团队花力气封了 plugin_dir,却忘了 general_log_file 和 slow_query_log_file 也能被 SET 成任意路径。攻击者一旦有 FILE 权限,就能:SET GLOBAL general_log_file = '/etc/passwd'; SET GLOBAL general_log = 'ON'; SELECT 'root:$6$abc:0:0:root:/root:/bin/bash:'; —— 直接覆盖系统文件。所以必须同步检查并锁定所有日志路径变量,且确保 FILE 权限只保留在极少数运维账号上。


















