先知原创作者翻译:原文链接
在今年一月下旬,我向Google提交了一个reCAPTCHA认证绕过漏洞。当然,该漏洞的前提条件是,采用reCAPTCHA认证机制的Web应用程序以不安全的方式构造提交给/recaptcha/api/siteverify的请求;并且,只要符合这一条件,攻击者总能成功绕过reCAPTCHA认证机制。由于这个安全问题是通过修改Google的reCAPTCHA API进行修复的,所以,使用该认证机制的Web应用程序无需进行任何修改。
reCAPTCHA是Google提供的一种服务,借助该服务,Web应用程序开发人员就可以轻轻松松地为网站添加CAPTCHA功能了。然而,reCAPTCHA本身却并不简单,它提供了多种使用方式:有时,它会根据cookie对用户进行验证;有时,它会要求你解决多重“挑战”。下面我们来介绍与本文涉及的漏洞有关的使用场景。
当Web应用需要“刁难”用户时,Google会提供一个图像集,并通过JavaScript代码在浏览器中显示这些图像集,具体如下所示:
之后,用户需要做出相应的选择,并点击“Verify”按钮,这将触发对Web应用程序的HTTP请求。该HTTP请求的内容具体如下所示:
POST /verify-recaptcha-response HTTP/1.1
Host: vulnerable-app.com
recaptcha-response={reCAPTCHA-generated-hash}
该应用程序需要通过向Google reCAPTCHA API发送一个POST请求来验证用户的响应:
POST /recaptcha/api/siteverify HTTP/1.1
Content-Length: 458
Host: www.google.com
Content-Type: application/x-www-form-urlencoded
recaptcha-response={reCAPTCHA-generated-hash}&secret={application-secret}
该应用程序需要使用{application-secret}对自身进行身份验证,并将{reCAPTCHA-generated-hash}发送到API以查询响应。如果用户的答案是正确的,那么API将返回下列内容:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 90
{
"success": true,
"challenge_ts": "2018-01-29T17:58:36Z",
"hostname": "..."
}
Web应用程序会接收并处理上述内容,并根据处理结果授予用户相应的资源访问权限。
HTTP参数污染 "HTTP参数污染")几乎无处不在:从客户端到服务器端,不过,该漏洞的危害性却很大程度上取决于漏洞所在的上下文——在某些特定情况下,它可能导致危害巨大的数据泄露问题,但在大多数情况下,它只是一个低危漏洞。
reCAPTCHA绕过漏洞的前提条件,是相应的Web应用程序中存在HTTP参数污染问题。正是由于这个限制条件,导致Google严重低估了该漏洞的危害程度。
这里有一个包含reCAPTCHA绕过漏洞的Web应用程序示例:
private String sendPost(String CaptchaResponse, String Secret) throws Exception {
String url = "https://www.google.com/recaptcha/api/siteverify"+"?response="+CaptchaResponse+"&secret="+Secret;
URL obj = new URL(url);
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
其中,字符串连接用于构建url变量。
需要注意的是,在Google方面,如果向其发送这两个HTTP请求的话,会得到相同的响应:
POST /recaptcha/api/siteverify HTTP/1.1
Host: www.google.com
...
recaptcha-response={reCAPTCHA-generated-hash}&secret={application-secret}
POST /recaptcha/api/siteverify HTTP/1.1
Host: www.google.com
...
recaptcha-response={reCAPTCHA-generated-hash}&secret={application-secret}&secret={another-secret-application-secret}
reCAPTCHA API总是使用请求中的第一个secret参数,并忽略第二个secret参数。虽然这不是一个漏洞,但是,却可以被攻击者所利用。
Web开发人员需要以自动化的方式测试自己的应用程序,为此,Google提供了一种简单的方法来帮助开发人员在staging环境中“禁用”reCAPTCHA验证。这在谷歌的文档中有详细的记录和说明,简单来说,若想禁用reCAPTCHA验证,需要以硬编码的方式为site和secret参数指定如下所示的取值:
现在,我们已经掌握了所有的要素,下面来看一看漏洞利用方法:
POST /verify-recaptcha-response HTTP/1.1
Host: vulnerable-app.com
recaptcha-response=anything%26secret%3d6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe
如果该应用程序存在HTTP参数污染漏洞,并且URL是通过在参数secret之前添加response参数得到的,那么,攻击者就可以绕过reCAPTCHA验证。
请注意,我们需要为含有该绕过漏洞的Web应用程序发送一个精心构造的response参数,其中包含:
anything: 只是一个占位符
%26: 字符&的URL编码形式
secret:待“注入”的参数的名称
当满足攻击条件时,web应用程序会向reCAPTCHA API发送以下HTTP请求:
POST /recaptcha/api/siteverify HTTP/1.1
Host: www.google.com
Content-Type: application/x-www-form-urlencoded
User-Agent: Python-urllib/2.7
recaptcha-response=anything&secret=6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe&secret=6LeYIbsSAAAAAJezaIq3Ft_hSTo0YtyeFG-JgRtu
请注意,该请求包含两个secret参数,第一个是由攻击者控制的(由于易受攻击的Web应用程序中含有HTTP参数污染漏洞),第二个是由应用程序本身控制的。鉴于reCAPTCHA API总是使用第一个secret参数,所以,该请求的响应总是:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 90
{
"success": true,
"challenge_ts": "2018-01-29T17:58:36Z",
"hostname": "..."
}
因为上面的响应是由Web应用程序处理的,所以,攻击者将被授予相应的访问权限。
实际上,Google是在其REST API中修复这个安全问题的,并且我认为这是一个非常明智的举措。其实,Google的修复方法很简单:如果/request/passpt/api/siteverify的HTTP请求包含两个具有相同名称的参数,则返回错误。
通过这种修复方式,我们无需给含有HTTP参数污染和reCAPTCHA绕过漏洞的应用程序打任何补丁,就能提供相应的安全保护:真是太棒了!
要在Web应用程序中利用该漏洞,需要具备两个严苛的条件。首先,应用程序在构造reCAPTCHA url过程中存在HTTP参数污染漏洞:Github搜索显示,在集成了reCAPTCHA的Web应用程序中,约有60%的程序满足该要求。
其次,包含漏洞的Web应用程序需要先创建带有response参数的URL,然后再创建带有secret参数的URL,即“response=…&secret=…”。奇怪的是,几乎所有的应用程序都使用“response=…&secret=…”这种格式的URL。据我猜测,可能是由于Google的文档和代码示例就是这样做的,而其他人则直接复制了这种格式。 Google在这方面还是很幸运的——如果他们反过来这样做的话,这个漏洞会影响到更多的网站。GitHub搜索显示,只有5%到10%的reCAPTCHA实现方式符合这一要求。
因此,如果我想在野外利用这一漏洞的话,只有约3%的采用reCAPTCHA的网站会受到影响:这并不算太糟糕,因为与其他漏洞相比,这个占比还是很小的。
对于开发人员:切勿使用字符串连接来创建查询字符串,而应该使用字典来存储键和值,然后对其进行URL编码。
对于应用安全专家:HTTP参数污染漏洞是你的朋友。