禁用 CREATE FUNCTION 权限不能阻止 UDF 提权,必须通过 plugin_dir=/nonexistent/path、secure_file_priv=/tmp 和 disable-plugin=udf 三项启动参数硬限制;还需验证路径、报错、函数表及进程参数,并锁定日志文件路径与 FILE 权限。

禁用 CREATE FUNCTION 权限本身不能阻止 UDF 提权,必须从 MySQL 启动层硬限制插件加载路径与机制。
为什么 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 中配置的三项关键参数
plugin_dir=/nonexistent/path —— 让所有 CREATE FUNCTION ... SONAME 必然报错 ERROR 1126 (HY000): Can't open shared library,这是最直接的拦截点secure_file_priv=/tmp —— 绝不能设为空或 /,否则 SELECT ... INTO DUMPFILE 可能写入任意路径,切断 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
FILE 权限和日志路径是隐性提权入口
很多团队封了 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 权限只保留在极少数运维账号上。


















