1.关于AssemblyInstaller
在前篇我们基本介绍了.NET Fastjson反序列化问题的一些基本情况,在最后提到了黑名单里的几个关键类,我们使用的只是ObjectDataProvider,其它几个之前确实没见过,所以这里来研究一下别的sink点
那么就从第一个AssemblyInstaller开始,从他的名字就可以看出来,这玩意涉及程序集的加载安装
涉及程序集的加载,那确实很敏感了,我们来看看是怎么个安装法,首先我们要写一个符合Installer规则的类,然后编译成exe:
写一个MyCustomInstaller类,实现Installer。并且要使用一个特性:
[RunInstaller(true)]
然后写一个构造函数,里面调用StartCalculator()方法,一个弹计算器的方法。然后需要实现Install方法
编译成ConsoleApp3.exe
然后我们使用官方给出的demo去加载ConsoleApp3.exe
可见,我们构造方法中的恶意代码确实被成功执行了,但是这里是依次调用Install()、Commit()方法才实现执行的。而Fastjson并不能随意调用目标类中的方法,因此我们需要搞清楚AssemblyInstaller构造方法、Install()、Commit()这几个方法背后的奥秘。首先看到AssemblyInstaller
这里对Path属性赋值,我们来看看Path属性的set
注意看,这里调用
Assembly.LoadFrom()
去对我们传入的exe路径进行了加载,得到Assembly对象,并赋值给assembly属性。这是个关键步骤,但是还不能触发恶意代码
那么压力就给到Install()、Commit(),注意到这俩方法一上来就调用本类的
InitializeFromAssembly()
从名字上看就涉及Assembly的初始化
于是我们跟进这个方法,这个方法一上来就把前面提到的assembly属性传入GetInstallerTypes方法,执行结果给array赋值,注意这个array是Type[]类型的
这个方法里面在做什么也是一目了然
后续遍历array并传入
Activator.CreateInstance()
调用目标类构造方法生成对象,也就导致恶意代码被执行
因此其实最关键的有两步,第一步是触发Path的set,完成
Assembly.LoadFrom()
再接着是要触发
InitializeFromAssembly()
那么除了commit和install两个方法,还有什么地方能触发InitializeFromAssembly()呢?最好是一些属性的get和set,这里很容易找到:
HelpText属性的get,就可以触发
因此我们的测试代码可以写成这样:
同样是只用Get和Set就能完成RCE,当然你可能会说这样实战还得要求我们上传一个exe文件,也挺麻烦,但其实不用,我们可以给ConsoleApp3.exe改为ConsoleApp3.jpg
然后用file://协议去加载
同样能实现RCE(当然我实测不用file://协议也行,反正后缀名无所谓的)
不过这个HelpText需要get才能触发,而这边的Fastjson好像又没有$ref触发get的姿势,所以得想办法找一些触发get的链子(Json.Net相关反序列化点里我们介绍过不少了),这里就要牵扯到黑名单中的另一个类
system.windows.forms.bindingsource
2.关于bindingsource
Bindingsource里有两个属性的set方法组合起来就可以触发任意类的指定属性的get方法,分别是DataMember和DataSource。其中DataSource这边先赋值一下,赋值成你想触发特定get方法的对象
然后DataMember这边赋值成你想要触发get的目标类中的对应属性名
进入ResetList()方法,这里把dataSource传入GetListFromType方法得到一个List,然后和dataMember一起传入GetList()方法
这里面有几个关键步骤
也即会从目标类中搜索特定属性,属性名也就是dataMember的值,然后会调用GetValue()方法获取其值,这会触发get操作:
So这里就可以把链子续上了,我们给dataSource设置成AssemblyInstaller类的对象,然后给dataMember设置成HelpText即可。
3.关于WorkflowDesigner
可以看到,和“画布”功能有关,当时就在想这个不会又和Xaml扯上关系了吧
往下看一下它支持的方法:
明牌了啊这直接,这个类确实支持处理Xaml。那我们来找找有没有什么属性的set可以触发恶意操作
注意到
PropertyInspectorFontAndColorData
属性的set操作,这里把传入的值传给XmlReader进行处理,然后进一步传给
XamlReader.Load()
梦开始的地方,希望你还记得这个方法,我们在XmlSerializer反序列化问题的学习中了解过这个方法,它和
XamlReader.Parse()
都可以加载xaml格式的数据,可以实现RCE
因此我们还是准备一个恶意xaml代码
读取这个文件的内容,并传给PropertyInspectorFontAndColorData属性
成功RCE,这个显然比上一个sink点简单多了。不过这里需要加一个[STAThread]特性
4.ResourceDictionary
又是WPF相关,推测最终的Sink点又是XamlReader,实际上也确实是。
其Source属性里这一段,涉及XamlReader,我们跟进
GetObjectAndCloseStreamCore()
这个方法里有一个关键点是一个委托的调用,把XamlReader传进去了
这个委托对应的方法是
AppModelKnownContentFactory.XamlConverterCore()
该方法最后有XamlReader.Load()的调用,可以执行Xaml代码
那么还有一个关键点在于,这个stream,我们可控吗?一路追溯这个参数的来源
又注意到,s的值是从远端文件中获取
因此可以做一个恶意服务端,返回恶意Xaml内容,借助这个链子实现RCE
5.ExchangeSettingsProvider
这玩意需要有Exchange环境,找半天没找到相关的源码,然后发现Y4er师傅在自己的博客上也分析了本文的几个.net Fastjson利用sink点,里面就给出了相关的源码
可以看到ByteData属性的set方法,拿到传入的value之后丢给binaryFormatter的Deserialize()方法,好简单的二次反序列化(
6.参考
https://y4er.com/posts/several-other-gadgets-of-dotnet/#systemactivitiespresentationworkflowdesigner