RMI攻击(二)

渗透技巧 10个月前 admin
216 0 0

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攻击(二)

版权声明:admin 发表于 2023年6月21日 上午9:01。
转载请注明:RMI攻击(二) | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...