CVE-2026-40594
Race condition in pyLoad's header handling allows cookie security downgrade.
- CVSS 4.8
- CWE-346
- Race Conditions
- Remote
pyLoad is a free and open-source download manager written in Python. Prior to 0.5.0b3.dev98, the set_session_cookie_secure before_request handler in src/pyload/webui/app/__init__.py reads the X-Forwarded-Proto header from any HTTP request without validating that the request originates from a trusted proxy, then mutates the global Flask configuration SESSION_COOKIE_SECURE on every request. Because pyLoad uses the multi-threaded Cheroot WSGI server (request_queue_size=512), this creates a race condition where an attacker's request can influence the Secure flag on other users' session cookies — either downgrading cookie security behind a TLS proxy or causing a session denial-of-service on plain HTTP deployments. This vulnerability is fixed in 0.5.0b3.dev98.
- CWE
- CWE-346
- CVSS base score
- 4.8
- Published
- 2026-04-21
- OWASP
- A07 Identification and Authentication Failures
- Orthogonal defect classification
- Timing/Serialization
- Code defect classification
- Timing Issues
- Category
- Race Conditions
- Subcategory
- Data Race Conditions in Threads
- Accessibility scope
- Remote
- Impact
- Denial of Service (DoS)
- Affected component
- pyLoad
- Fixed by upgrading
- Yes
Solution
Upgrade pyLoad to version 0.5.0b3.dev98 or later.
Vulnerable code sample
from flask import Flask, request, session
# This is a simplified representation of the pyLoad Flask application setup.
# The actual application is more complex, but this captures the essence
# of the vulnerability.
app = Flask(__name__)
# A secret key is required for Flask sessions.
app.config['SECRET_KEY'] = 'a-placeholder-secret-key'
@app.before_request
def set_session_cookie_secure():
"""
VULNERABLE FUNCTION:
This function is executed before every request.
It reads the X-Forwarded-Proto header and mutates the global
SESSION_COOKIE_SECURE configuration setting. It does not validate
that the request comes from a trusted proxy, allowing any client
to control this setting.
"""
app.config['SESSION_COOKIE_SECURE'] = request.headers.get(
"X-Forwarded-Proto") == 'https'
@app.route("/")
def index():
"""
A sample route that sets a session variable to demonstrate
that a session cookie is being created and affected by the
vulnerable function.
"""
session['user'] = 'example'
return "Session cookie created."Patched code sample
import os
from flask import Flask, request
from werkzeug.middleware.proxy_fix import ProxyFix
from flask.sessions import SecureCookieSessionInterface
# In a real application, this would be the main Flask app object.
app = Flask(__name__)
app.secret_key = os.urandom(24)
# This custom session interface fixes the race condition by determining the
# 'secure' flag on a per-request basis, rather than reading a mutable
# global configuration value.
class ThreadSafeSessionInterface(SecureCookieSessionInterface):
"""
A session interface that sets the 'Secure' attribute on session cookies
based on the security of the incoming request, which is made reliable by
the ProxyFix middleware. This prevents a race condition.
"""
def get_cookie_secure(self, app):
"""
Overrides the default behavior to use a thread-local, per-request
value instead of a global configuration variable.
"""
# request.is_secure is a thread-safe boolean that is correctly
# set by the ProxyFix middleware based on trusted headers.
return request.is_secure
# 1. The vulnerable `before_request` handler that modified app.config on every
# request is removed entirely.
# 2. A middleware is configured to securely process proxy headers.
# It trusts the X-Forwarded-Proto header from one trusted proxy (x_proto=1).
# This makes `request.is_secure` a reliable indicator of whether the
# original connection was made over HTTPS. An attacker spoofing the
# header from an untrusted IP is ignored.
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1)
# 3. Flask's default session handler is replaced with our thread-safe version.
# This ensures the 'Secure' flag for the session cookie is set correctly
# for each response without mutating shared global state.
app.session_interface = ThreadSafeSessionInterface()
# Example route to demonstrate session usage
@app.route("/")
def index():
return "Session cookie 'Secure' flag will be set correctly."Payload
curl -H "X-Forwarded-Proto: http" https://<pyload-instance-address>
Cite this entry
@misc{vaitp:cve202640594,
title = {{Race condition in pyLoad's header handling allows cookie security downgrade.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-40594},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-40594/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
