MachineKeySessionSecurityTokenHandler
我们前面学习过SessionSecurityTokenHandler这个plugins,这个plugins在实战中的价值是比较低的,因为它的加解密都需要依赖DPAPI,但能接触到这玩意的时候基本已经进入后渗透阶段了,基本不会再利用SessionSecurityTokenHandler的安全问题。
但是,在翻阅SessionSecurityTokenHandler文档时,注意到微软的介绍:
注意这个点:
微软的文档告诉我们,在Web场景下可以使用
MachineKeySessionSecurityTokenHandler
它会使用machineKey中的签名密钥和加密密钥对Cookie进行加解密。关于machineKey以及相关配置,我们在ViewState安全问题相关的文章中已经学习过,这边不再展开了。
说回MachineKeySessionSecurityTokenHandler:
我们注意到这个类继承了SessionSecurityTokenHandler,那么是极可能存在类似的问题的,又注意到
new DeflateCookieTransform(),
new MachineKeyTransform()
前者不必多说(参考对SessionSecurityTokenHandler的分析),而后者从名字上就类似于ProtectedDataCookieTransform(),我们猜测MachineKeyTransform()就是用于获取MachineKey并进行数据的加解密的,因此可以写出下面的测试代码用于生成payload:
可以拿到payload:
随后,使用下面的代码进行测试,传入上面生成的payload
运行,可以成功实现RCE
这个姿势就要比SessionSecurityTokenHandler要实用一些了,因为算是windows推荐给开发者的姿势,并且要利用的话只需要获取MachineKey配置项中的密钥,利用思路和之前介绍过的ViewState安全问题是很类似的,拿到web.config配置信息,并且目标代码中有类似的写法,就可以实现攻击了。
2.Plugins的实现
上面我们操作了这么多,只是抱着试验的态度去验证了攻击MachineKeySessionSecurityTokenHandler的可行性,若是想实现一个好用的插件,还是有很大问题的,我们上面的操作依赖于完整的web环境,总不能把整个WEB环境打包到工具里吧?
所以接下来得研究MachineKeySessionSecurityTokenHandler Cookie的生成逻辑,研究一下.NET取出web.config中的MachineKey配置后,都经过了哪些流程才实现了加密流程。这里流程其实也比较长,就长话短说了。其核心是一个Protect()方法:
System.Web.Security.Cryptography.NetFXCryptoService.Protect()
在这里,乍一看数据结构和逻辑环环相扣,我差点想从头把整个生成逻辑抠出来,非常绝望,不过后面理清了之后发现并没有那么令人绝望,首先看到NetFXCryptoService构造方法
首先看到老朋友encryptionKey和validationKey,都是CryptographicKey类的对象,后续关于它们的调用主要是两个地方:
那么跟进CryptographicKey以及相关方法
非常好理解,这里我简单猜测就是把encryptionKey和validationKey字符串转化为byte[]形式传入这个类。就暂时不去寻找具体的调用赋值流程。继续看Protect()方法
可以看到还有一个多次使用的关键属性_cryptoAlgorithmFactory,这里先是调用
_cryptoAlgorithmFactory.GetEncryptionAlgorithm()
拿到了一个SymmetricAlgorithm类的对象,然后给Key属性赋值,值即为encryptionKey的byte[]形式
再接着又调用
_cryptoAlgorithmFactory.GetValidationAlgorithm()
拿到了一个KeyedHashAlgorithm类的对象,然后还是给Key赋值,值即为validationKey的byte[]形式
看起来加密和验签都和_cryptoAlgorithmFactory密切相关,_cryptoAlgorithmFactory是ICryptoAlgorithmFactory类型的对象
而在本案例中,其具体是MachineKeyCryptoAlgorithmFactory类的对象
并且注意到
_encryptionAlgorithmFactory
和
_validationAlgorithmFactory
都为null,这个后续对我们有用,先记着。
跟进MachineKeyCryptoAlgorithmFactory
其构造方法接受一个MachineKeySection类的对象,乍一看我们还得去研究MachineKeySection的结构,如果你这时候再跟进MachineKeySection,就会发现还有一大堆东西要去继续分析,第一次看的我非常绝望。但实际上这样就是走远了。我们之所以需要
MachineKeyCryptoAlgorithmFactory
是因为上面调用了它里面的GetEncryptionAlgorithm()和GetValidationAlgorithm()两个方法,其它的东西实际上和我们没有关系的,那我们可以先看看这两个方法做了什么,首先是GetEncryptionAlgorithm()
当_encryptionAlgorithmFactory为null时,调用
GetEncryptionAlgorithmFactory()
跟进来就很明了了,这里显然是根据MachineKey配置文件中的加密算法来返回相应的委托
GetValidationAlgorithm()也是同理:
因此我们根本不需要关注其它的实现,只需要返回相关的委托即可!后续会返回委托执行结果
因此上述各种复杂的委托,实际上是执行下面这样的方法,并接受其结果:
CryptoAlgorithms.CreateAes()
并且其最终的返回都是类似下面这样的:
new AesCryptoServiceProvider()
这就是一个Public的类了,这样调用就很方便了,最终我们只需要调用上面这样的方法即可,这样就凑齐所有条件了
然后最抽象的是,我刚把最核心的逻辑分离出来,上网找相关资料的时候发现早就有人实现过了,直接导包就行了,浪费一晚上时间(
最后实现代码如下:
运行可以拿到Payload:
替换原来的测试代码进行试验:
成功RCE
3.第一次pull request
第一次给开源项目做贡献,怪紧张的,新手也来记录一下,首先先给ysoserial.net原项目fork一下
然后git clone回来:
git clone https://github.com/YOUR_USERNAME/PROJECT.git
然后在这个文件夹使用git设置原项目地址,便于随时pull最新的修改:
git remote add upstream https://github.com/pwntester/ysoserial.net.git
然后创建一个本地分支
然后开始改代码即可,我这边主要做了如下的工作,首先来到ysoserial.net的Plugins目录,添加一个新的.cs文件,名为
MachineKeySessionSecurityTokenHandlerPlugin
根据ysoserial.net的插件的语法规则编写新的插件:
然后使用Nuget添加AspNetTicketBridge依赖,再添加System.IdentityModel.Services的引用
修改完之后,使用以下命令添加更改到暂存区:
git add .
然后,提交这些更改,简短的说明一下都干了什么:
git commit -m "add MachineKeySessionSecurityTokenHandlerPlugin"
提交更改后,将这些更改推送到GitHub上你的分支。
git push origin ysoserial.net_addMachinePlugin
确保自己的文件都更新好之后,访问自己fork的地址:
可以看到多了一个按钮,点一下,然后在后续进入的界面中说明你都做了哪些改动以及改动的原因,最后再提交即可