RMI攻击(四)

admin 2024年10月18日23:02:34评论8 views字数 12708阅读42分21秒阅读模式

RMI攻击(四)

DGC

DGC(Distributed Garbage Collection)分布式垃圾回收,用于维护 Register 端或 Server 端中被 Client 端使用的远程引用。RMI 定义 java.rmi.dgc.DGC 接口,提供了两个方法 dirty() 和 clean()

publicinterface DGC extendsRemote{Lease dirty(ObjID[] ids,long sequenceNum,Lease lease)throwsRemoteException;    clean(ObjID[] ids,long sequenceNum, VMID vmid,boolean strong)throwsRemoteException;}

dirty():若 Client 端需使用 Register 端或 Server端的远程对象,需调用dirty()进行注册,注册持续一段时间,若需再次使用,则需再次调用dirty()clean():若 Client 端不再使用调用clean()删除远程对象引用

java.rmi.dgc.DGC 接口存在两个实现类sun.rmi.transport.DGCImpl和sun.rmi.transport.DGCImpl_Stubsun.rmi.transport.DGCImpl 存在 static 代码块如下:

static{/**"Export" the singleton DGCImplin a context isolated from* the arbitrary current thread context.*/AccessController.doPrivileged(newPrivilegedAction<Void>(){publicVoid run(){ClassLoader savedCcl =Thread.currentThread().getContextClassLoader();try{Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());/**Put remote collector objectin table by hand to prevent* listen on port.(UnicastServerRef.exportObject would* cause transport to listen.)*/try{                    dgc =newDGCImpl();ObjID dgcID =newObjID(ObjID.DGC_ID);// new LiveRef(dgcID, 0) 此处传入端口为0//Register 端若与 Server 端分离即无远程对象监听的随机端口则此处 LiveRef 监听端口为0//Server 端则此处 LiveRef 监听端口与远程对象监听的随机端口相同LiveRefref=newLiveRef(dgcID,0);UnicastServerRef disp =newUnicastServerRef(ref,DGCImpl::checkInput);//此处调用 Util.createProxy,因存在 DGCImpl_Stub 类,此处返回 DGCImpl_StubRemote stub =Util.createProxy(DGCImpl.class,newUnicastRef(ref),true);//UnicastServerRef#setSkeleton() 调用 skel = Util.createSkeleton(impl);//Util.createSkeleton(impl) 即获取 DGCImpl_Skel//因存在类 DGCImpl_Skel,故此处 dgc 实际为 DGCImpl_Skel                    disp.setSkeleton(dgc);Permissions perms =newPermissions();                    perms.add(newSocketPermission("*","accept,resolve"));ProtectionDomain[] pd ={newProtectionDomain(null, perms)};AccessControlContext acceptAcc =newAccessControlContext(pd);// 声明 targetTarget target =AccessController.doPrivileged(newPrivilegedAction<Target>(){publicTarget run(){returnnewTarget(dgc, disp, stub, dgcID,true);}}, acceptAcc);//将声明的 target 添加至 ObjectTableObjectTable.putTarget(target);}catch(RemoteException e){thrownewError("exception initializing server-side DGC", e);}}finally{Thread.currentThread().setContextClassLoader(savedCcl);}returnnull;}});}

DGCImpl_Skel#dispatch()

publicvoid dispatch(java.rmi.Remote obj, java.rmi.server.RemoteCall call,int opnum,long hash)throws java.lang.Exception{// hash 校验if(hash != interfaceHash)thrownew java.rmi.server.SkeletonMismatchException("interface hash mismatch");    sun.rmi.transport.DGCImpl server =(sun.rmi.transport.DGCImpl) obj;//判断 clean() 或 dirty()switch(opnum){case0:// clean(ObjID[], long, VMID, boolean){                java.rmi.server.ObjID[] $param_arrayOf_ObjID_1;long $param_long_2;                java.rmi.dgc.VMID $param_VMID_3;boolean $param_boolean_4;try{//获取客户端发送的值                    java.io.ObjectInputin= call.getInputStream();//对值进行反序列化                    $param_arrayOf_ObjID_1 =(java.rmi.server.ObjID[])in.readObject();                    $param_long_2 =in.readLong();//对值进行反序列化                    $param_VMID_3 =(java.rmi.dgc.VMID)in.readObject();                    $param_boolean_4 =in.readBoolean();}catch(java.io.IOException e){thrownew java.rmi.UnmarshalException("error unmarshalling arguments", e);}catch(java.lang.ClassNotFoundException e){thrownew java.rmi.UnmarshalException("error unmarshalling arguments", e);}finally{                    call.releaseInputStream();}//进行 clean 操作                server.clean($param_arrayOf_ObjID_1, $param_long_2, $param_VMID_3, $param_boolean_4);try{                    call.getResultStream(true);}catch(java.io.IOException e){thrownew java.rmi.MarshalException("error marshalling return", e);}break;}case1:// dirty(ObjID[], long, Lease){                java.rmi.server.ObjID[] $param_arrayOf_ObjID_1;long $param_long_2;                java.rmi.dgc.Lease $param_Lease_3;try{//获取客户端发送的值                    java.io.ObjectInputin= call.getInputStream();//对值进行反序列化                    $param_arrayOf_ObjID_1 =(java.rmi.server.ObjID[])in.readObject();                    $param_long_2 =in.readLong();//对值进行反序列化                    $param_Lease_3 =(java.rmi.dgc.Lease)in.readObject();}catch(java.io.IOException e){thrownew java.rmi.UnmarshalException("error unmarshalling arguments", e);}catch(java.lang.ClassNotFoundException e){thrownew java.rmi.UnmarshalException("error unmarshalling arguments", e);}finally{                    call.releaseInputStream();}//进行 dirty 操作                java.rmi.dgc.Lease $result = server.dirty($param_arrayOf_ObjID_1, $param_long_2, $param_Lease_3);try{                    java.io.ObjectOutputout= call.getResultStream(true);out.writeObject($result);}catch(java.io.IOException e){thrownew java.rmi.MarshalException("error marshalling return", e);}break;}default:thrownew java.rmi.UnmarshalException("invalid method number");}}

3.1、Register 端启动 Register

启动 Register Registry registry = LocateRegistry.createRegistry(1099);将在 ObjectTable 中添加一个 Target 封装的DGCImpl_Stub,其中Target.disp.skel 为 DGCImpl_SkelRegister 端 DGC 调用链:UnicastServerRef#exportObject() ---> new Target() ---> Target#pinImpl() ---> WeakRef#pin() ---> DGCImpl static{} UnicastServerRef#exportObject() 调用 new Target()

RMI攻击(四)
image.png

new Target()

RMI攻击(四)
image.png

Target#pinImpl()

RMI攻击(四)
image.png

WeakRef#pin()

RMI攻击(四)
image.png

DGCImpl static{}

RMI攻击(四)
image.png

3.2、Server 端声明远程对象

启动 Server IHello hello = new IHelloImpl();将在 ObjectTable 中添加一个 Target 封装的DGCImpl_Stub,其中Target.disp.skel 为 DGCImpl_Skel Register 端 DGC 调用链:UnicastServerRef#exportObject() ---> LiveRef#exportObject() ---> TCPEndpoint#exportObject() ---> Transport#exportObject() ---> ObjectTable#putTarget() ---> DGCImpl static{} UnicastServerRef#exportObject()

RMI攻击(四)
image.png

LiveRef#exportObject()

RMI攻击(四)
image.png

TCPEndpoint#exportObject()

RMI攻击(四)
image.png

Transport#exportObject()

RMI攻击(四)
image.png

ObjectTable#putTarget()

RMI攻击(四)
image.png

DGCImpl static{}

RMI攻击(四)
image.png

3.3、Server 端请求 Register 端服务绑定

Server 端服务绑定调用 RegistryImpl_Stub#bind()方法,调用UnicastRef#invoke()进行网络传输,Register 端调用UnicastServerRef#dispatch()方法处理 Server 端发送过来的请求,最终调用至RegistryImpl_Skel#dispatch()处理请求

publicvoid dispatch(java.rmi.Remote obj, java.rmi.server.RemoteCall call,int opnum,long hash)throws java.lang.Exception{if(hash != interfaceHash)thrownew java.rmi.server.SkeletonMismatchException("interface hash mismatch");    sun.rmi.registry.RegistryImpl server =(sun.rmi.registry.RegistryImpl) obj;switch(opnum){case0:// bind(String, Remote){// Check access before reading the argumentsRegistryImpl.checkAccess("Registry.bind");                java.lang.String $param_String_1;                java.rmi.Remote $param_Remote_2;try{                    java.io.ObjectInputin= call.getInputStream();                    $param_String_1 =(java.lang.String)in.readObject();//调用ObjectInputSteam#readObject()                    $param_Remote_2 =(java.rmi.Remote)in.readObject();}catch(java.io.IOException| java.lang.ClassNotFoundException e){thrownew java.rmi.UnmarshalException("error unmarshalling arguments", e);}finally{                    call.releaseInputStream();}......}}}

in.readObject()最终调用至UnicastRef#readExternal()

publicvoid readExternal(ObjectInputin)throwsIOException,ClassNotFoundException{ref=LiveRef.read(in,false);}

调用至LiveRef#read(in, false)

publicstaticLiveRef read(ObjectInputin,boolean useNewFormat)throwsIOException,ClassNotFoundException{Endpoint ep;ObjID id;// Now read in the endpoint, id, and result flag// (need to choose whether or not to read old JDK1.1 endpoint format)// 从输入流中读取endpoint, id和result flag// 一个固定的格式版本判断,根据JDK版本有关if(useNewFormat){        ep =TCPEndpoint.read(in);}else{        ep =TCPEndpoint.readHostPortFormat(in);}    id =ObjID.read(in);boolean isResultStream =in.readBoolean();// 声明 LiveRefLiveRefref=newLiveRef(id, ep,false);//默认进入此 ifif(ininstanceofConnectionInputStream){ConnectionInputStream stream =(ConnectionInputStream)in;// save ref to send "dirty" call after all args/returns// have been unmarshaled.        stream.saveRef(ref);if(isResultStream){// set flag in stream indicating that remote objects were// unmarshaled.  A DGC ack should be sent by the transport.            stream.setAckNeeded();}}else{DGCClient.registerRefs(ep,Arrays.asList(newLiveRef[]{ref}));}returnref;}

跟进stream.saveRef(ref),即调用ConnectionInputStream#saveRef(ref),将声明的 ref 添加至 incomingRefTable中

void saveRef(LiveRefref){Endpoint ep =ref.getEndpoint();// check whether endpoint is already in the hashtableList<LiveRef> refList = incomingRefTable.get(ep);if(refList ==null){    refList =newArrayList<LiveRef>();    incomingRefTable.put(ep, refList);}// add ref to list of refs for endpoint eprefList.add(ref);}

然后调用至call.releaseInputStream();调用链如下:call.releaseInputStream() ---> StreamRemoteCall#releaseInputStream() ---> ConnectionInputStream#registerRefs() ---> DGCClient#registerRefs() ---> DGCClient##makeDirtyCall() ---> DGCImpl_Stub#dirty()ConnectionInputStream#registerRefs()

void registerRefs()throwsIOException{if(!incomingRefTable.isEmpty()){//遍历 incomingRefTable ,从incomingRefTable中读取reffor(Map.Entry<Endpoint,List<LiveRef>> entry :         incomingRefTable.entrySet()){DGCClient.registerRefs(entry.getKey(), entry.getValue());}}}

DGCImpl_Stub#dirty()

public java.rmi.dgc.Lease dirty(java.rmi.server.ObjID[] $param_arrayOf_ObjID_1,long $param_long_2, java.rmi.dgc.Lease $param_Lease_3)throws java.rmi.RemoteException{try{//建立连接        java.rmi.server.RemoteCall call =ref.newCall((java.rmi.server.RemoteObject)this, operations,1, interfaceHash);try{            java.io.ObjectOutputout= call.getOutputStream();//序列化out.writeObject($param_arrayOf_ObjID_1);out.writeLong($param_long_2);out.writeObject($param_Lease_3);}catch(java.io.IOException e){thrownew java.rmi.MarshalException("error marshalling arguments", e);}//调用 UnicastRef#invoke()进行网络传输,发送请求至 Server 端ref.invoke(call);        java.rmi.dgc.Lease $result;Connection connection =((StreamRemoteCall) call).getConnection();try{            java.io.ObjectInputin= call.getInputStream();if(ininstanceofObjectInputStream){/***Set a filter on the stream for the return value.*/ObjectInputStream ois =(ObjectInputStream)in;AccessController.doPrivileged((PrivilegedAction<Void>)()->{ObjectInputFilter.Config.setObjectInputFilter(ois,DGCImpl_Stub::leaseFilter);returnnull;});}            $result =(java.rmi.dgc.Lease)in.readObject();}catch(java.io.IOException| java.lang.ClassNotFoundException e){if(connection instanceofTCPConnection){// Modified to prevent re-use of the connection after an exception((TCPConnection) connection).getChannel().free(connection,false);}thrownew java.rmi.UnmarshalException("error unmarshalling return", e);}finally{ref.done(call);}return $result;}catch(java.lang.RuntimeException e){throw e;}catch(java.rmi.RemoteException e){throw e;}catch(java.lang.Exception e){thrownew java.rmi.UnexpectedException("undeclared checked exception", e);}}

Server 端接收到 Register 端发送的请求后,Server 端调用UnicastServerRef#dispatch()方法处理请求,最终调用至DGCImpl_Skel#dispatch()处理请求,之前已分析过DGCImpl_Skel#dispatch()反序列化 Register 端传递的参数。

3.4、Client 端请求 Reister 端服务发现

Registry registry =LocateRegistry.getRegistry("127.0.0.1",1099);IHello hello =(IHello) registry.lookup("hello");

此处调用RegistryImpl_Stub#lookup()最终调用UnicastRef#invoke(),此阶段 Client 端与 Register 端不涉及DGC操作。

3.5、Client 端请求 Server 端服务调用

RegistryImpl_Stub#lookup()调用UnicastRef#invoke()完成网络请求后,调用in.readObject(),此处调用与 3.3 中 Register 端中一致,最终调用至ConnectionInputStream#saveRef(ref),将声明的 ref 添加至 incomingRefTable中继续调用至ref.done(call);即调用UnicastRef#done()

public java.rmi.Remote lookup(java.lang.String $param_String_1)throws java.rmi.AccessException, java.rmi.NotBoundException, java.rmi.RemoteException{try{        java.rmi.server.RemoteCall call =ref.newCall((java.rmi.server.RemoteObject)this, operations,2, interfaceHash);try{            java.io.ObjectOutputout= call.getOutputStream();out.writeObject($param_String_1);}catch(java.io.IOException e){thrownew java.rmi.MarshalException("error marshalling arguments", e);}ref.invoke(call);        java.rmi.Remote $result;try{            java.io.ObjectInputin= call.getInputStream();            $result =(java.rmi.Remote)in.readObject();}catch(java.io.IOException e){thrownew java.rmi.UnmarshalException("error unmarshalling return", e);}catch(java.lang.ClassNotFoundException e){thrownew java.rmi.UnmarshalException("error unmarshalling return", e);}finally{ref.done(call);}return $result;...}

UnicastRef#done()

publicvoiddone(RemoteCall call)throwsRemoteException{/* Done only uses the connection inside the call to obtain the* channel the connection uses.Once all information is read*from the connection, the connection may be freed.*/    clientRefLog.log(Log.BRIEF,"free connection (reuse = true)");/* Free the call connection early. */    free(call,true);try{//StreamRemoteCall#done()        call.done();}catch(IOException e){/* WARNING: If the conn has been reused early, then it is* too late to recover from thrown IOExceptions caught* here.This code is relying on StreamRemoteCall.done()*not actually throwing IOExceptions.*/}}

StreamRemoteCall#done()

publicvoiddone()throwsIOException{/* WARNING: Currently, the UnicastRef.java invoke methods rely* upon this method not throwing an IOException.*/releaseInputStream();}publicvoid releaseInputStream()throwsIOException{/* WARNING: Currently, the UnicastRef.java invoke methods rely* upon this method not throwing an IOException.*/try{if(in!=null){// execute MarshalInputStream "done" callbackstry{in.done();}catch(RuntimeException e){}// add saved references to DGC table//ConnectionInputStream#registerRefs()in.registerRefs();/* WARNING: The connection being passed to done may have* already been freed.*/in.done(conn);}    conn.releaseInputStream();}finally{in=null;}}

ConnectionInputStream#registerRefs()

void registerRefs()throwsIOException{if(!incomingRefTable.isEmpty()){for(Map.Entry<Endpoint,List<LiveRef>> entry :         incomingRefTable.entrySet()){DGCClient.registerRefs(entry.getKey(), entry.getValue());}}}

DGCClient.registerRefs()之后与3.3中调用一致,最终调用至DGCImpl_Stub#dirty()发送请求。Server 端依旧使用DGCImpl_Skel#dispatch()处理请求,存在反序列化漏洞。

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

RMI攻击(四)

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

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

发表评论

匿名网友 填写信息