OAuth to Account takeover

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

Basic Information

OAuth 提供多种版本,基础信息可在 OAuth 2.0 documentation 获取。本讨论主要集中在广泛使用的 OAuth 2.0 authorization code grant type,它提供了一个授权框架,允许一个应用访问或代表用户在另一个应用(the authorization server)的账户上执行操作。

考虑一个假想网站 https://example.com,用于展示你所有的社交媒体帖子,包括私密帖。为此使用了 OAuth 2.0。https://example.com 会请求你授权其访问你的社交媒体帖子。随后在 https://socialmedia.com 会出现一个同意屏幕,列出所请求的权限及发起请求的开发者。在你授权后,https://example.com 将能够代表你访问你的帖子

在 OAuth 2.0 框架中,需要理解以下组件:

  • resource owner: 你,作为授权访问你资源的用户/实体,例如你的社交媒体账号帖子。
  • resource server: 在应用代表 resource owner 获取到 access token 之后,负责处理已认证请求的服务器,例如 https://socialmedia.com
  • client application: 向 resource owner 请求授权的应用,例如 https://example.com
  • authorization server: 在 resource owner 成功认证并授权后向 client application 签发 access tokens 的服务器,例如 https://socialmedia.com
  • client_id: 应用的公开唯一标识符。
  • client_secret: 只有应用和 authorization server 知晓的机密密钥,用于生成 access_tokens
  • response_type: 指定请求的 token 类型的值,例如 code
  • scope: client application 向 resource owner 请求的访问级别
  • redirect_uri: 授权后用户被重定向到的 URL。这通常必须与预先注册的 redirect URL 对应。
  • state: 用于在用户重定向到授权服务器来回传递数据的参数。其唯一性对于作为 CSRF 保护机制至关重要。
  • grant_type: 指明授权类型及要返回的 token 类型的参数。
  • code: 来自 authorization server 的授权 code,客户端与 client_idclient_secret 一起使用它来换取 access_token
  • access_token: client application 代表 resource owner 发起 API 请求时使用的 token。
  • refresh_token: 允许应用在不再次提示用户的情况下获取新的 access_token

Flow

实际的 OAuth 流程如下:

  1. 你访问 https://example.com 并点击 “Integrate with Social Media” 按钮。
  2. 站点随后向 https://socialmedia.com 发送请求,询问你是否授权 https://example.com 的应用访问你的帖子。请求结构如下:
https://socialmedia.com/auth
?response_type=code
&client_id=example_clientId
&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
&scope=readPosts
&state=randomString123
  1. 随后会向你展示一个同意页面。
  2. 在你批准后,Social Media 会将带有 codestate 参数的响应发送到 redirect_uri
https://example.com?code=uniqueCode123&state=randomString123
  1. https://example.com 使用该 code,连同其 client_idclient_secret,发起服务器端请求以代表你获取 access_token,从而访问你同意的权限:
POST /oauth/access_token
Host: socialmedia.com
...{"client_id": "example_clientId", "client_secret": "example_clientSecret", "code": "uniqueCode123", "grant_type": "authorization_code"}
  1. Finally, the process concludes as https://example.com employs your access_token to make an API call to Social Media to access

漏洞

Open redirect_uri

根据 RFC 6749 §3.1.2,授权服务器必须只将浏览器重定向到 pre-registered, exact redirect URIs。这里的任何弱点都允许攻击者通过一个恶意的 authorization URL 将受害者送到攻击者控制的端点,使得 IdP 将受害者的 code(和 state)直接发送到攻击者,随后攻击者可以兑换并窃取 tokens。

典型攻击流程:

  1. 构造 https://idp.example/auth?...&redirect_uri=https://attacker.tld/callback 并发送给受害者。
  2. 受害者完成身份验证并批准 scopes。
  3. IdP 重定向到 attacker.tld/callback?code=<victim-code>&state=...,攻击者记录该请求并立即兑换该 code。

常见的验证漏洞待检测:

  • No validation – any absolute URL is accepted, resulting in instant code theft.
  • Weak substring/regex checks on the host – 可通过类似域名绕过,例如 evilmatch.commatch.com.evil.commatch.com.mxmatchAmatch.comevil.com#match.commatch.com@evil.com
  • IDN homograph mismatches – 验证发生在 punycode 形式(xn--),但浏览器会重定向到攻击者控制的 Unicode 域名。
  • Arbitrary paths on an allowed host – 将 redirect_uri 指向 /openredirect?next=https://attacker.tld 或任何 XSS/用户内容端点会通过级联重定向、Referer 头或注入的 JavaScript leaks the code。
  • Directory constraints without normalization – 像 /oauth/* 这样的模式可以被 /oauth/../anything 绕过。
  • Wildcard subdomains – 接受 *.example.com 意味着任何 takeover(dangling DNS、S3 bucket 等)都会立即产生有效的 callback。
  • Non-HTTPS callbacks – 允许 http:// URI 会让网络攻击者(Wi-Fi、corporate proxy)有机会在传输中截取 code。

还应检查辅助的重定向类参数(client_uripolicy_uritos_uriinitiate_login_uri 等)以及 OpenID discovery document(/.well-known/openid-configuration),以查找可能继承相同验证漏洞的其他端点。

XSS in redirect implementation

如该漏洞赏金报告所述 https://blog.dixitaditya.com/2021/11/19/account-takeover-chain.html,可能存在重定向 URL 在用户认证后被服务器反射在响应中,从而 vulnerable to XSS。可测试的可能 payload:

https://app.victim.com/login?redirectUrl=https://app.victim.com/dashboard</script><h1>test</h1>

CSRF - Improper handling of state parameter

state 参数是 Authorization Code 流程中的 CSRF token:客户端必须为每个浏览器实例生成一个 加密学上随机的值,并将其持久化到只有该浏览器可读的位置(cookie、local storage 等),在 authorization 请求中发送该值,并拒绝任何未返回相同值的响应。只要该值是静态的、可预测的、可选的或未绑定到用户会话,攻击者就可以完成他们自己的 OAuth 流程,截获最终的 ?code= 请求(不发送它),并在稍后强制受害者浏览器重放该请求,从而使受害者账号被链接到攻击者的 identity provider 配置文件。

重放模式总是相同的:

  1. 攻击者用自己的账号在 IdP 进行认证并截获包含 code(以及任何 state)的最后一次重定向。
  2. 他们丢弃该请求,保存 URL,随后利用任意 CSRF 原语(链接、iframe、自动提交表单)强制受害者浏览器加载该请求。
  3. 如果客户端不强制校验 state,应用会消费攻击者的 authorization 结果并将攻击者登录到受害者的应用账号。

state 处理的实用测试清单:

  • 缺少 state 完全 – 如果参数从未出现,整个登录流程就是可 CSRF 的。
  • state 非必需 – 从初始请求中移除它;如果 IdP 仍然签发客户端接受的 codes,那么该防护是可选的(opt-in)。
  • 返回的 state 未被验证 – 在响应中篡改该值(Burp、MITM proxy)。接受不匹配的值意味着存储的 token 从未被比较。
  • 可预测或纯数据驱动的 state – 许多应用将重定向路径或 JSON blob 塞进 state 而未混入随机性,允许攻击者猜测有效值并重放流程。始终在编码数据前后加入强随机熵。
  • state 固定(state fixation) – 如果应用允许用户提供 state 值(例如,通过精心构造的 authorization URL)并在整个流程中重用,攻击者可以锁定一个已知值并在多个受害者间重用它。

PKCE 可以补充 state(尤其对 public clients),通过将 authorization code 与 code verifier 绑定,但 web 客户端仍必须追踪 state 以防止跨用户 CSRF/账号关联漏洞。

Pre Account Takeover

  1. 在创建账号时不进行邮箱验证:攻击者可以预先使用受害者的邮箱创建一个账号。如果受害者后来使用第三方服务登录,应用可能会无意中将该第三方账号链接到攻击者预先创建的账号,从而导致未授权访问。
  2. 利用宽松的 OAuth 邮箱验证:攻击者可能利用不验证邮箱的 OAuth 服务,先用自己的服务注册,然后将账号邮箱改为受害者的邮箱。该方法同样有导致未授权访问的风险,与第一种场景通过不同的攻击向量实现类似效果。

Disclosure of Secrets

client_id 本来就是公开的,但 client_secret 永远不应被终端用户恢复。在 mobile APK、desktop clients 或 single-page apps 中嵌入 secret 的 Authorization Code 部署,实际上把该凭证交给任何能下载该包的人。检查 public clients 时应始终:

  • 解包 APK/IPA、桌面安装包或 Electron 应用并 grep 查找 client_secret、可解码为 JSON 的 Base64 blob,或硬编码的 OAuth 端点。
  • 审查捆绑的配置文件(plist、JSON、XML)或反编译后的字符串以寻找 client 凭证。

一旦攻击者提取出 secret,他们只需窃取任意受害者的 authorization code(通过弱 redirect_uri、日志等)即可独立调用 /token 并铸造 access/refresh tokens,而无需涉及合法应用。将 public/native clients 视为 无法保管 secrets —— 它们应依赖 PKCE (RFC 7636) 来证明持有每个实例的 code verifier,而不是静态 secret。测试时,确认 PKCE 是否为强制,并且后端是否确实拒绝省略 client_secret 有效 code_verifier 的 token 交换。

Client Secret Bruteforce

你可以尝试 暴力破解服务提供商的 client_secret,以尝试窃取账号。
请求以 BF 的形式可能看起来类似于:

POST /token HTTP/1.1
content-type: application/x-www-form-urlencoded
host: 10.10.10.10:3000
content-length: 135
Connection: close

code=77515&redirect_uri=http%3A%2F%2F10.10.10.10%3A3000%2Fcallback&grant_type=authorization_code&client_id=public_client_id&client_secret=[bruteforce]

Referer Header leaking Code + State

一旦客户端获得了 code and state,如果在他浏览到不同页面时这些值被反射在 Referer header 内,则存在漏洞。

Access Token Stored in Browser History

The core guarantee of the Authorization Code grant is that access tokens never reach the resource owner’s browser。当实现把 tokens leak 到客户端时,任何轻微的漏洞(XSS、Referer leak、proxy logging)都会导致账号立即被攻陷。始终检查:

  • Tokens in URLs – 如果 access_token 出现在 query/fragment 中,它会出现在浏览器历史、server logs、analytics,以及发送给第三方的 Referer headers 中。
  • Tokens transiting untrusted middleboxes – 通过 HTTP 或通过调试/企业代理返回 tokens 会让网络观察者直接捕获它们。
  • Tokens stored in JavaScript state – React/Vue stores、全局变量,或序列化的 JSON blobs 会向同源的每个脚本(包括 XSS payloads 或恶意扩展)暴露 tokens。
  • Tokens persisted in Web StoragelocalStorage/sessionStorage 会在共享设备上于登出后长时间保留 tokens,并且脚本可访问。

上述任何一项通常会把原本“低危”的漏洞(比如 CSP bypass 或 DOM XSS)升级为完整的 API 接管,因为攻击者可以简单地读取并重放被泄露的 bearer token。

Everlasting Authorization Code

Authorization codes 必须是短时、一次性且有重放检测的。在评估一个流时,抓取一个 code 并:

  • Test the lifetime – RFC 6749 建议以分钟为单位,而不是小时。尝试在 5–10 分钟后兑换该 code;如果仍然有效,则任何被泄露 code 的暴露窗口过长。
  • Test sequential reuse – 连续发送同一个 code 两次。如果第二次请求再次返回 token,说明攻击者可以无限克隆会话。
  • Test concurrent redemption/race conditions – 并行触发两个 token 请求(Burp intruder、turbo intruder)。弱实现有时会同时授予两个。
  • Observe replay handling – 重放尝试不仅应该失败,还应撤销任何已从该 code 签发的 token。否则,检测到的重放会让攻击者的第一个 token 仍然有效。

将易被重放的 code 与任意 redirect_uri 或日志问题结合,会允许在受害者完成合法登录后仍然保持对账号的持久访问。

Authorization/Refresh Token not bound to client

如果你能得到 authorization code 并在不同的 client 上使用它,那么你就能 takeover 其他账号。

Happy Paths, XSS, Iframes & Post Messages to leak code & state values

Check this post

AWS Cognito

在这份 bug bounty 报告:https://security.lauritz-holtmann.de/advisories/flickr-account-takeover/ 中你可以看到 AWS Cognito 返回给用户的 token 可能拥有足够的权限去覆盖用户数据。因此,如果你能够 change the user email for a different user email,你可能能够 take over 其他人的账号。

# Read info of the user
aws cognito-idp get-user --region us-east-1 --access-token eyJraWQiOiJPVj[...]

# Change email address
aws cognito-idp update-user-attributes --region us-east-1 --access-token eyJraWQ[...] --user-attributes Name=email,Value=imaginary@flickr.com
{
"CodeDeliveryDetailsList": [
{
"Destination": "i***@f***.com",
"DeliveryMedium": "EMAIL",
"AttributeName": "email"
}
]
}

For more detailed info about how to abuse AWS Cognito check AWS Cognito - Unauthenticated Enum Access.

滥用其他应用的 tokens

As mentioned in this writeup, OAuth flows that expect to receive the token (and not a code) could be vulnerable if they not check that the token belongs to the app.

This is because an attacker could create an application supporting OAuth and login with Facebook (for example) in his own application. Then, once a victim logins with Facebook in the attackers application, the attacker could get the OAuth token of the user given to his application, and use it to login in the victim OAuth application using the victims user token.

Caution

Therefore, if the attacker manages to get the user access his own OAuth application, he will be able to take over the victims account in applications that are expecting a token and aren’t checking if the token was granted to their app ID.

According to this writeup, it was possible to make a victim open a page with a returnUrl pointing to the attackers host. This info would be stored in a cookie (RU) and in a later step the prompt will ask the user if he wants to give access to that attackers host.

To bypass this prompt, it was possible to open a tab to initiate the Oauth flow that would set this RU cookie using the returnUrl, close the tab before the prompt is shown, and open a new tab without that value. Then, the prompt won’t inform about the attackers host, but the cookie would be set to it, so the token will be sent to the attackers host in the redirection.

Prompt Interaction Bypass

As explained in this video, some OAuth implementations allows to indicate the prompt GET parameter as None (&prompt=none) to prevent users being asked to confirm the given access in a prompt in the web if they are already logged in the platform.

response_mode

As explained in this video, it might be possible to indicate the parameter response_mode to indicate where do you want the code to be provided in the final URL:

  • response_mode=query -> The code is provided inside a GET parameter: ?code=2397rf3gu93f
  • response_mode=fragment -> The code is provided inside the URL fragment parameter #code=2397rf3gu93f
  • response_mode=form_post -> The code is provided inside a POST form with an input called code and the value
  • response_mode=web_message -> The code is send in a post message: window.opener.postMessage({"code": "asdasdasd...

OAuth consent/login dialogs are ideal clickjacking targets: if they can be framed, an attacker can overlay custom graphics, hide the real buttons, and trick users into approving dangerous scopes or linking accounts. Build PoCs that:

  1. Load the IdP authorization URL inside an <iframe sandbox="allow-forms allow-scripts allow-same-origin">.
  2. Use absolute positioning/opacity tricks to align fake buttons with the hidden Allow/Approve controls.
  3. Optionally pre-fill parameters (scopes, redirect URI) so the stolen approval immediately benefits the attacker.

During testing verify that IdP pages emit either X-Frame-Options: DENY/SAMEORIGIN or a restrictive Content-Security-Policy: frame-ancestors 'none'. If neither is present, demonstrate the risk with tooling like NCC Group’s clickjacking PoC generator and record how easily a victim authorizes the attacker’s app. For additional payload ideas see Clickjacking.

OAuth ROPC flow - 2 FA bypass

According to this blog post, this is an OAuth flow that allows to login in OAuth via username and password. If during this simple flow a token with access to all the actions the user can perform is returned then it’s possible to bypass 2FA using that token.

ATO on web page redirecting based on open redirect to referrer

This blogpost comments how it was possible to abuse an open redirect to the value from the referrer to abuse OAuth to ATO. The attack was:

  1. Victim access the attackers web page
  2. The victim opens the malicious link and an opener starts the Google OAuth flow with response_type=id_token,code&prompt=none as additional parameters using as referrer the attackers website.
  3. In the opener, after the provider authorizes the victim, it sends them back to the value of the redirect_uri parameter (victim web) with 30X code which still keeps the attackers website in the referer.
  4. The victim website trigger the open redirect based on the referrer redirecting the victim user to the attackers website, as the respose_type was id_token,code, the code will be sent back to the attacker in the fragment of the URL allowing him to tacke over the account of the user via Google in the victims site.

SSRFs parameters

Check this research For further details of this technique.

Dynamic Client Registration in OAuth serves as a less obvious but critical vector for security vulnerabilities, specifically for Server-Side Request Forgery (SSRF) attacks. This endpoint allows OAuth servers to receive details about client applications, including sensitive URLs that could be exploited.

Key Points:

  • Dynamic Client Registration is often mapped to /register and accepts details like client_name, client_secret, redirect_uris, and URLs for logos or JSON Web Key Sets (JWKs) via POST requests.
  • This feature adheres to specifications laid out in RFC7591 and OpenID Connect Registration 1.0, which include parameters potentially vulnerable to SSRF.
  • The registration process can inadvertently expose servers to SSRF in several ways:
  • logo_uri: A URL for the client application’s logo that might be fetched by the server, triggering SSRF or leading to XSS if the URL is mishandled.
  • jwks_uri: A URL to the client’s JWK document, which if maliciously crafted, can cause the server to make outbound requests to an attacker-controlled server.
  • sector_identifier_uri: References a JSON array of redirect_uris, which the server might fetch, creating an SSRF opportunity.
  • request_uris: Lists allowed request URIs for the client, which can be exploited if the server fetches these URIs at the start of the authorization process.

Exploitation Strategy:

  • SSRF can be triggered by registering a new client with malicious URLs in parameters like logo_uri, jwks_uri, or sector_identifier_uri.
  • While direct exploitation via request_uris may be mitigated by whitelist controls, supplying a pre-registered, attacker-controlled request_uri can facilitate SSRF during the authorization phase.

OAuth providers Race Conditions

If the platform you are testing is an OAuth provider read this to test for possible Race Conditions.

Mutable Claims Attack

In OAuth, the sub field uniquely identifies a user, but its format varies by Authorization Server. To standardize user identification, some clients use emails or user handles. However, this is risky because:

  • Some Authorization Servers do not ensure that these properties (like email) remain immutable.
  • In certain implementations—such as “Login with Microsoft”—the client relies on the email field, which is user-controlled by the user in Entra ID and not verified.
  • An attacker can exploit this by creating their own Azure AD organization (e.g., doyensectestorg) and using it to perform a Microsoft login.
  • Even though the Object ID (stored in sub) is immutable and secure, the reliance on a mutable email field can enable an account takeover (for example, hijacking an account like victim@gmail.com).

Client Confusion Attack

In a Client Confusion Attack, an application using the OAuth Implicit Flow fails to verify that the final access token is specifically generated for its own Client ID. An attacker sets up a public website that uses Google’s OAuth Implicit Flow, tricking thousands of users into logging in and thereby harvesting access tokens intended for the attacker’s site. If these users also have accounts on another vulnerable website that does not validate the token’s Client ID, the attacker can reuse the harvested tokens to impersonate the victims and take over their accounts.

Scope Upgrade Attack

The Authorization Code Grant type involves secure server-to-server communication for transmitting user data. However, if the Authorization Server implicitly trusts a scope parameter in the Access Token Request (a parameter not defined in the RFC), a malicious application could upgrade the privileges of an authorization code by requesting a higher scope. After the Access Token is generated, the Resource Server must verify it: for JWT tokens, this involves checking the JWT signature and extracting data such as client_id and scope, while for random string tokens, the server must query the Authorization Server to retrieve the token’s details.

Redirect Scheme Hijacking

In mobile OAuth implementations, apps use custom URI schemes to receive redirects with Authorization Codes. However, because multiple apps can register the same scheme on a device, the assumption that only the legitimate client controls the redirect URI is violated. On Android, for instance, an Intent URI like com.example.app:// oauth is caught based on the scheme and optional filters defined in an app’s intent-filter. Since Android’s intent resolution can be broad—especially if only the scheme is specified—an attacker can register a malicious app with a carefully crafted intent filter to hijack the authorization code. This can enable an account takeover either through user interaction (when multiple apps are eligible to handle the intent) or via bypass techniques that exploit overly specific filters, as detailed by Ostorlab’s assessment flowchart.

References

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks