利用浏览器缓存绕过SOP
引言
在Keybase.io
的公开项目中寻找安全问题时,我注意到几个API端点启用了CORS
。对于部分不熟悉CORS的人而言,它意味着允许站点放松SOP(同源策略)以允许其他域能够与(最常见的)web API进行交互。在这篇文章中我将讨论我是如何通过Keybase
错误配置的CORS
策略来操纵浏览器缓存以返回私有用户数据的
概要
Keybase
提供了一个 API
,允许用户在加密消息时执行对其他 Keybase
用户的查找,类似于联系簿查找。它提供了其他用户的公共访问信息,这些信息在加密消息(如其公共PGP密钥)时是必需的。到目前为止还算安全,对吧?
在这个端点上实现的 CORS
策略如下所示:1
2
3
4Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Allow-Headers: Content-Type, Authorization, Content-Length, X-Requested-With
Access-Control-Allow-Credentials: false
为了理解这个问题,需要了解一些重要的方面:
Access-control-allow-origin
的通配符(*)值允许任何外部域与 API 交互并执行跨源查询- 使用通配符值实现了
‘ Access-Control-Allow-Credentials: false’
,这意味着不允许跨源验证请求(出于安全原因)。 这里为经过身份验证的请求公开的Authorization header
与利用的关系不大,因为它不需要查询公共API
。
问题
为了使这些跨源请求正常工作,自然不需要 CSRF
令牌,因为 API
被认为是公共的。使用 Keybase.io
的用户经过身份验证,他们的会话存储在 cookie
中,而更敏感的 API
端点需要在请求头部中使用授权令牌。
我注意到如果在验证时对自己进行查询(cookie
中会设置一个keybase.io
的session
),甚至当输入一封包含了我名字的信,我的个人账号将会同私人信息一起返回到搜索结果中,它包含了:
- 电子邮件地址
- 我已经使用的邀请码/还剩多少可用
- 账单信息
- 与最后登录有关的时间戳,以及电子邮件验证的时间 / 日期
- 私人
PGP key
(用TripleSec
加密)
我没有私人 PGP
密钥存储在 Keybase
,后来我了解到 Keybase.io
上的私人 PGP
密钥存储实际上是2015-2016年的遗留特性,现在已经不再实施。
一旦 cookie
从我的请求中移除到端点,我上面提到的私有信息就不再返回。然而,我注意到端点返回的响应中有一个'Etag'
头。 这个头是浏览器缓存的指示器,如果响应中的内容没有更改,则指示浏览器从其缓存中获取。
利用
我想起了我从@bitk 那里学到的一个技巧,在这个技巧中,可以使用 javascript
的获取API
方式强制跨源请求从浏览器缓存中直接获取。 Keybase
没有在响应中实现任何缓存控制头,所以我在本地创建了一个payload
(空源无关紧要) ,如下所示:1
2
3
4
5
6
7
8
9<html>
<script>
var url = "https://keybase.io/_/api/1.0/user/lookup.json?username={YOUR_USERNAME}";
fetch(url, {
method: 'GET',
cache: 'force-cache'
});
</script>
</html>
兄弟萌,看!
为了确认payload
是成功的,我们可以在下面看到 fetch
直接从浏览器缓存中拉出了响应。
时间线
19/12/2019 – 向 Keybase 报告该问题
19/12/2019 (2小时后) – 通过在响应中实现“ Cache-Control: no-store”头来纠正问题。
24/12/2019 – 公开披露
原文链接:
https://enumerated.wordpress.com/2019/12/24/sop-bypass-via-browser-cache/
Author: damn1t
Link: http://microvorld.com/2019/12/25/翻译文章/sop-bypass-via-browser-cache/
Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.