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_Stub
Remote 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);
// 声明 target
Target target =AccessController.doPrivileged(
newPrivilegedAction<Target>(){
publicTarget run(){
returnnewTarget(dgc, disp, stub, dgcID,true);
}
}, acceptAcc);
//将声明的 target 添加至 ObjectTable
ObjectTable.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()
new Target()
Target#pinImpl()
WeakRef#pin()
DGCImpl static{}
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()
LiveRef#exportObject()
TCPEndpoint#exportObject()
Transport#exportObject()
ObjectTable#putTarget()
DGCImpl static{}
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 arguments
RegistryImpl.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();
// 声明 LiveRef
LiveRefref=newLiveRef(id, ep,false);
//默认进入此 if
if(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 hashtable
List<LiveRef> refList = incomingRefTable.get(ep);
if(refList ==null){
refList =newArrayList<LiveRef>();
incomingRefTable.put(ep, refList);
}
// add ref to list of refs for endpoint ep
refList.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中读取ref
for(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" callbacks
try{
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攻击(四)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论