转载

利用XML签名攻击绕过SAML2.0单点登录

简介

近期我们注意到新西兰很多网站希望使用SSO(Single Sign On)单点登录,替代传统的密码登录,这其中还包括了许多政府服务。当前最普遍的做法便是使用支持多种框架与开发语言的SAML 2.0标准。该机制将验证用户身份的SAML响应数据,在返回给浏览器的过程中,使用一个签名以防止数据被篡改。

不幸的是,大多数SAML使用者不会去验证响应是否正确,从而为绕过身份验证埋下伏笔。

SAML 2.0 SSO

当你在一个使用SAML2.0的网站上进行注册账号,有以下3类角色出现:

1.服务提供方(SP),我们想要访问的Web应用

2.当事人,登录的用户

3.身份提供方(IdP),进行身份验证

只有服务提供方获得了身份提供方所提供的信息,才可以实现访问Web应用的目的。

通过一个SAML请求,服务提供方重定向用户到身份提供方。身份提供方确认用户身份之后向服务提供方返回一个SAML响应。以下为给Web SSO发送消息的3种主要方法,用于传输这些消息的机制称为 SAML 绑定:

HTTP重定向绑定,在URL中直接包含SAML消息

HTTP POST绑定,在POST请求的body内包含SAML消息

HTTP工件(浏览器工件)绑定,发送一个随机token令牌,充当一个标识符从反向信道获取文档

有关SAML 2.0 绑定可参考IBM文档( 传送门 )前两个绑定存在一些严重的实现问题。

识别SAML响应

如前文所述,SAML响应一般通过类似如下的URL传递:

利用XML签名攻击绕过SAML2.0单点登录

或者是像如下的包含在POST请求体中传递:

利用XML签名攻击绕过SAML2.0单点登录

对于这两种形式,都是通过浏览器进行传递,因此能被攻击者操纵。另一方面,如果你看到类似如下的SAML工件:

利用XML签名攻击绕过SAML2.0单点登录

作为攻击者,你可能真的就没啥事情可做了。这些token解析成原始消息,之后通过反向信道取回。所以,除非你能访问目标的私人网络(或许在这里存在一个SSL/TLS漏洞),否则这些token对于攻击者来说是毫无用处的。

在传输过程中保护消息

问题就出在HTTP重定向及HTTP POST绑定。从身份提供方获取的文档是通过用户的浏览器来验证身份。因此在消息传输过程中很可能会被篡改,HTTP工具绑定则不受此问题影响。

如果这些消息没有得到足够保护,攻击者可以简单的修改返回的响应数据伪装身份。比如我以Tim的身份登录到身份提供方,之后我可以简单的修改响应文档伪装成Emmanuel。事实上,我们可以伪造整个响应,化身为Emmanuel。

当然,SMAL标准的作者也不是吃素的,他们已经尽最大的努力解决这个问题了。对该问题的解决方案便是为每一条消息附加一个XML签名,保护消息不被篡改。

XML签名标准

你可以将XML签名标准看作一头庞大又复杂的野兽,由一个有许多知名大神组成的工作小组设计。并旨在构建一个适应所有人的tamper-resistant XML文档解决方案。不幸的是,这也是常态一个适应所有人的方案变成了所有人都得去适应的方案。

有关XML签名标准可以参考 《XML 签名: 幕后》 ,以及《 技术分析:微软 Office 365平台SAML服务漏洞,可越权访问其他用户资源 》部分内容

关于XML实体攻击可以参考《 FreeBuf公开课:XML实体攻击 – 从内网探测到命令执行步步惊心 》

在一个标准应用的数字签名中,我们对需要签名的文档运行一个密码散列函数计算出hash,然后对该hash值应用数字签名算法。如果接收到的文档与原文档完全相同,签名生效。反之就算是只有一比特的差别,签名都是无效的,并且文档会被接收方拒绝。

不幸的是,XML签名有一个致命的特性:该标准允许我们对文档进行部分签名,来代替对整个文档的签名,之后将这个签名嵌入到本应该要验证的同一文档中,这也被称做内联签名。通过在签名文档中包含一个对文档局部的引用以实现签名,通常是引用XML元素的ID属性,但理论上允许使用任意符合XPath标准的对象作为表达式。例如我可以在一个文档内的任意位置写入指向“third to last element”的签名,或者类似的模糊表达式。

当验证一个XML签名时,仅仅确认“这是一个来自签名者的有效的签名吗?”这个问题是不够的。我们还得确认“签名是否存在?如果存在,指向了文档的正确位置么?正确应用了标准么?签发者所签发的签名有效么?”通常情况是,至少有一项未被验证。

SAML Raider入门指南

本文描述的所有攻击方法不需要很多工具就能够实现。不过通常情况下,Burp Suite的SAML Raider插件就能对常见情况进行测试。

检测

如前所述,签名可能出现在SAML消息中的不同位置并且覆盖消息的各个部分。通过保留消息内容,向其中增加新的内容,同时修改剩余部分的结构,我们可以构造出一个新的消息,从技术上讲这个新消息仍然是合法签名的,但是可能被SAML库解析为包含了已签名的关键内容,事实上这个关键内容并不存在。

每当服务提供方进行验证时,有一定的机率验证失败或者进行了不正确的验证,这都给了我们绕过签名验证的机会。打开Burp的拦截功能,捕获SAML请求,然后尝试对这些请求内容进行转换。每一次尝试都要根据一个新的、有效的登录动作来完成,因为通常会有一个Nonce阻止我们重复发送相同的请求。

对于重复测试,按照下面这样设置Burp的Proxy,更方便: 

利用XML签名攻击绕过SAML2.0单点登录

签名是否必需?

SAML标准要求所有经过非安全信道(如用户的浏览器)进行传输的消息都要有数字签名。不过,如果消息通过安全信道(如SSL/TLS反向通道)进行传输的话,签名就不是必需的。因此,我们发现SAML使用者在签名存在时,就进行验证;如果签名被删除,就跳过验证。软件基本上假设我们已经检查了来自非安全信道的消息已完成签名,然而并非如此。

所带来的影响是,我们能够直接删除签名,并篡改响应,就好像这里没有签名一样。SAML Raider插件可以很容易完成该测试。

利用XML签名攻击绕过SAML2.0单点登录

签名是否得到验证?

XML签名的验证极其复杂,因为XML签名标准预期在签名验证之前会进行一系列的转换和规范化步骤(e.g.会忽略多余的空白符)。这使得如果没有一个功能齐全的XML签名库在背后做支撑,那么验证签名就极其困难。影响如下:

1.开发者并非普遍理解签名验证的原理;

2.一些中间组件,如web应用防火墙(WAF),不知道签名是否有效;

3.签名库可能存在一些配置选项,如允许的规范化方法列表,这些选项对开发者来说毫无意义。

实现签名标准很困难,并且签名标准自身也存在晦涩难解的特性,这就导致了我们现在看到的问题。

首先,测试一个签名是否有效是很简单的。改变已完成签名内容中的某些数据,看看是否导致中断。

签名是来自正确的签名者吗?

另外一个绊脚石就是接收方是否验证了签名者的身份。我们无法看到这一步是否正确,但是SAML Raider插件可以很轻松的做到。

将签名证书复制到SAML Raider的证书存储区:

利用XML签名攻击绕过SAML2.0单点登录

保存之后对此证书进行自签名,所以现在我们便有了一个相同证书的自签名副本。

利用XML签名攻击绕过SAML2.0单点登录

这时我们就可以使用这个自签名证书对原始请求进行再签名,可以对整个消息进行签名,或者只对签断言部分签名。

利用XML签名攻击绕过SAML2.0单点登录

你能够辨别采用这两种选项那个更好,或者两种方式都试一下。

对响应的正确部分进行签名?

XSW攻击原理

SAML标准所允许的签名存在的位置仅有两处:

1.签名位于标签中,对标签及其子节点进行签名;

2.签名位于标签中,对标签及其子节点进行签名。

SAML标准对允许签名存在的位置以及允许被签名的内容都有具体的描述。

然而没有人为了使用SAML就完整地实现复杂的XML签名机制。该标准是通用的,标准的实现以及为此所开发的软件库也是如此。因此,就有了以下的“职责分离”:

1.XML签名库根据XML验证标准验证签名,它允许从任何地方签名任何内容;

2.SAML库期望XML签名库告诉它响应消息是否有效。

在两个组件之间往往缺少相关规则去规定哪些内容必须签名的。结果就是,我们经常可以对文档的不同部分进行签名,而在接收方看来签名依然有效。

通过复制文档的签名部分,并保证签名数据指向这些副本,我们可以将XML签名库验证的内容和SAML库需要的内容分开。

自动化进行XSW攻击

SAML Raider插件可以进行自动化攻击。

利用XML签名攻击绕过SAML2.0单点登录

尝试下拉菜单中的每一个选项,点击“Apply XSW”发送请求数据。如果没有出现错误,就改变SAML XML中所有的用户名或者其他用户标识符然后重复这个动作。

SAML Raider的局限性

尽管SAML Raider插件可以对常见的情况进行测试,但我们仍然有必要对此进行更深层次的理解:

1.生成一个将针对XML Schema进行验证的响应(需要在可能包含xs:any的元素中隐藏影子副本(shadow copy));

2.当Response和Assertion都被签名和验证时,如何绕过验证;

3.在非SAML上下文中绕过XML签名,如在使用WS-Security扩展的SOAP Endpoints中。

手动XSW

如果SAML Raider插件自带的选项都不起作用,你可以尝试手动测试方法:

1.对Base64编码的内容解码以获取SAML响应XML;

2.检查签名的标签是否包含被签名元素的ID;

3.复制文档中其他部分被签名的内容(一般情况下,放在标签的末尾是可以的;如果还要验证XML Schema,那就放在不会破坏XML Schema的位置);

4.从XML签名中删除副本,将其保留在原始文档中。这是有必要的,因为XML封装签名标准去掉了将被验证的签名。在原始文档中,这就是所包含的签名,所以我们必须将其从副本中删除);

5.改变原始签名内容的ID为其他值(如改变其中一个字母);

6.改变原始断言的内容;

7.对以上内容重新进行Base64编码,将其放回请求中,然后再提交请求。

如果签名验证指向副本,那么你所做的上述改动将被忽略。在实际操作过程中,如果服务器严格限制了请求时间,你应该快速地完成这些步骤。

SAML渗透测试检查表

1.SAML响应通过浏览器了吗?

2.响应内容是否被签名了?如果没有签名,尝试改变其内容。

3.如果删除签名数据,响应内容是否被接受?

4.如果我们使用其他证书重新进行签名,响应内容是否被接受?

5.使用SAML Raider自带的8种转换方式生成的结果是否被接受?

6.如果你更改此类响应,更改后的响应是否被接受?

7.是否遇到了上文所述的SAML Raider的局限性?如果是,你需要尝试手动测试方法。

*参考来源: Aurainfosec ,FB小编鸢尾编译,转载请注明来自FreeBuf(FreeBuf.COM)

原文  http://www.freebuf.com/articles/web/122365.html
正文到此结束
Loading...