RMI攻击(二)

admin 2024年11月4日10:53:17评论10 views字数 6747阅读22分29秒阅读模式

RMI攻击(二)

RMI使用

上接RMI攻击(一)服务注册

服务发现

1、Client 端向 Register 发送请求

//获取注册中心//此处获取到本地创建的 RegistryImpl_StubRegistry registry = LocateRegistry.getRegistry();//调用 loolup() 查找IHello hello = (IHello) registry.lookup("hello");//调用 sayHello() 方法String bai = hello.sayHello("bai");System.out.println(bai);

registry.lookup() 即调用至 RegistryImpl_Stub#lookup()

//此处var1 为 lookup() 参数 namepublic Remote lookup(String var1) throws AccessException, NotBoundException, RemoteException {    try {        //与 Registry 端连接        RemoteCall var2 = super.ref.newCall(this, operations, 2, 4905912898345647071L);        try {            ObjectOutput var3 = var2.getOutputStream();            //将 name 序列化传递            var3.writeObject(var1);        } catch (IOException var18) {            throw new MarshalException("error marshalling arguments", var18);        }        //调用 UnicastRef#invoke() 与 Registry 端通信        super.ref.invoke(var2);        Remote var23;        try {            ObjectInput var6 = var2.getInputStream();            //将 Registry 端回传结果反序列化            var23 = (Remote)var6.readObject();        } catch (IOException var15) {            throw new UnmarshalException("error unmarshalling return", var15);        } catch (ClassNotFoundException var16) {            throw new UnmarshalException("error unmarshalling return", var16);        } finally {            super.ref.done(var2);        }        return var23;    } catch (RuntimeException var19) {        throw var19;    } catch (RemoteException var20) {        throw var20;    } catch (NotBoundException var21) {        throw var21;    } catch (Exception var22) {        throw new UnexpectedException("undeclared checked exception", var22);    }}

2、Registry 端处理请求并将结果发送至 Client 端

与服务发现阶段调用一致,依旧调用至RegistryImpl_Skel#dispatch(),lookup 对应值为2

public void dispatch(Remote var1, RemoteCall var2, int var3, long var4) throws Exception {    if (var4 != 4905912898345647071L) {        throw new SkeletonMismatchException("interface hash mismatch");    } else {        RegistryImpl var6 = (RegistryImpl)var1;        String var7;        Remote var8;        ObjectInput var10;        ObjectInput var11;        switch(var3) {        ...        case 2:            try {                var10 = var2.getInputStream();                //反序列化 client 传入的 name                var7 = (String)var10.readObject();            } catch (IOException var89) {                throw new UnmarshalException("error unmarshalling arguments", var89);            } catch (ClassNotFoundException var90) {                throw new UnmarshalException("error unmarshalling arguments", var90);            } finally {                var2.releaseInputStream();            }            //调用 RegistryImpl#lookup()            //此处获取到的是 server 端声明的动态代理类 RemoteObjectInvocationHandler,此类中包含 Server 端对此项服务监听的端口            var8 = var6.lookup(var7);            try {                ObjectOutput var9 = var2.getResultStream(true);                //将查询结果序列化传输至 client                var9.writeObject(var8);                break;            } catch (IOException var88) {                throw new MarshalException("error marshalling return", var88);            }        ...    }}

服务调用

1、Client 端获取 Register 端返回结果后调用方法

IHello hello = (IHello) registry.lookup("hello");

此处即调用至 RemoteObjectInvocationHandler#invoke(),类中包含 Server 端对此项服务监听的端口,此时 Client 端与 Server 端使用JRMP协议直接进行通信。RemoteObjectInvocationHandler#invoke() 方法之前已经分析过,若为 Object 的方法将调用 invokeObjectMethod 方法,其他的则调用 invokeRemoteMethod 方法。invokeRemoteMethod 调用UnicastRef#invoke()方法,调用至 UnicastRef#unmarshalValue(),此处前面分析过,不再赘述

protected static Object unmarshalValue(Class<?> var0, ObjectInput var1) throws IOException, ClassNotFoundException {    if (var0.isPrimitive()) {        if (var0 == Integer.TYPE) {            return var1.readInt();        } else if (var0 == Boolean.TYPE) {            return var1.readBoolean();        } else if (var0 == Byte.TYPE) {            return var1.readByte();        } else if (var0 == Character.TYPE) {            return var1.readChar();        } else if (var0 == Short.TYPE) {            return var1.readShort();        } else if (var0 == Long.TYPE) {            return var1.readLong();        } else if (var0 == Float.TYPE) {            return var1.readFloat();        } else if (var0 == Double.TYPE) {            return var1.readDouble();        } else {            throw new Error("Unrecognized primitive type: " + var0);        }    } else {        //反序列化        return var1.readObject();    }}

2、Server 端调用 Client要求调用的方法,并将结果传给 Client

此处调用前半段与 Register 端监听调用链一致,即Server 端启动监听线程后由sun.rmi.transport.tcp.TCPTransport#handleMessages()方法处理请求 --> serviceCall() --> serviceCall() 从 ObjectTable 中获取封装的 Target 对象,并获取其中的封装的 UnicastServerRef 以及 RegistryImpl 对象。然后调用 UnicastServerRef 的 dispatch 方法

public void dispatch(Remote obj, RemoteCall call) throws IOException {    // positive operation number in 1.1 stubs;    // negative version number in 1.2 stubs and beyond...    int num;    long op;    try {        // read remote call header        ObjectInput in;        try {            in = call.getInputStream();            num = in.readInt();        } catch (Exception readEx) {            throw new UnmarshalException("error unmarshalling call header",                                         readEx);        }        if (num >= 0) {            //Server 端 skel 为空不进入if            if (skel != null) {                oldDispatch(obj, call, num);                return;            } else {                throw new UnmarshalException(                    "skeleton class not found but required " +                    "for client version");            }        }        try {            op = in.readLong();        } catch (Exception readEx) {            throw new UnmarshalException("error unmarshalling call header",                    readEx);        }        /*         * Since only system classes (with null class loaders) will be on         * the execution stack during parameter unmarshalling for the 1.2         * stub protocol, tell the MarshalInputStream not to bother trying         * to resolve classes using its superclasses's default method of         * consulting the first non-null class loader on the stack.         */        MarshalInputStream marshalStream = (MarshalInputStream) in;        marshalStream.skipDefaultResolveClass();        //从 this.hashToMethod_Map 中寻找 Client 端对应执行 Method 的 hash 值        Method method = hashToMethod_Map.get(op);        //找不到 method 报错        if (method == null) {            throw new UnmarshalException("unrecognized method hash: " +                "method not supported by remote object");        }        // if calls are being logged, write out object id and operation        logCall(obj, method);        // unmarshal parameters        Class<?>[] types = method.getParameterTypes();        Object[] params = new Object[types.length];        try {            unmarshalCustomCallData(in);            // Unmarshal the parameters            for (int i = 0; i < types.length; i++) {                //反序列化 Client 端传过来参数                params[i] = unmarshalValue(types[i], in);            }        } catch (AccessException aex) {            // For compatibility, AccessException is not wrapped in UnmarshalException            // disable saving any refs in the inputStream for GC            ((StreamRemoteCall) call).discardPendingRefs();            throw aex;        } catch (java.io.IOException | ClassNotFoundException e) {            // disable saving any refs in the inputStream for GC            ((StreamRemoteCall) call).discardPendingRefs();            throw new UnmarshalException(                "error unmarshalling arguments", e);        } finally {            call.releaseInputStream();        }        // make upcall on remote object        Object result;        try {            //通过反射执行方法            result = method.invoke(obj, params);        } catch (InvocationTargetException e) {            throw e.getTargetException();        }        // marshal return value        try {            ObjectOutput out = call.getResultStream(true);            Class<?> rtype = method.getReturnType();            if (rtype != void.class) {                //序列化方法执行结果,传递给 Client 端                marshalValue(rtype, result, out);            }        ...}            

3、客户端获取服务端返回调用结果,并将其反序列化

此处调用逻辑与第3步完全相同,同样在 unmarshalValue() 处进行反序列化

RMI调用总结

RMI 调用整体来看分以下三步:1、注册端启动,服务端创建远程对象获取注册中心并将远程对象绑定至注册中心(图中1-5) 2、客户端获取注册中心并查找服务,注册端向客户端返回代理类(图中6-8) 3、客户端调用远程对象,服务端收到请求调用方法并将结果返回客户端(图中9-10)

RMI攻击(二)
image.png

RMI调用流程, [Javasec]描述如下:

RMI 底层通讯采用了Stub (运行在客户端) 和 Skeleton (运行在服务端) 机制,RMI 调用远程方法的大致如下:

1.RMI 客户端在调用远程方法时会先创建 Stub ( sun.rmi.registry.RegistryImpl_Stub )。2.Stub 会将 Remote 对象传递给远程引用层 ( java.rmi.server.RemoteRef ) 并创建 java.rmi.server.RemoteCall( 远程调用 )对象。3.RemoteCall 序列化 RMI 服务名称、Remote 对象。4.RMI 客户端的远程引用层传输 RemoteCall 序列化后的请求信息通过 Socket 连接的方式传输到 RMI 服务端的远程引用层。5.RMI服务端的远程引用层( sun.rmi.server.UnicastServerRef )收到请求会请求传递给 Skeleton ( sun.rmi.registry.RegistryImpl_Skel#dispatch )。6.Skeleton 调用 RemoteCall 反序列化 RMI 客户端传过来的序列化。7.Skeleton 处理客户端请求:bind、list、lookup、rebind、unbind,如果是 lookup 则查找 RMI 服务名绑定的接口对象,序列化该对象并通过 RemoteCall 传输到客户端。8.RMI 客户端反序列化服务端结果,获取远程对象的引用。9.RMI 客户端调用远程方法,RMI服务端反射调用RMI服务实现类的对应方法并序列化执行结果返回给客户端。10.RMI 客户端反序列化 RMI 远程方法调用结果。

如您有问题、建议、需求、合作、加群交流请后台留言或添加微信

RMI攻击(二)

原文始发于微信公众号(白给信安):RMI攻击(二)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年11月4日10:53:17
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   RMI攻击(二)https://cn-sec.com/archives/1823869.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息