George0Papasotiriou/CVE-2026-3456-OAuth2-PKCE-Race-Condition-Account-Takeover-
GitHub: George0Papasotiriou/CVE-2026-3456-OAuth2-PKCE-Race-Condition-Account-Takeover-
该项目演示了 OAuth2 PKCE 流程中因授权码未原子性失效而导致的竞态条件漏洞,并提供了完整的模拟服务器与利用脚本。
Stars: 0 | Forks: 0
## 8. CVE-2026-3456 – OAuth2 PKCE 竞态条件(账户接管)
### 概述
OAuth2 授权服务器中的竞态条件允许攻击者在合法客户端兑换之前重用受害者的授权码,方法是在极短的时间窗口内猜测或暴力破解 PKCE code_verifier。
**严重程度:** 高(账户接管)
### 模拟(Python HTTP 服务器)
```
#!/usr/bin/env python3
"""
vulnerable_auth_server.py - Authorization server with race condition window.
"""
import time, random, hashlib, base64, secrets
from http.server import HTTPServer, BaseHTTPRequestHandler
import urllib.parse
# 模拟 storage
auth_codes = {} # code -> {client_id, redirect_uri, code_challenge, scope, user}
tokens = {}
def generate_code():
return secrets.token_urlsafe(16)
class AuthHandler(BaseHTTPRequestHandler):
def do_GET(self):
parsed = urllib.parse.urlparse(self.path)
params = urllib.parse.parse_qs(parsed.query)
if parsed.path == '/authorize':
# User approves, redirect with code
code = generate_code()
auth_codes[code] = {
'client_id': params.get('client_id', ['unknown'])[0],
'redirect_uri': params.get('redirect_uri', [''])[0],
'code_challenge': params.get('code_challenge', [''])[0],
'user': 'victim@example.com'
}
redirect = f"{params['redirect_uri'][0]}?code={code}&state={params.get('state',[''])[0]}"
self.send_response(302)
self.send_header('Location', redirect)
self.end_headers()
elif parsed.path == '/token':
# Token endpoint (POST) but simplified as GET for demo
code = params.get('code', [''])[0]
verifier = params.get('code_verifier', [''])[0]
if code in auth_codes:
entry = auth_codes[code]
# Check PKCE: SHA256(verifier) == challenge?
challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).decode().rstrip('=')
if challenge == entry['code_challenge']:
# Race condition: we do not invalidate code immediately (small window)
# Attacker can try to redeem same code with a different verifier if they win race.
access_token = secrets.token_urlsafe(32)
tokens[access_token] = entry['user']
# Insecure: code still present for a few milliseconds
# To simulate, we add a deliberate delay
time.sleep(0.1) # window of opportunity
del auth_codes[code] # remove after use (but after sleep)
self.send_response(200)
self.end_headers()
self.wfile.write(f'access_token={access_token}'.encode())
else:
self.send_response(400)
self.end_headers()
self.wfile.write(b'invalid code_verifier')
else:
self.send_response(400)
self.end_headers()
self.wfile.write(b'invalid code')
else:
self.send_response(404)
self.end_headers()
server = HTTPServer(('0.0.0.0', 5000), AuthHandler)
print("Auth server on :5000")
server.serve_forever()
```
# CVE-2026-3456 – OAuth2 PKCE 竞态条件(账户接管)

## 📖 概述
OAuth2 授权服务器的 PKCE 流程中存在一个漏洞,允许攻击者通过竞态条件在合法客户端之前兑换授权码,从而绕过 PKCE。服务器未能原子性地使授权码失效,留下了一个可以尝试恶意 verifier 的时间窗口。
## ⚙️ 漏洞详情
- **类型:** 竞态条件 / TOCTOU
- **影响:** 通过获取受害者的 access token 进行账户接管。
- **根本原因:** token endpoint 会检查 PKCE、签发 token,然后移除授权码,但该授权码在短暂的时间内仍然有效。获取到授权码的攻击者(例如,通过 open redirect)可以并发尝试多个 verifier。
## 🧪 漏洞利用演示
1. 启动有漏洞的授权服务器:
python vulnerable_auth_server.py
2. 运行竞态条件漏洞利用:
python race_condition_exploit.py
标签:OAuth2, Python, 安全漏洞, 无后门, 漏洞环境, 竞态条件, 账户接管, 逆向工具