深入考察SAML XML注入漏洞
2021-04-13 10:49:05 Author: www.4hou.com(查看原文) 阅读量:165 收藏

在进行身份验证控制和身份管理时,经常会使用单点登录(SSO)技术,由于该技术不仅便于用户使用,同时安全性也较高,所以,它很快就在各种组织和大型在线服务中流行开来。这样做的好处是显而易见的:对于终端用户来说,对单一服务进行身份验证就能获得对所有所需应用的访问权限,这让生活方便了许多;而对于管理员来说,可以在单个位置来控制凭据和权限。然而,凡事有利即有弊——这种便利性也为攻击者带来了新的机会:SSO身份验证流程中只要出现一个漏洞,就可能带来灾难性的后果,因为可能会暴露组织使用的所有服务中存储的数据。

在本文中,我们将为读者介绍NCC Group在对各种SSO服务进行安全审计过程中发现的一类漏洞,具体来说,就是会影响安全断言标记语言(SAML)实现的漏洞。这些漏洞允许攻击者修改身份提供方生成的SAML响应,从而获得对任意用户账户的未经授权的访问,或在应用程序中提升自己的权限。

什么是SAML?

首先,下面简要介绍一下SAML身份验证的具体流程。如果您已经熟悉SAML和SSO,则可以跳过本节。 

SAML是一个允许在不同上下文之间安全交换身份验证和授权数据的标准。它通常用于Web应用程序,以提供SSO功能,并可以轻松地与Active Directory进行集成,从而使其成为在企业环境中使用的应用程序的流行的身份验证方案。

身份验证过程依赖于两方之间的信任关系——身份提供方(对终端用户进行身份验证)和服务提供方(终端用户想要访问的应用)。在最常见的身份验证流程下,当用户想要访问服务提供方时,他们会被重定向到身份提供方,并发送SAML请求消息。

如果用户还没有登录,身份提供方就会对用户进行身份验证,如果验证成功,就会通过SAML响应消息(通常是在POST请求的主体中)将用户重定向到服务提供方。其中,SAML响应消息将包含一个确定用户身份的断言,并描述一些条件(响应的过期时间和受众限制,以说明该断言对哪种服务有效)。之后,服务提供方将该验证相应的响应、断言和条件,只有在身份验证成功的情况下才会向用户提供对应用程序的访问权限。 

为防止篡改,对于SAML的响应和断言来说,至少需要对其中的一种进行加密签名处理,或者两者都进行加密签名,以便服务提供方检查它们是否被出现被篡改的情况。使用这种签名技术后,恶意用户就很难修改断言中的用户标识符了,因为这样会导致签名不再有效。

关于SAML更深入的介绍,请参阅https://www.pingidentity.com/en/resources/client-library/articles/saml.html。

XML注入漏洞 

XML注入是一种介绍资料比较齐全的漏洞类别,它通常影响在后端利用XML或SOAP服务的“老式”Web应用。其中,一种比较常见的情况是用户输入被直接包含在发送到后端服务器的XML消息中。如果用户输入没有经过适当的验证或编码,攻击者就可以注入额外的XML,从而修改请求参数或调用额外的功能。虽然在某些应用中仍然受该漏洞的影响,但眼下XML注入已经不那么常见了,因为开发人员正在转向采用基于JSON、YAML和协议缓冲区等较新数据格式构建的服务。

然而,对于SAML身份提供方来说,XML注入仍然是一个需要密切关注的安全问题,因为在身份验证流程中构建的SAML消息是基于XML的,并且包含的数据通常来自不受信任的位置。如果这些数据被危险地包含在SAML断言或响应消息中,攻击者就有可能会注入额外的XML,并改变SAML消息的结构。根据注入的位置和服务提供方的配置,还可能注入额外的角色,修改断言的接收者,或者注入一个全新的用户名,以试图入侵另一个用户的帐户。最重要的是,SAML断言和响应的XML总是在应用加密签名之前建立的。因此,使用响应签名并不能防御这种漏洞。 

这种类型的漏洞最常见于SAML身份提供方,因为它们直接使用字符串模板来构建SAML XML消息,而用户控制的数据可以使用模板语言、正则表达式匹配/替换或简单串联等方式插入到模板字符串中。当然,这种漏洞并不只限于这种情况,即使是使用适当的库构建XML的实现,如果库的使用不正确,也可能成为这个漏洞的受害者。

在对SAML身份提供方进行安全评估的过程中,我们发现,攻击者可以成功地利用XML注入漏洞来修改签名的断言,从而获得对任意用户账户的未授权访问。

受影响的字段

在构建SAML响应和断言时,身份提供方可能会与由用户直接或间接控制的数据打交道。其中,显而易见的例子就是SAML NameID,它能唯一地标识用户(可能是数字标识符、用户名或电子邮件地址),以及服务提供方要求的附加属性,如用户的全名、电话号码或职业。

然而,在大多数的SAML实现中,有一些字段却是从SAML请求中获取的。下面提供了SAML请求中可能包含在SAML响应/断言中的字段(当然,这里并不是最全面的):

· SAML请求的ID通常包含在SAML响应的InResponseTo属性中。需要说明的是,在我们审查的身份提供方中,几乎所有的实现都是在SAML响应中包含了SAML请求的ID。因此,这个字段被认为是探测XML注入漏洞最可靠的字段。

· Issuer字段,用于标识SAML请求的发出者,可以包含在SAML断言的Audience字段中。

· IssueInstant字段,用于说明SAML请求生成的时间,可以包含在断言条件的NotBefore属性中。

· Destination字段,用于说明接收SAML请求的端点。该字段也可用于断言的Audience元素中。

此外,某些实现甚至包括来自基本SAML身份验证流程之外的位置的数据。例如,在一个SAML身份提供方中,如果从未经过身份验证的客户端接收到SAML请求,则服务器使用包含SAML请求的ID的GET参数发出对登录页面的重定向。当用户输入凭证时,服务器将使用GET参数ID查找与SAML请求相关联的服务提供方,然后,在InResponseTo属性中使用这个ID来建立SAML响应。因此,通过修改登录请求中的ID GET参数,攻击者就可以在SAML响应中注入额外的XML。

识别漏洞

这个漏洞可以使用常见的XML注入的探测payload来识别。根据在NCC Group安全评估期间观察到的情况,我们在本地环境中重新创建了以下示例。首先,为了确定是否有可能进行XML注入,我们使用了一个拦截代理来修改发送给身份提供方的SAML请求。该payload被插入到请求的ID属性中(下面加粗),其目的是为了从属性值中逃逸出来,并注入一个额外的属性值(ncctest);注意,该payload中的引号是XML编码的。这是为了确保请求的XML仍然有效;当这个值被身份提供方读取时,许多实现会对这些实体进行XML解码:

image.png

当身份提供方对此进行处理时,ID属性将直接放入SAML响应模板中,具体来说将被放入samlp:Response和saml:SubjectConfirmationData元素的InResponseTo属性中:

image.png

如果这个测试成功,则可以尝试在响应中注入额外的XML元素。虽然修改属性是很有趣的事情,但并不是特别有用;如果能够注入额外的XML,攻击者就能够修改SAML断言,并最终获得对另一个用户账户的未授权访问。

作为一个基本的测试,下面的SAML请求被用来注入一个额外的XML元素(ncc-elem)到响应中。与之前一样,引号和尖括号也是XML编码的。同时,请注意这里注入的元素还涉及另一个属性——这是为了确保身份提供方使用的模板中的引号是对称的,并且响应是有效的XML:

image.png

该请求会在SAML响应中生成以下XML:

image.png

实际上,类似的过程也可以用于其他注入点。例如,如果身份提供方在响应的Audience中提供了SAML请求的Issuer字段,则可以使用相关payload(例如以下内容)来注入其他元素。请注意,必须对尖括号(<和>)进行相应的编码处理:

 image.png 

该请求将在SAML断言中生成以下Audience元素:

image.png

对于用户属性来说,能否成功地将XML字符注入到SAML断言中,具体取决于身份提供方是如何更新和存储这些属性的;如果XSS防御措施阻止用户在其属性中存储尖括号等字符,则可能无法执行该攻击。在下面的例子中,将用户的名字设置为“Adam< / saml:AttributeValue > < ncc-test > aaaa < / ncc-test > < saml:AttributeValue >”会在断言中产生以下Attribute元素。在这种特殊情况下,有必要关闭saml:AttributeValue元素并创建一个新的AttributeValue元素以通过服务器执行的XML验证:

image.png  

漏洞的利用

对于SAML XML注入漏洞来说,识别起来那是相当简单的事情,但要想利用这些漏洞的话,可就没有这么容易了。至于漏洞能否成功利用,需要否取决于多种因素,包括注入点发生的位置,用于签署和解析SAML响应的库对无效XML的容忍度,以及服务提供方是否会信任注入的有效载荷。事实上,即使可以对某些身份提供方进行XML注入,但是,某些服务提供方仍然可能会拒绝或忽略修改后的有效载荷。之所以出现这种情况,并不是因为签名无效,而是因为文档出现了重复的元素所致。

这个漏洞的特性将意味着,在许多情况下,有必要注入重复的元素或构建全新的断言。由此导致的问题包括:

· 服务提供方可能会选择自己创建的原始合法元素(断言或NameID),而不是选择注入的元素。许多XML库在选择文档中重复的元素时,会有不同的表现;通常情况下,这要么是第一次出现,要么是最后一次出现。

· 某些有安全意识的服务提供方可能会完全拒绝包含重复元素的响应;例如,一个断言通常没有充分的理由包含两个NameID元素。

· 如果服务提供方包含了针对XML签名封装(XSW)攻击的防御措施,这种攻击也可能失败(有关XML签名封装攻击的详细介绍,请参见https://www.ei.ruhr-uni-bochum.de/media/nds/veroeffentlichungen/2012/08/22/BreakingSAML_3.pdf)。这是一种较为常见的SAML漏洞:攻击者通过修改SAML响应的结构,试图欺骗服务提供方从一个无签名的元素中读取用户的身份(例如,在SAML响应中具有合法签名断言之前添加第二个未签名断言)。虽然XML注入攻击意味着这两个断言都会包含在SAML响应签名的范围内,但仅仅是第二个断言元素的存在就足以让一些服务提供方拒绝该消息。

漏洞利用示例

在NCC Group进行的评估中,针对这种漏洞的攻击经常出现在以下两种情况中:

· 属性注入:这种注入漏洞通常出现在身份提供方中与帐户相关的SAML属性中。

· InResponseTo注入:这种注入漏洞会影响SAML响应的“InResponseTo”属性。 

下一节提供了针对这两种情况的示例利用。由于篇幅的原因,我们不可能在本文中演示对SAML实现的所有可能的XML注入攻击,因此,希望这些示例能够为读者带来一些启发。实际上,对于这里介绍的漏洞利用技术来说,只要稍加改造,就能用于测试受该漏洞影响的大部分身份提供方。

免责声明:这些示例是在专门为演示这些漏洞利用过程而构建的本地环境中复现的。

属性注入

除了NameID(这是用户的唯一标识符)之外,SAML响应还可以包含一组对服务提供方有用的用户属性。这些属性是可选的,并且没有特别的要求;通常情况下,它们用于发送用户的姓名、电子邮件地址和电话号码等数据。此外,有些服务提供方还会使用角色属性或类似的属性,以便在通过身份验证后,为用户分配相应的权限。因此,如果这些属性没有被适当地编码,攻击者就可以注入或修改属性来实现提权,或以其他方式获取服务提供方中的敏感数据。 

作为一个例子,假设SAML断言包含一个AttributeStatement,其中包括两个属性;一个是用户的全名,另一个是用户的角色(viewer):

 image.png

那么,攻击者就可以将其在身份提供方中的名称改为如下所示的值:

image.png

如果身份提供方在没有经过适当验证的情况下将这个值包含在name属性中,那么,就会向服务提供方发送以下AttributeStatement。这样的话,攻击者就能够以 “administrator”而非“viewer”的角色进行身份验证:

image.png

请注意,属性元素“role”是重复的,因此,如果服务提供方读取了第二个role属性值,或者验证器拒绝了该断言,攻击就有可能失败。如果攻击者控制了两个属性(如姓名和电子邮件地址),就能使用XML注释来有效地删除身份提供方生成的角色属性。接下来,我们以下面的AttributeStatement为例进行演示,其中包括用户的电子邮件地址、角色和一个名字属性:

image.png

其中,role属性包含在email和name属性之间。因此,攻击者可以将其电子邮件地址和名称设置为以下值:

image.png

当身份提供方创建AttributeStatement元素时,将生成以下XML,其中角色属性“viewer”将被包含在一个XML注释中:

image.png

当服务提供方完成解析后,该用户将在管理员的上下文下对应用程序进行身份验证。

在利用SAML消息中的XML注入时,注释是一个有用的工具。如果应用得当的话,通常可以藉此控制SAML响应或断言的大部分内容,这意味着:攻击者可以利用注释来有效突破服务提供方施加的各种限制。值得注意的是,SAML实现所使用的大多数XML签名方案都会在计算签名之前对XML文档进行规范化,作为这个过程的一部分,注释会从文档中删除。换句话说,在计算签名时不会考虑SAML响应中的注释,因此可以在提交给服务提供者之前完全删除这些注释。如果有可能将XML注入SAML响应中的两个位置,那么通过使用XML注释,成功利用该漏洞的胜算就会大得多。

InResponseTo与断言注入

当SAML请求ID被危险地包含在响应中时,就会导致危害InResponseTo属性的注入漏洞。如前所述,绝大多数SAML标识提供程序都在响应中反映SAML请求ID的值,因此,这被认为是探测注入漏洞时的一个非常可靠的属性。然而,利用这种类型的注入漏洞则可能非常困难。主要的原因是这个值包含在SAML响应中的两个位置;第一个是在响应元素的InResponseTo属性中,第二个是在断言中的SubjectConfirmationData元素的InResponseTo属性中。

下面是受此漏洞影响的身份提供方(托管在本地服务器上)生成的SAML响应示例。可以看到,InResponseTo属性中含有值“_6c4ac3bd08f45c9f34a9230c39ef7e12ede0531e46”,这是服务提供方在SAML请求中设置的:

 image.png

大多数攻击者的目标是注入一个新的断言,其中包括一个不同的NameID,从而获得对另一个用户在服务提供方上的账户的访问权。对于下面的有效载荷(为了可读性而进行了解码和格式化),如果设法将其放入发送给身份提供方的SAML请求的ID中时,就可以实现这一目标。

image.png

下面,我们对这个有效载荷中的几个元素做些必要的解释:

1. 首先,“>用于从InResponseTo属性中“逃逸”出来,进入XML上下文。

2. 在注入的XML中,包含了从身份提供方观察到的其他响应中包含的Issuer和Status元素的副本。

3. 然后,创建一个全新的断言,其中的NameID指定了电子邮件地址“[email protected]”。这个断言是使用服务器生成的合法响应的断言建立的;NameID字段和NotOnOrAfter属性(指定未来的时间)以及InResponseTo属性都被修改为包括SAML请求的ID。通过替换这些值,可以确保服务提供方不会拒绝该断言,因为它将期待一个没有过期的断言,并且是为它之前发出的SAML请求生成的。

4. 最后,在结尾处打开一个无关的元素 "elem",并且,该元素含有一个属性。这样做的目的,为了修复由身份提供方创建的Response和SubjectConfirmationData元素在注入点所在的位置留下的悬空标记。但请注意,这一步是可选的,其必要性取决于XML解析器的容忍度:如果悬空标记不是元素的一部分,一些解析器将拒绝XML文档,而另一些解析器将简单地将悬空标记视为额外的文本节点。如果服务器拒绝没有这个元素的有效载荷,请尝试在另一个SAML请求中包含它。

下面的SAML请求包含了这个有效载荷,并为传输进行了编码处理:

image.png

身份提供方收到此消息后,将生成以下SAML响应。 注入的XML已以粗体突出显示,但是请注意,在身份提供方插入XML签名时已对XML进行了相应的调整:image.png

需要注意的是,由于存在两个注入点,因此这个SAML响应将包含三个断言,一个是使用XML注入有效载荷注入的,第二个是由身份提供方生成的(使用合法的NameID,即[email protected] ),还有一个注入的断言嵌入在合法的断言中(在第二个InResponseTo属性的位置)。如前所述,对这种SAML响应的处理将取决于服务提供方的配置。在我们所进行的测试中,易受攻击的身份提供方连接到一个SimpleSAMLphp安装;该安装接受了SAML响应,并使用第一次出现的断言来验证用户的身份,这意味着攻击者是在[email protected]的上下文中登录服务的。

如果服务提供方使用第二个断言而不是第一个断言,或者由于重复出现的断言而拒绝响应,则可以再次利用XML注释来有效地从响应中删除身份提供方的断言。在我们进行的测试中,有两种方法被成功使用。对于第一种方法:如果服务提供方使用的XML解析器不是非常严格的话,则只需在有效载荷的末尾留下一个没有终止的注释即可。身份提供方可能会忽略该注释尚未结束的情况,并仅使用攻击者的断言为响应生成签名。下面给出了一个可以实现这一目的的有效载荷的示例(为便于阅读,已进行了解码和格式化):image.png

当身份提供方生成SAML响应时,“<!–”字符串后面的内容将被忽略,从而有效地删除了身份提供方的断言以及反映第二个InResponseTo插入点的第二个断言。

但是,某些身份提供方将拒绝该有效负载,因为XML无效且带有未终止的注释。为了绕过这种限制,可以使用如下所示的有效载荷(同样,为了便于阅读,这里进行了相应的解码和格式化处理):

image.png

该有效载荷利用了这样一个事实,即在身份提供方生成的SAML响应中,内容会重复两次。其中,注释和CDATA块的组合用于关闭身份提供方的断言,并注入新的断言。有效载荷可以分解为以下几个部分:

1. 首先,第一个InResponseTo属性中使用引号进行转义,然后创建一个新的属性‘ncc-injection’。这个属性的值使用的是单引号,这样就可以保留注入断言的XML中的双引号。

2. 属性值内的有效载荷包括一个结束注释字符串“–>”,后面是恶意断言XML。这与之前的有效载荷类似,但止于SubjectConfirmationData元素,因为这是第二个InResponseTo属性出现的地方。

3. 在断言XML之后,属性值包括用于打开CDATA块的字符串。

4. 然后,通过单引号和尖括号来关闭ncc-injection属性和Response元素。

5. “<!–”字符串用于打开一个新的注释;这个注释将包含身份提供方的断言。

6. 然后是一个“]]>”字符串,以关闭CDATA块。

7. 最后,提供一个新的元素“ncc-elem”,并且该元素需要带有一个属性;这样做,是为了呼应身份提供方创建的InResponseTo属性留下的引号字符。(注意:同样,这个元素可能不是必需的,具体取决于XML解析器的实现情况)。

当被易受攻击的身份提供方处理时,会生成以下XML。请注意,第一个注入的断言,由于是在“samlp:Response”“ncc-injection”属性中关闭的,所以并不会被激活。同时,注释关闭了身份提供方断言的第一部分,其中指定了用户名“[email protected]”。然后,当在身份提供方的断言的第二个InResponseTo属性中重复有效载荷时,“->”字符串将会关闭注释,从而将恶意XML释放出来。该恶意XML结束于SubjectConfirmationData元素(CDATA块开始的位置)处;这个CDATA块被设计成关闭第二个注释字符串“<!-”,以防止断言/响应XML的剩余部分被注释掉。最后,“ncc-elem”元素用于实现引号的配对,而身份提供方断言模板的其余部分则关闭XML,从而创建一个有效的SAML响应:

image.png

根据InResponseTo属性在XML文档中的位置,有时需要调整有效载荷,以确保XML及其格式正确无误。

但是,对于InResponseTo注入漏洞的利用来说,还是有些事情需要进行说明。这个特殊的注入攻击之所以成功,完全是因为SAML响应中的断言没有进行签名。不过,一些身份提供方会同时对断言和SAML响应进行签名。在这种情况下,仅可能利用第二个InResponseTo注入点,因为在进行签名后,对此断言的任何修改都可能导致验证失败。因此,这个利用方法的细节,会因身份提供方的实现以及用于解析和签名XML的库而有所不同。

小结

依赖SAML进行身份验证的组织和服务,应仔细检查身份提供方,并确定它们是否受到XML注入漏洞的影响,特别是如果身份提供方使用基于字符串的模板来构建含有用户控制数据的SAML响应/断言。在理想的情况下,应该使用适当的XML库来构造SAML响应和断言,而这些库应该能够安全地在属性和文本节点中设置受用户控制的数据。

如果绝对有必要使用字符串模板或字符串函数在SAML消息中包含受用户控制的数据,则应严格验证数据的合法性。如果在用户输入中检测到XML字符,则应使用错误消息拒绝身份验证尝试。在插入文档之前,应将XML编码应用于数据,以确保即使绕过了验证,用户输入也无法注入其他XML。 

此外,如果可能的话,请考虑对服务提供方发送的SAML身份验证请求强制使用签名。如果身份提供方对SAML请求签名进行验证,就能检测到任何通过修改请求以包含XML注入有效负载的尝试(例如利用InResponseTo属性的尝试)。

本文翻译自:https://research.nccgroup.com/2021/03/29/saml-xml-injection/如若转载,请注明原文地址:


文章来源: https://www.4hou.com/posts/WpKn
如有侵权请联系:admin#unsafe.sh