导语:安全研究人员发现Oracle Access Manager中存在一个安全漏洞,可被远程攻击者利用绕过身份验证并接管任何用户的帐户。
去年11月,SEC密码咨询中心发现了一种相当有趣的由Oracle Access Manager(OAM)使用的加密格式。在这篇博文中,我们将演示密码实现的小缺陷如何对产品的安全性产生实际影响。通过利用此漏洞,我们可以制作任意身份验证令牌,模拟任何用户并有效攻破OAM的主要功能。
Oracle Access Manager (OAM)是Oracle融合中间件的组件,可处理各种Web应用程序的身份验证。在典型的情况下,为应用程序提供访问的Web服务器配备了认证组件(Oracle WebGate)。当用户向Web服务器请求受保护资源时,会将其重定向到OAM的身份验证终端。然后,OAM对用户进行身份验证(例如,使用用户名和密码)并将其重定向到Web应用程序。由于所有身份验证都由中央应用程序处理,因此用户只需进行一次身份验证即可访问受OAM保护的任何应用程序(单一登录)。
在某研究项目中,我们发现OAM使用的加密格式存在严重缺陷。通过利用此漏洞,我们能够制作会话令牌。当WebGate使用此令牌时,会接受它作为合法的身份验证形式,并允许我们访问受保护的资源。更重要的是,会话cookie制作过程中可以为任意用户名创建一个会话cookie,从而允许模拟OAM已知的任何用户。
一、影响范围
目前支持的两个版本,11g和12c都受到此漏洞的影响,此处描述的情况仅在版本12c中得到验证。一个简单的Google Dork可以找到大约11,800个OAM,其中一些指向重要的组织(包括Oracle本身)。这只是在互联网上找到的而已。
在2017年11月发现这个漏洞之后,我们负责任地向Oracle披露了此漏洞。Oracle的响应速度非常迅速,并在2018年4月提供了最新的重要补丁更新(CPU)修复。由于此修补程序在Oracle的定期更新计划中提供,OAM管理员现在已经应用了该修补程序。如果您的组织还没有完成的话,现在是时候这样做了!
此外,建议分析历史日志以查看此攻击留下的模式:成功的攻击会导致大量由于填充错误而导致的解密失败(javax.crypto.BadPaddingException)。
我们的安全咨询(security advisory )包含了有关受影响版本及供应商时间表。
二、技术细节
在技术层面上,在认证过程中会发生以下情况:
1.用户访问受保护的资源。
2.Web服务器中的Oracle Webgate组件通过重定向到OAM来响应此请求。加密的消息(encquery)通过URL参数传递。
3.用户根据OAM进行身份验证(例如,使用用户名和密码)。
4.OAM将用户重定向回Web服务器。有关成功登录的信息将通过参数encreply传递。
5.Web服务器将用户重定向到最初请求的资源。加密的身份验证令牌存储在cookie(OAMAuthnCookie)中。
6.Cookie中的身份验证令牌从现在起用于对用户进行身份验证。
encquery,enquply和OAMAuthnCookie中的值(应该是)加密保护以防修改。这样,当OAM或WebGate接收到其中一个值时,即使它们来自用户,内容也(应该)保证不被修改。OAM为所有这些消息使用单一加密格式。用于所有这些消息的密钥在OAM和WebGate之间共享。
(一)加密格式
正如之前已经暗示的那样,缺陷在于密码格式的实现。创建受保护消息的算法处理key-value对的输入,并使用共享密钥生成base64编码的输出字符串。这种格式的主要目的是提供完整性和真实性。这是算法的工作原理:
1.输入格式如下:
salt=<salt> <key>=<value> [...] validate=<base64 hash>
其中salt是一个随机生成的值,参数验证是对消息固定部分的MD5散列。
2.然后使用CBC模式中的分组密码对此字符串进行加密。
(二)漏洞
分析这种格式时首先想到的是,所用的加密算法(即散列和CBC块密码)都不是用来提供主要目标真实性的。由于攻击者不知道共享秘密,攻击是不可能的。
有密码学背景的人可能已经注意到了,最突出的一件事是CBC模式。如果使用不正确,可以使用oracle填充攻击来破解它。经典的oracle填充攻击需要密文和oracle填充。oracle填充是一个组件,揭示了所提供的加密字符串被解密时是否有一个有效的填充。一开始暴露这些信息的oracle起初看起来并没有用,但事实上,它允许我们解密和加密任意的信息。
简而言之,分组密码需要填充以便能够以任意长度加密消息。分组密码只能处理固定大小的块(例如16个字节)。如果我们想要例如加密长度为25个字节的消息,我们将加密前16个字节,然后保留9个字节。由于分组密码无法处理9个字节的输入,我们需要追加7个填充字节。典型的方法是添加填充字节,其中每个字节包含填充字节的数量(如PKCS#7中定义的)。在这种情况下,附加7个字节,每个字节为0x7。当不需要填充时,会添加一个完整的填充块(16个字节0x10)。
关于oracle填充攻击的解释超出了本文的范围。现在,我们需要找到一种方法来确定加密的字符串是否具有适当的填充。
要查看是否有可能发生oracle填充oracle攻击,我们需要观察系统对不能正确解填充的信息以及可以正确填充但之后未通过检查的信息(例如,如果填充的文本不能被正确解析)反应有何不同。当同时对两个测试用例进行encquery参数测试时,OAM会同时响应“系统错误” ,因此我们无法轻易区分这两种情况。
只要有任何不适合OAM的情况,就会显示“系统错误”。因此,为了区分填充不正确的消息和填充正确的消息,一种方法是在攻击中使用的所有填充消息完全正确合法。显然,当OAM遇到有效的消息时,它不会响应一个错误消息。如果解填充失败,我们仍然会看到错误消息。
(三)使Oracle填充工作
事实证明,如果OAM通过空格字符与有效消息分开,OAM将忽略任何附加到解密消息的填充。我们可以尝试创建一个最终具有空格字符的有效消息。然后追加我们想要测试的块来填充。
具有有效填充的解密消息看起来像这样:
<valid message> <decryption of tested blocks>07070707070707
OAM会首先检查填充(会成功),然后解析有效的消息。它会忽略该消息的其余部分。
然而,具有无效填充的解密消息看起来像这样:
<valid message> <decryption of tested blocks>8BA09FB1ABC543
OAM将检查填充,发现它是无效的并返回系统错误。
(四)空格:最后的边界
那么如何确保有效的信息后面跟着一个空格字符呢?—— 用暴力。
首先,我们需要创建一个长度恰好可以被块长度整除的有效消息。我们需要找到一种方法来影响明文,使得到的密文符合这个标准。事实证明,encquery包含用户最初请求的受保护的URL。我们可以捕获访问不同长度的URL所产生的加密值:
http://example.com/protected/? http://example.com/protected/?a http://example.com/protected/?aa http://example.com/protected/?aaa ...
只要encquery的长度增加了16个字节,我们知道加密的消息的长度可以被16整除,而最后一个块包含填充字节的长度:
|---------------|---------------| < valid msg>PPPP < valid msg >PPP < valid msg >PP < valid msg >P < valid msg >PPPPPPPPPPPPPPPP
这允许我们忽略最后一个块并继续使用仅包含加密字符串且不包含填充的加密字符串。然后我们确定,下面的块在第一个位置包含一个空格字符。
我们可以创建一个加密消息,其中包含没有填充的有效消息,我们选择的块以及原始消息的最后2个块(以保持填充有效)。我们可以任意选择加密消息中的块 ,但是,不能有意识地影响块解密后的结果文本。因此,我们可以继续尝试随机加密块,直到明文块符合需求。
|---------------|---------------|---------------|---------------| < valid msg >< brute force >< last 2 blocks of orig. msg >
如果解密后的有效消息后面没有空格字符,则该消息无效,并显示“系统错误”。我们将继续使用随机块构建消息,直到最终被OAM接受。然后我们知道,所选块的解密仅仅是偶然的,在第一个字节中包含一个空格字符:
|---------------|---------------|---------------|---------------| < valid msg > <random data >< last 2 blocks of orig. msg >
在这一步之后,攻击很简单:我们只是使用构造的消息作为我们想要测试有效填充的块的前缀。如果填充不正确,则解填充步骤失败,导致出现错误消息。如果填充是正确的,则OAM将正确地将消息解压缩,开始解析有效消息,并且没有错误消息进行响应。
|---------------|---------------|---------------|---------------|---------------|---------------| < valid msg > <random block >< last 2 blocks of orig. msg >< some text ><pad valid?>
(五)小结
oracle填充允许我们解密任何消息。由于所有加密的消息(encquery,encreply,OAMAuthnCookie)都使用相同的密钥加密,所以我们可以解密这些消息中的任何一个。
众所周知,oracle填充攻击也可以用来加密消息。因此,如果我们构建一个有效的身份验证cookie并使用我们的padding oracle攻击对其进行加密,那么我们可以将其传递给Web服务器。事实上,没有任何东西阻止我们这样做:由于加密字符串中的验证值是一个简单的哈希而不是HMAC,因此我们可以简单地计算它而不需要任何秘钥。
SEC Consult开发了PoC脚本,但目前我们不会发布它。
三、经验教训
你应该从这篇文章中学到的信息是坚持前两个密码学规则,第一个规则是:
“不要推出你自己的密码!”
第二:
“不要推出你自己的密码!”
密码学很难完全正确。即使使用算法的标准实现,设计适当的加密格式或协议也是具有挑战性的。很多时候,看似安全的实现可能会出现严重的漏洞———而且这远远超出了这里已经证明的颇为知名的填充攻击。只有当没有现成的解决方案时,才应该尝试设计密码格式———由密码学专家来!