VAITP Dataset

← Back to the dataset

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.

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 ::