14.net remoting安全问题(未完结)

admin 2024年10月30日19:30:01评论8 views字数 7633阅读25分26秒阅读模式

1.Remoting机制的使用

什么是Remoting,网上找到的比较标准的介绍如下:

简而言之,我们可以将其看作是一种分布式处理方式。Microsoft .NET Remoting 提供了一种允许对象通过应用程序域与另一对象进行交互的框架。这也正是我们使用Remoting的原因。为什么呢?在Windows操作系统中,是将应用程序分离为单独的进程。这个进程形成了应用程序代码和数据周围的一道边界。如果不采用进程间通信(RPC)机制,则在一个进程中执行的代码就不能访问另一进程。这是一种操作系统对应用程序的保护机制。然而在某些情况下,我们需要跨过应用程序域,与另外的应用程序域进行通信,即穿越边界。
在Remoting中是通过通道(channel)来实现两个应用程序域之间对象的通信的。首先,客户端通过Remoting,访问通道以获得服务端对象,再通过代理解析为客户端对象。这就提供一种可能性,即以服务的方式来发布服务器对象。远程对象代码可以运行在服务器上(如服务器激活的对象和客户端激活的对象),然后客户端再通过Remoting连接服务器,获得该服务对象并通过序列化在客户端运行。

当然,上面这一大段是Remoting机制的标准介绍,对JAVA比较熟悉的读者可以尝试用RMI去带入理解一下这个机制,包括它的安全问题和RMI都是非常类似的。

既然Remoting涉及对象的传递,那么自然也就要涉及序列化与反序列化,Remoting主要有两种通道,Http和Tcp。在.Net中,System.Runtime.Remoting.Channel中定义了IChannel接口。IChannel接口包括了TcpChannel通道类型和Http通道类型。它们分别对应Remoting通道的这两种类型。

TcpChannel类型放在名字空间System.Runtime.Remoting.Channel.Tcp中。Tcp通道提供了基于Socket的传输工具,使用Tcp协议来跨越Remoting边界传输序列化的消息流。TcpChannel类型默认使用二进制格式序列化消息对象,因此它具有更高的传输性能。HttpChannel类型放在名字空间System.Runtime.Remoting.Channel.Http中。它提供了一种使用Http协议,使其能在Internet上穿越防火墙传输序列化消息流。默认情况下,HttpChannel类型使用Soap格式序列化消息对象,因此它具有更好的互操作性。通常在局域网内,我们更多地使用TcpChannel;如果要穿越防火墙,则使用HttpChannel。

除此之外还有一种IpcChannel,用于本机的进程之间的数据传输。总结一下就是HttpChannel用Soap格式传输数据,TcpChannel用二进制格式传输数据。这里就可以简单猜测一下前者使用SoapFormatter进行序列化与反序列化,后者使用BinaryFormatter进行序列化与反序列化。

我们可以使用一些demo来学习Remoting机制的使用。

2.HttpChannel的使用与分析

这里先实现一个传输对象类,也即后续我们要传输的对象

14.net remoting安全问题(未完结)

这个功能很简单,每次访问这个count就+1

接着我们实现Remoting的服务端,代码如下

14.net remoting安全问题(未完结)

这里绑定了服务端的端口是9999,指定了要传输的对象是我们前面实现的RemoteDemoObjectClass,并且发布的uri地址为

RemoteDemoObjectClass.rem

目前都很好理解,这第三个参数

WellKnownObjectMode.Singleton

又有什么作用呢?这玩意主要分为两种模式:

SingleTon模式:此为有状态模式。如果设置为SingleTon激活方式,则Remoting将为所有客户端建立同一个对象实例。当对象处于活动状态时,SingleTon实例会处理所有后来的客户端访问请求,而不管它们是同一个客户端,还是其他客户端。SingleTon实例将在方法调用中一直维持其状态。举例来说,如果一个远程对象有一个累加方法(i=0;++i),被多个客户端(例如两个)调用。如果设置为SingleTon方式,则第一个客户获得值为1,第二个客户获得值为2,因为他们获得的对象实例是相同的。如果熟悉Asp.Net的状态管理,我们可以认为它是一种Application状态。
SingleCall模式:SingleCall是一种无状态模式。一旦设置为SingleCall模式,则当客户端调用远程对象的方法时,Remoting会为每一个客户端建立一个远程对象实例,至于对象实例的销毁则是由GC自动管理的。同上一个例子而言,则访问远程对象的两个客户获得的都是1。我们仍然可以借鉴Asp.Net的状态管理,认为它是一种Session状态。

我们把上面做好的服务端编译为exe

14.net remoting安全问题(未完结)

然后接着再写客户端,客户端代码如下:

14.net remoting安全问题(未完结)

主要就是连接服务端,指定前面发布的

/RemoteDemoObjectClass.rem

如下图,我客户端一直访问,然后这边的值就一直+1

14.net remoting安全问题(未完结)

当然这里我就好奇了,如果我在浏览器直接访问

http://localhost:9999/RemoteDemoObjectClass.rem

会得到什么信息呢

14.net remoting安全问题(未完结)

14.net remoting安全问题(未完结)

请求头里有一个

Server: MS .NET Remoting, MS .NET CLR 4.0.30319.42000

可以看到这个特征还是挺显著,如果实战中遇到.rem的uri,并且看到类似的异常信息,马上反应过来这里是基于HttpChannel的Remoting机制。

这里也看一下客户端和服务端的通信过程,这里配置一下Burp代理的转发,从8080端口拿到流量后转发到9999端口,然后启用透明代理

14.net remoting安全问题(未完结)

然后客户端请求的时候把端口改成8080

14.net remoting安全问题(未完结)

可以在Http History里看见通信过程:

14.net remoting安全问题(未完结)

14.net remoting安全问题(未完结)

可以看到这个模式和RMI那边还是很像的,服务端那边调用之后会把结果返回回来,先返回一个100 Continue的状态,然后返回信息。只不过这里用的是SOAP格式的数据进行传输。这里我们浅析一下这个通信的序列化与反序列化流程。

首先简单的搜索一下Serialize之类的关键词,其实很容易能找到,在SoapClientFormatterSink类里,可以先找到SerializeMessage方法。从这个类的名字就可以知道,这是客户端相关的方法,那么SerializeMessage就是客户端发起请求时,对请求信息进行序列化的方法

14.net remoting安全问题(未完结)

如图,运行客户端发起请求的时候,可以在这里断住

14.net remoting安全问题(未完结)

其中调用了CoreChannel类的SerializeSoapMessage()方法

14.net remoting安全问题(未完结)

我们跟进这个方法,在它的一开始就调用本类的CreateSoapFormatter()方法,得到一个SoapFormatter类的对象

14.net remoting安全问题(未完结)

跟进CreateSoapFormatter(),这里主要就是创建一个SoapFormatter类的对象

14.net remoting安全问题(未完结)

继续往后看SerializeSoapMessage()方法,最后调用SoapFormatter的Serialize()方法,完成信息的序列化

14.net remoting安全问题(未完结)

既然SoapClientFormatterSink中有SerializeMessage方法,用于发送请求时的序列化,那么自然也有DeserializeMessage方法,从服务端收到响应后,正是由这个方法对响应信息进行反序列化:

14.net remoting安全问题(未完结)

这个方法里又调用CoreChannel类的

DeserializeSoapResponseMessage()

14.net remoting安全问题(未完结)

类似地,直接拿到SoapFormatter对象后对接收到的信息进行Deserialize()反序列化

14.net remoting安全问题(未完结)

这样我们就弄懂了客户端信息收发的流程。主要就是

SoapClientFormatterSink

中的SerializeMessage和DeserializeMessage。

那么既然有

SoapClientFormatterSink

理所当然的也应该有

SoapServerFormatterSink

其中有一个ProcessMessage()方法

14.net remoting安全问题(未完结)

调用CoreChannel类的

DeserializeSoapRequestMessage()

对客户端发来的Soap数据进行反序列化,但是这里注意还传入了一个TypeFilterLevel

14.net remoting安全问题(未完结)

14.net remoting安全问题(未完结)

这个值会影响后续反序列化的功能

14.net remoting安全问题(未完结)

总之我们继续回到

DeserializeSoapRequestMessage()

还是老样子,创建SoapFormatter对象,然后调用Deserialize()方法对客户端传过来的信息进行反序列化。

14.net remoting安全问题(未完结)

但这里需要尤其注意一点,这里给

soapFormatter.FilterLevel

进行赋值了,值就是传入的TypeFilterLevel,我们刚刚看到了一个状态,就是TypeFilterLevel.Full

14.net remoting安全问题(未完结)

此外还有一个常见状态是TypeFilterLevel.Low,GPT介绍他们两者的区别如下:

14.net remoting安全问题(未完结)

但是感觉应该不只有这个差别,网上的另一篇文章还提到了Low模式下的一些限制:

https://github.com/cbwang505/TcpServerChannelRce
“这种模式在反序列化客户端传来的数据包的时候使用代码访问安全(CAS)机制禁用了反序列化操作危险函数调用”

总之,当设定为Full的时候,就是最正宗经典的SoapFormatter反序列化点,当然也有反序列化问题,而设置为Low的时候,会影响我们的利用。

然后服务端给客户端发信息的时候,也要先进行序列化

14.net remoting安全问题(未完结)

14.net remoting安全问题(未完结)

调用CoreChannel的SerializeSoapMessage方法,这里就没啥好看的了

14.net remoting安全问题(未完结)

因此我们总结HttpChannel场景下Remoting客户端与服务端通信场景以及涉及到的几个关键函数。

①客户端SoapClientFormatterSink#SerializeMessage()将信息进行序列化,并发送给服务端②服务端SoapServerFormatterSink#ProcessMessage()对客户端发来的信息进行反序列化(TypeFilterLevel影响反序列化流程)③服务端SoapServerFormatterSink#AsyncProcessResponse()对结果信息进行序列化,并发送给客户端④客户端SoapClientFormatterSink#DeserializeMessage()对服务端发来的结果信息进行反序列化

以上就是整个流程,那么我们不难发现,在服务端和客户端进行反序列化的时候,基本都是SoapFormatter的反序列化方法一把梭,那我们能不能用SoapFormatter反序列化点的Payload一把梭呢?

3.攻击HttpChannel服务端

这里生成一个:

ysoserial.exe -f soapformatter -g TextFormattingRunProperties -c calc

14.net remoting安全问题(未完结)

我们打打看,注意这里要把<SOAP-ENV:Body>标签删掉

14.net remoting安全问题(未完结)

当然这里直接发包会发现并没有预期结果,这就是TypeFilterLevel在作祟,我们前面分析过原理了

14.net remoting安全问题(未完结)

我们重新写一个服务端,这里强制指定一个TypeFilterLevel.Full

14.net remoting安全问题(未完结)

再使用上述payload进行发包,成功RCE

14.net remoting安全问题(未完结)

4.攻击HttpChannel客户端

这里本来我还想试试打服务端打客户端的,想自己搓一个恶意服务端,致敬一波RMI,理论上来说只要按照HttpChannel的这个通信规则去返回payload就行。一些国外的文章也提到了这个姿势:

https://www.nccgroup.com/us/research-blog/finding-and-exploiting-net-remoting-over-http-using-deserialisation/

14.net remoting安全问题(未完结)

不过搞了半天老是提示“未经处理的异常: System.FormatException: 输入字符串的格式不正确。”。

那么要咋办呢?后面我学ObjRef链子的时候,发现这玩意是真正的RMI、JRMP精神续作,简单来说对ObjRef反序列化的时候,受害主机也会主动向服务端发出Remoting请求,然后对结果进行反序列化,等于说把目标变成了一个Remoting中的客户端,那么这就需要一个恶意服务端返回payload,前人已经写好了相关项目。然后我就在想这个恶意服务端应该可以直接拿过来用吧:

14.net remoting安全问题(未完结)

ysoserial.exe -f SoapFormatter -g TextFormattingRunProperties -o raw -c MSPaint.exe > MSPaint.soap

(触发链子则弹出画图功能)

恶意服务端如下:

https://github.com/codewhitesec/RogueRemotingServer

14.net remoting安全问题(未完结)

RogueRemotingServer.exe --wrapSoapPayload http://127.0.0.1:9744/index.html MSPaint.soap

(指定恶意服务端返回的payload为MSPaint.soap)

14.net remoting安全问题(未完结)

链子成功执行

总之,在审计中只需要关注目标是否用HttpServerChannel开启了Remoting服务端,并且观察TypeFilterLevel是否指定为Full,都满足的话就是有一个Remoting反序列化点的。

14.net remoting安全问题(未完结)

当然不只是HttpServerChannel,HttpChannel也是可以的,反正就是Http+Channel这样的关键字,同时也要注意有没有客户端的使用。

5.HttpChannel场景下反序列化攻击对抗WAF

这个就很有意思了,虽然看起来HttpChannel场景下还是用HTTP数据包进行数据传递的,但实际上他并不完全遵守Http的规范。还是来到SoapServerFormatterSink处理客户端请求的ProcessMessage()方法,其中从请求头里拿到__RequestVerb作为请求方法

14.net remoting安全问题(未完结)

除此之外,还通过请求头的__RequestUri的值作为请求的Uri

14.net remoting安全问题(未完结)

所以可以不按HTTP的规则去构造一个畸形数据包,这篇文章里也提到了相关姿势:

https://www.nccgroup.com/us/research-blog/finding-and-exploiting-net-remoting-over-http-using-deserialisation/

14.net remoting安全问题(未完结)

14.net remoting安全问题(未完结)

这里可以看出另一个姿势就是,修改xml数据的encoding,XXE那边的常用绕WAF姿势,这样结合起来打组合拳应该就没啥WAF能检测到了

6.TcpChannel的使用与分析

服务端代码如下:

14.net remoting安全问题(未完结)

客户端代码如下

14.net remoting安全问题(未完结)

可见和HttpChannel的区别并不大,还是直接来看代码

BinaryClientFormatterSink

是客户端处理序列化与反序列化的方法,其

AsyncProcessMessage

方法调用本类SerializeMessage方法

14.net remoting安全问题(未完结)

SerializeMessage又调用

CoreChannel.SerializeBinaryMessage()

14.net remoting安全问题(未完结)

SerializeBinaryMessage()还是老样子,只不过这回用的是BinaryFormatter的Serialize

14.net remoting安全问题(未完结)

BinaryClientFormatterSink.AsyncProcessResponse()

方法负责对服务端的响应进行反序列化:

14.net remoting安全问题(未完结)

14.net remoting安全问题(未完结)

14.net remoting安全问题(未完结)

一路跟下来都很简单,没啥好说的。再就是服务端的BinaryServerFormatterSink, ProcessMessage()方法负责对客户端传来的信息进行反序列化,依然受到TypeFilterLevel的影响

14.net remoting安全问题(未完结)

14.net remoting安全问题(未完结)

So,和前面介绍的Soap的处理流程其实基本上都是一样的,无非一个是用的Soap,一个用的是二进制数据。

7.攻击TCPChannel服务端

这里又是James Forshaw佬的工具,真是学.NET安全绕不过去的大佬

https://github.com/tyranid/ExploitRemotingService

这里先把TCPChannel服务端开起来

14.net remoting安全问题(未完结)

然后用ysoserial.net生成一个BinaryFormatter反序列化payload

ysoserial.exe -f binaryformatter -g TextFormattingRunProperties -c calc -o base64

14.net remoting安全问题(未完结)

然后使用ExploitRemotingService加载上述payload攻击TcpChannel服务端

exploitRemotingService.exe tcp://localhost:9999/RemoteDemoObjectClass.rem raw <ysoersial生成的payload>

发包,成功RCE

14.net remoting安全问题(未完结)

也是有服务端攻击客户端的,参考上面介绍过的工具就行。

8.TypeFilterLevel.Low场景下的攻击姿势

网上看到的的文章基本都说,在TypeFilterLevel.Low场景下,ExploitRemotingService也可以进行利用,但是要设置

ConfigurationManager.AppSettings.Set("microsoft:Remoting:AllowTransparentProxyMessage", false);

这个全局非默认配置,似乎就没有别的绕过空间了。

然而今年8月最新的一个成果,cbwang505师傅给出了TcpChannel、TypeFilterLevel.Low场景下的新利用方法

https://github.com/cbwang505/TcpServerChannelRce

因为里面还没学的东西太多,这里先不去分析实现原理了,看起来整个流程非常精彩,过段时间再积累,用这里面的项目实验一下:

先开启一个ChannelServer,这里的TypeFilterLevel是low

14.net remoting安全问题(未完结)

这里还有个恶意Client

14.net remoting安全问题(未完结)

运行后大约两秒会弹记事本

14.net remoting安全问题(未完结)

修改这里即可实现任意代码执行了

14.net remoting安全问题(未完结)

当然这个点以后再来分析吧,需要的前置知识点有点多(

9.总结

关注目标中是否有HttpChannel、TcpChannel等字样,并且观察TypeFilterLevel是否指定为Full(打服务端)。不过是Low的情况下也有一定利用空间,以后再学习。

10.参考

https://xz.aliyun.com/t/9605https://www.freebuf.com/articles/web/199147.htmlhttps://github.com/cbwang505/TcpServerChannelRce

原文始发于微信公众号(HW专项行动小组):14.net remoting安全问题(未完结)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月30日19:30:01
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   14.net remoting安全问题(未完结)https://cn-sec.com/archives/3333889.html

发表评论

匿名网友 填写信息