影响版本:Log4j1.2.x<=1.2.17
漏洞环境:Log4j1.2.17+Debian+JDK1.7
下载地址: https://logging.apache.org/log4j/1.2/download.html
其实这个漏洞非常简单,本质就是对从 socket 流中获取的数据没有进行过滤,而直接反序列化。如果当前环境中存在可利用的反序列化 Gadget 链,就可以达到命令执行等效果。我们可以创建如下 Demo 代码用于测试。
// src/SocketDeserializeDemo.java import org.apache.log4j.net.SimpleSocketServer; public class SocketDeserializeDemo { public static void main(String[] args){ System.out.println("INFO: Log4j Listening on port 8888"); String[] arguments = {"8888", (new SocketDeserializeDemo()).getClass().getClassLoader().getResource("log4j.properties").getPath()}; SimpleSocketServer.main(arguments); System.out.println("INFO: Log4j output successfuly."); } }
# src/resources/log4j.properties log4j.rootCategory=DEBUG,stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.threshold=DEBUG log4j.appender.stdout.layout.ConversionPattern=[%d{yyy-MM-dd HH:mm:ss,SSS}]-[%p]-[MSG!:%m]-[%c/:%L]%n
我们跟进 SimpleSocketServer 的 main 方法,发现其中调用了 SocketNode 类,具体代码如下。
如下图, SocketNode 类实现了 Runnable 接口,其构造方法从 socket 流中获取了数据,并将数据封装为一个 ObjectInputStream 对象。然后在其 run 方法中调用了 readObject 方法进行反序列化操作。由于这里没有对数据进行过滤,所以这里就出现了反序列化漏洞。
这里我们添加一条 commons-collections 的 Gadget 链用来演示命令执行。
影响版本:Log4j2.x<=2.8.1
漏洞环境:Log4j2.8.1+Debian+JDK1.7
下载地址: https://archive.apache.org/dist/logging/log4j/2.8.1/
漏洞本质和上面是一样的,我们先创建如下 Demo 代码用于测试。
// src/main/java/Log4jSocketServer.java import org.apache.logging.log4j.core.net.server.ObjectInputStreamLogEventBridge; import org.apache.logging.log4j.core.net.server.TcpSocketServer; import java.io.IOException; import java.io.ObjectInputStream; public class Log4jSocketServer { public static void main(String[] args){ TcpSocketServer<ObjectInputStream> myServer = null; try{ myServer = new TcpSocketServer<ObjectInputStream>(8888, new ObjectInputStreamLogEventBridge()); } catch(IOException e){ e.printStackTrace(); } myServer.run(); } }
<!-- maven文件pom.xml --> <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>log4j-2.x-rce</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.8.1</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.8.1</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-collections/commons-collections --> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> </dependency> </dependencies> </project>
当我们运行代码后,程序会在本地的 8888
端口开始等待接收数据,然后在下图第105行代码处,将接收到的数据转换成 ObjectInputStream
对象数据,最终在 handler.start()
中调用 SocketHandler
类的 run
方法。
在 SocketHandler 类的 run 方法中, ObjectInputStream 对象数据被传入了 ObjectInputStreamLogEventBridge 类的 logEvents 方法,而反序列化就发生在这个方法中。
同样这里我们添加一条 commons-collections 的 Gadget 链用来演示命令执行。