限制plugin_dir写权限比禁用file更关键,因攻击者可借select...into dumpfile将恶意udf库落地该目录,再通过create function...soname加载提权;而secure_file_priv不约束plugin_dir。

为什么限制 plugin_dir 写权限比禁用 FILE 更关键
FILE 权限只是让攻击者能“写文件”,但真正触发提权的是 MySQL 主动加载恶意 .so 或 .dll —— 这个加载动作必须指向 plugin_dir 下的合法路径。只要该目录可写、且 MySQL 进程有读取权限,攻击者就能用 SELECT ... INTO DUMPFILE 把编译好的 UDF 库直接落地,再通过 CREATE FUNCTION ... SONAME 加载执行。
而很多环境误以为关掉 secure_file_priv 就安全了,其实它只控制 LOAD_FILE() 和 INTO OUTFILE 的路径,对 plugin_dir 完全不设防。
plugin_dir 目录权限设置的实操要点
MySQL 不校验 plugin_dir 的属主或权限,但加载时会检查:库文件是否属于 mysql 用户、权限是否 ≤644、路径是否含符号链接(8.0.29+)、是否在白名单插件路径内。所以光改配置不够,必须配合系统层加固:
- 确认当前值:
SHOW VARIABLES LIKE 'plugin_dir';,常见路径如/usr/lib/mysql/plugin或/var/lib/mysql/plugin - 把该目录属主设为
mysql:mysql,权限收紧到755:chown -R mysql:mysql /usr/lib/mysql/plugin && chmod 755 /usr/lib/mysql/plugin - 禁止其他用户写入:
chmod o-w /usr/lib/mysql/plugin(注意不是750,因为 MySQL 可能需要组内读取) - 检查是否有符号链接:
ls -l $(mysql -Nse "SELECT @@plugin_dir"),若输出含->,立刻用真实路径替换并重启 mysqld
MySQL 启动参数与配置文件的双重锁定
仅靠文件系统权限是脆弱的——如果攻击者已有高权限账号,仍可能通过 SET GLOBAL plugin_dir = '/tmp' 动态修改(需 SUPER 权限)。所以必须从启动源头堵死:
- 在
my.cnf的[mysqld]段强制指定不可写路径:plugin_dir = /nonexistent/path(不是注释掉,是明确指向一个不存在且不可创建的路径) - MySQL 8.0.29+ 支持启动时禁用 UDF 插件加载:
--disable-plugin=udf,加到 systemd service 文件的ExecStart行末尾 - 避免使用
--skip-grant-tables启动,它会让所有权限检查失效,包括plugin_dir校验 - 验证是否生效:
mysql -e "SHOW PLUGINS;" | grep -i udf应无输出;尝试CREATE FUNCTION pwn RETURNS STRING SONAME 'x.so'必须报错ERROR 1126 (HY000)
别忽略编译产物和临时目录的清理
攻击者常利用中间路径绕过 plugin_dir 限制:先写入 /tmp 或 /var/lib/mysql-files/(secure_file_priv 允许的路径),再用符号链接或硬链接“映射”过去。这类操作依赖于临时目录可写且未被隔离:
- 清空示例 UDF 文件:
rm -f /usr/lib/mysql/plugin/udf_example.so,但不要删整个plugin/目录(否则影响 InnoDB、audit_log 等核心插件) - 确保
/tmp和/var/tmp挂载时启用noexec,nosuid,nodev - 若用容器部署,挂载
/usr/lib/mysql/plugin为只读卷,或在 entrypoint 中运行chown mysql:mysql /usr/lib/mysql/plugin && chmod 555 /usr/lib/mysql/plugin - 定期检查异常共享库:
find /usr/lib/mysql/plugin /tmp /var/lib/mysql-files -name "*.so" -o -name "*.dll" -user root 2>/dev/null,非 mysql 用户拥有的必须告警
最关键的其实是:plugin_dir 路径本身不能由数据库账号可控,也不能依赖运行时权限判断——它必须是启动即固化、操作系统层不可篡改的硬约束。任何试图在 SQL 层“回收权限”来代替路径锁定的做法,都会在真实攻击链中被绕过。











