OAuth的奇妙世界:漏洞赏金版
OAuth的奇妙世界:漏洞赏金版
如果你曾经访问过网站,那么你可能在某个时候遇到过OAuth,即使你从未听说过它。你见过“使用Google登录”按钮吗?如果见过,那么你就遇到过OAuth!本文将简要讨论OAuth(特别是OAuth 2.0)是什么,以及从安全角度来看它可能如何被错误地实现。特别是,它将重点介绍我在漏洞赏金狩猎中遇到的许多实现问题。
“快速”入门
当我们谈论OAuth时,有几个不同的版本和授权类型需要考虑。要了解这些,我建议阅读https://oauth.net/2/以获取基础知识。在本文中,我们将重点介绍目前最常见的流程,即OAuth 2.0授权码授权类型。本质上,OAuth为开发人员提供了一种授权机制,允许一个应用程序从另一个应用程序(授权服务器)访问数据或针对你的账户执行某些操作。
例如,假设网站https://yourtweetreader.com具有显示你发送的所有推文(包括私密推文)的功能。为了实现这一点,引入了OAuth 2.0。https://yourtweetreader.com会要求你授权他们的Twitter应用程序访问你的所有推文。一个同意页面将在https://twitter.com上弹出,显示请求的权限以及请求的开发者是谁。一旦你授权请求,https://yourtweetreader.com将能够代表你访问你的推文。现在,这是一个非常高层次的描述,实际上还有一些复杂性。以这个例子为例,以下是OAuth 2.0上下文中需要理解的一些重要元素:
- resource owner:资源所有者是授予访问其受保护资源(如他们的Twitter账户推文)的用户/实体。
- resource server:资源服务器是在应用程序代表资源所有者获取访问令牌后处理认证请求的服务器。在上面的例子中,这将是https://twitter.com。
- client application:客户端应用程序是请求资源所有者授权的应用程序。在这个例子中,这将是https://yourtweetreader.com。
- authorization server:授权服务器是在成功认证资源所有者并获得授权后向客户端应用程序颁发访问令牌的服务器。在上面的例子中,这将是https://twitter.com。
- client_id:client_id是应用程序的标识符。这是一个公开的非秘密唯一标识符。
- client_secret:client_secret是只有应用程序和授权服务器知道的秘密。用于生成access_token。
- response_type:response_type是一个值,用于详细说明请求的令牌类型,例如code。
- scope:scope是客户端应用程序请求从资源所有者获得的访问级别。
- redirect_uri:redirect_uri是授权完成后用户被重定向到的URL。通常必须与之前注册的服务重定向URL匹配。
- state:state参数可以在用户被定向到授权服务器并返回时保持数据。重要的是这是一个唯一值,因为如果每个请求包含一个唯一或随机值,它可以作为CSRF保护机制。
- grant_type:grant_type参数解释了授权类型是什么,以及将返回哪个令牌。
- code:此代码是从授权服务器接收的授权代码,将在此请求的查询字符串参数“code”中。客户端应用程序使用此代码与client_id和client_secret一起获取access_token。
- access_token:access_token是客户端应用程序用于代表资源所有者发出API请求的令牌。
- refresh_token:refresh_token允许应用程序在不提示用户的情况下获取新的access_token。
好吧,这本来是一个快速入门,但似乎对于OAuth,你无法简单地给出一个简短的描述。将所有这些放在一起,这是一个真实的OAuth流程:
- 你访问https://yourtweetreader.com并点击“与Twitter集成”按钮。
- https://yourtweetreader.com向https://twitter.com发送请求,要求你(资源所有者)授权https://yourtweetreader.com的Twitter应用程序访问你的推文。请求将如下所示:https://twitter.com/auth ?response_type=code &client_id=yourtweetreader_clientId &redirect_uri=https%3A%2F%2Fyourtweetreader.com%2Fcallback &scope=readTweets &state=kasodk9d1jd992k9klaskdh123
- 你将看到一个同意页面:
- 一旦接受,Twitter将向redirect_uri发送一个带有code和state参数的请求:https://yourtweetreader.com?code=asd91j3jd91j92j1j9d1&state=kasodk9d1jd992k9klaskdh123
- https://yourtweetreader.com将获取该code,并使用其应用程序的client_id和client_secret,从服务器发出请求以代表你获取access_token,这将允许他们访问你同意的权限:POST /oauth/access_token Host: twitter.com ... { "client_id": "yourtweetreader_clientId", "client_secret": "yourtweetreader_clientSecret", "code": "asd91j3jd91j92j1j9d1", "grant_type": "authorization_code" }
- 最后,流程完成,https://yourtweetreader.com将使用你的access_token向Twitter发出API调用以访问你的推文。
漏洞赏金发现
现在,有趣的部分来了!在OAuth实现中,很多事情都可能出错,以下是我经常看到的不同类型的漏洞:
弱redirect_uri配置
这可能是每个人在寻找OAuth实现漏洞时更常见的问题之一。redirect_uri非常重要,因为授权后敏感数据(如code)会附加到此URL。如果redirect_uri可以重定向到攻击者控制的服务器,这意味着攻击者可以通过自己使用code来潜在地接管受害者的账户,并获取受害者的数据。
利用这一点的方式将因授权服务器而异。有些只接受与客户端应用程序中指定的完全相同的redirect_uri路径,但有些会接受redirect_uri同一域或子目录中的任何内容。
根据服务器处理的逻辑,有许多技术可以绕过redirect_uri。在redirect_uri为https://yourtweetreader.com/callback的情况下,这些包括:
- 开放重定向:https://yourtweetreader.com/callback?redirectUrl=https://evil.com
- 路径遍历:https://yourtweetreader.com/callback/../redirect?url=https://evil.com
- 弱redirect_uri正则表达式:https://yourtweetreader.com.evil.com
- HTML注入和通过referer头窃取令牌:https://yourtweetreader.com/callback/home/attackerimg.jpg
state参数处理不当
这是我迄今为止在OAuth实现中最常见的问题。很多时候,state参数被完全省略或以错误的方式使用。如果state参数不存在,或者是一个从不更改的静态值,OAuth流程很可能容易受到CSRF攻击。有时,即使有state参数,应用程序也可能不对参数进行任何验证,攻击仍然有效。利用这一点的方法是:在你自己的账户上完成授权过程,并在授权后暂停。然后你会遇到如下请求:
https://yourtweetreader.com?code=asd91j3jd91j92j1j9d1
在你收到此请求后,你可以丢弃该请求,因为这些代码通常是一次性使用的。然后你可以将此URL发送给已登录的用户,它将把你的账户添加到他们的账户中。起初,这听起来可能不太敏感,因为你只是将你的账户添加到受害者的账户中。然而,许多OAuth实现用于登录目的,因此如果你可以添加用于登录的Google账户,你可能通过一次点击执行账户接管,因为使用你的Google账户登录将让你访问受害者的账户。
我还多次看到state参数用作额外的重定向值。应用程序将使用redirect_uri进行初始重定向,但随后使用state参数作为第二次重定向,其中可能包含查询参数或referer头中的code。
需要注意的是,这不仅适用于登录和账户接管类型的情况。我在以下情况下看到过配置错误:
- Slack集成允许攻击者将他们的Slack账户添加为所有通知/消息的接收者
- Stripe集成允许攻击者覆盖支付信息并接受来自受害者客户的付款
- PayPal集成允许攻击者将他们的PayPal账户添加到受害者的账户,这将把钱存入攻击者的PayPal
基于邮箱的账户分配
我看到的另一个更常见的问题是,当应用程序允许“使用X登录”但也允许用户名/密码登录时。有两种不同的攻击方式:
- 如果应用程序在账户创建时不要求邮箱验证,尝试在受害者注册之前使用受害者的邮箱地址和攻击者的密码创建一个账户。如果受害者随后尝试使用第三方(如Google)注册或登录,应用程序可能会进行查找,发现该邮箱已注册,然后将他们的Google账户链接到攻击者创建的账户。这是一种“预账户接管”,攻击者将在受害者注册之前拥有受害者账户的访问权限。
- 如果OAuth应用程序不要求邮箱验证,尝试使用受害者的邮箱地址注册该OAuth应用程序。可能存在与上述相同的问题,但你将从另一个方向攻击它,并获得对受害者账户的访问权限以实现账户接管。
密钥泄露
识别哪些OAuth参数是秘密的并保护它们非常重要。例如,泄露client_id是完全可以的且必要的,但泄露client_secret是危险的。如果泄露,攻击者可能会利用受信任客户端应用程序的信任和身份来窃取用户access_tokens和集成账户的私有信息/访问权限。回到我们之前的例子,我看到的一个问题是从客户端而不是服务器执行此步骤:
- https://yourtweetreader.com将获取该code,并使用其应用程序的client_id和client_secret,从服务器发出请求以代表你获取access_token,这将允许他们访问你同意的权限。
如果从客户端完成此操作,client_secret将被泄露,用户将能够代表应用程序生成access_tokens。通过一些社会工程,他们还可以向OAuth授权添加更多范围,并且所有请求都将看起来合法,因为它们来自受信任的客户端应用程序。
结束语
在OAuth实现中,还有很多其他攻击和可能出错的地方,但这些是你将看到的一些更常见的问题。这些配置错误出奇地常见,并且有大量漏洞来自这些错误。我本打算让“快速入门”相当简短,但很快意识到所有知识对于文章的其余部分都是必要的。鉴于此,大多数开发人员不会知道安全实现的所有细节也就不足为奇了。通常情况下,这些问题都是高严重性的,因为它涉及私有数据泄露/操纵和账户接管。我希望在某个时候更详细地介绍这些类别中的每一个,但希望这能作为一个一般性介绍,并提供需要注意的事项的想法!
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
公众号二维码

 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号