标准激活自我实现伟大
本周@decoder_it和@splinter_code披露了一种滥用DCOM/RPC NTLM中继攻击访问远程服务器的新方法。这基于一个事实:如果您以用户身份登录到会话0(例如通过PowerShell远程处理)并调用CoGetInstanceFromIStorage,DCOM激活器将在最低交互会话而非会话0上创建对象。一旦对象创建,IStorage对象的初始解组将在认证到该会话的用户上下文中进行。如果该用户恰好是特权用户(如域管理员),则NTLM认证可能被中继到远程服务器,从而引发问题。
此攻击的明显问题在于需要处于会话0。当然,非管理员用户可能被允许通过PowerShell远程处理认证到系统,但这比在终端服务器上认证并攻击其他用户要罕见得多。如果能以某种方式选择对象创建的会话,那就更好了。
当然,这已经存在,您可以使用会话moniker跨会话激活对象(除了特殊的会话0)。我曾多次滥用此功能进行跨会话攻击,例如此、此或此。我多次告诉Microsoft需要修复此激活路径,因为非管理员能够执行此操作毫无意义。但我的警告未被重视。
如果您阅读会话moniker的描述,可能会注意到一个问题:它不能与IStorage激活结合使用。COM API只提供其中之一。然而,如果您仔细研究DCOM协议文档,会发现它们在技术上是独立的。会话激活通过设置SpecialPropertiesData激活属性中的dwSessionId字段指定。而封送的IStorage对象可以在InstanceInfoData激活属性的ifdStg字段中传递。您将这些激活属性打包并发送到IRemoteSCMActivator的RemoteGetClassObject或RemoteCreateInstance方法。当然,这可能无法真正工作,但至少它们是独立的属性,可以混合使用。
测试此问题的问题在于实现DCOM激活很麻烦。激活属性首先需要在blob中进行NDR封送。然后需要正确打包才能发送到激活器。此外,文档仅适用于远程激活,这不是我们想要的,而且本地激活有一些奇怪的特性,我不打算深入探讨。是否有任何文档化的方式可以访问激活器而无需完成所有这些工作?
不,抱歉。但如果您感兴趣,有一种未记录的方式?当然?好的,我们继续。这类挑战的关键在于查看系统如何 already 执行此操作。具体来说,我们可以查看会话moniker如何激活对象,也许从中我们可以幸运地 reuse 它用于我们自己的目的。
从哪里开始?如果您阅读此MSDN文章,您可以看到需要调用MkParseDisplayNameEx将字符串解析为moniker。但这实际上是MkParseDisplayName的包装器,以提供我们不关心的URL moniker功能。我们将从OLE32中的MkParseDisplayName开始。
|
|
几乎 immediately 我们看到对FindSessionMoniker的调用,似乎有希望。深入研究该函数,我们找到了我们需要的内容。
|
|
此代码解析会话moniker数据,然后创建CSessionMoniker类的新实例。当然,这还没有进行任何激活。您不单独使用会话moniker,而是应该构建一个包含new或class moniker的复合moniker。MkParseDisplayName API将继续解析字符串(这就是为什么pchEaten被更新)并组合它找到的每个moniker。因此,如果您有moniker显示名称:
|
|
API将返回一个复合moniker,由会话3的会话moniker和CLSID 0002DF02-0000-0000-C000-000000000046(Browser Broker)的类moniker组成。示例代码然后在复合moniker上调用BindToObject,该函数首先调用最右侧的moniker,即类moniker。
|
|
pmkToLeft参数由复合moniker设置为左侧moniker,即会话moniker。我们可以看到类moniker调用会话moniker的BindToObject方法,请求IClassActivator接口。然后它调用GetClassObject方法,传递要激活的CLSID。我们快到了。
|
|
最后,会话moniker创建一个具有IStandardActivator接口的新COM激活器对象。然后它查询ISpecialSystemProperties接口并设置moniker的会话ID和控制台状态。然后它在IStandardActivator上调用StandardGetClassObject方法,您现在应该拥有一个跨会话的COM服务器。当然,这些接口或类都没有官方文档记录(AFAIK)。
关键问题是,您是否也可以通过IStandardActivator接口进行IStorage激活?在COMBASE中深入研究该接口的实现,您会发现其功能之一是:
|
|
似乎答案是肯定的。当然,可能仍然无法将两者混合。这就是为什么我用C#编写了一个快速而粗糙的示例,可在此处获取。似乎工作正常。当然,我还没有用实际漏洞测试它是否在该场景中工作。这是其他人要做的事情。