CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-PW6J-QG29-8W7F

GHSA-pw6j-qg29-8w7f: State Persistence and Sensitive Credential Leakage in Tornado CurlAsyncHTTPClient

Amit Schendel
Amit Schendel
Senior Security Researcher

Jun 16, 2026·7 min read·41 visits

Executive Summary (TL;DR)

Tornado's CurlAsyncHTTPClient reuses pycurl handles without resetting configuration state, leading to client certificate and proxy credential leakage to unintended destinations.

A state persistence vulnerability exists in Tornado's CurlAsyncHTTPClient component where pooled pycurl.Curl handles are reused across asynchronous requests without a complete state reset. Consequently, sensitive per-request configurations, such as client TLS certificates or proxy basic authentication credentials, persist on the shared handle. This behavior leads to subsequent requests leaking these credentials to unauthorized remote servers.

Vulnerability Overview

The Tornado web framework provides multiple backends for handling asynchronous HTTP requests. The CurlAsyncHTTPClient is an advanced backend designed as a wrapper around pycurl, the Python bindings for the C-based libcurl library. This backend is frequently selected in performance-critical environments because of its speed, multi-socket scheduling capabilities, and support for complex network topologies. To achieve high request throughput, CurlAsyncHTTPClient maintains a internal pool of pycurl.Curl easy handles that are checked out of an idle queue, configured for a specific request, and then checked back into the queue upon execution completion.

However, this optimization creates a significant attack surface if security states are not strictly isolated between consecutive execution threads. The client fails to wipe the internal configuration history of these reusable easy handles. When a handle is returned to the free list, any configuration set during the lifetime of the previous request remains active unless explicitly overwritten. Consequently, a request designed with strict security parameters (such as client-side certificates or proxy credentials) will unintentionally pass those configuration values down to any subsequent request scheduled on the same handle.

This structural logic flaw results in two distinct data leakage vectors. The first vector leaks client-side TLS certificates (configured via SSLCERT and SSLKEY) to arbitrary public servers. The second vector leaks proxy authentication credentials to unauthorized intermediate proxy servers. This vulnerability exposes organizations using mutual TLS (mTLS) or credentialed proxy configurations to unauthorized credential harvesting and identity impersonation.

Root Cause Analysis

The fundamental driver of this vulnerability is the state retention model used by the underlying libcurl library. In libcurl, when an option is applied to an easy handle via curl_easy_setopt (wrapped by Python's curl.setopt()), the setting persists on that handle indefinitely. The setting remains active until it is overridden by another setopt call, cleared using curl_easy_unsetopt, or wiped using curl_easy_reset (wrapped by curl.reset()). Tornado's CurlAsyncHTTPClient does not reset handles between operations, opting instead to configure options dynamically on a per-request basis in the _curl_setup_request method.

This design operates on the assumption that _curl_setup_request will comprehensively configure or clear every parameter. This assumption is incorrect. In tornado/curl_httpclient.py (v6.5.6 and earlier), the setup routine implements multiple conditional checks that apply options only when a specific configuration is active, lacking corresponding else branches to clear those parameters when they are absent in subsequent requests.

For example, during mTLS requests, SSLCERT and SSLKEY parameters are bound to the pycurl.Curl instance. If a subsequent request that does not specify client certificates is allocated the same handle, the configuration block is skipped, allowing the previously bound paths to persist. During proxy operations, if the client establishes a request using an authenticated proxy (configuring PROXYUSERPWD) and then issues a subsequent request through an unauthenticated proxy, the code updates the proxy host and port but bypasses the credential update block. Because the unsetting logic is only reachable when proxying is disabled entirely, the previous credentials remain bound and are transmitted to the new proxy server.

Code-Level Flaw Analysis

The vulnerable logic exists in tornado/curl_httpclient.py inside the _curl_setup_request method. The following block displays how client certificates are configured conditionally without cleanup branches:

# Vulnerable code in tornado/curl_httpclient.py (v6.5.6)
if request.client_cert is not None:
    curl.setopt(pycurl.SSLCERT, request.client_cert)
 
if request.client_key is not None:
    curl.setopt(pycurl.SSLKEY, request.client_key)

Because there are no corresponding else conditions, a handle that has request.client_cert configured will continue to hold that reference on all subsequent requests where request.client_cert is None. A similar issue affects proxy credential management:

# Vulnerable proxy credentials logic
if request.proxy_host and request.proxy_port:
    curl.setopt(pycurl.PROXY, request.proxy_host)
    curl.setopt(pycurl.PROXYPORT, request.proxy_port)
    if request.proxy_username:
        assert request.proxy_password is not None
        credentials = httputil.encode_username_password(
            request.proxy_username, request.proxy_password
        )
        curl.setopt(pycurl.PROXYUSERPWD, credentials)
    # Missing 'else' branch to clear PROXYUSERPWD when proxy_username is None
else:
    try:
        curl.unsetopt(pycurl.PROXY)
    except TypeError:
        curl.setopt(pycurl.PROXY, "")
    curl.unsetopt(pycurl.PROXYUSERPWD)

In this block, if request.proxy_host remains true but request.proxy_username is omitted, the code does not reach the outer else block containing curl.unsetopt(pycurl.PROXYUSERPWD). Consequently, the credentials from the prior proxy configuration are reused.

The security patches in Tornado version 6.5.7 resolve these issues by implementing systematic unsetting logic. When optional parameters are omitted from a request, the framework explicitly executes curl.unsetopt() to clear the active state of the handle before execution.

Exploitation and Attack Methodology

Exploiting this vulnerability does not require complex payload construction. It relies instead on scheduling behavior and pool reuse. An attacker does not need direct access to the application's memory or network flow to trigger the leak. Instead, the application itself initiates the leakage when executing sequential requests on behalf of users.

In a multi-tenant or multi-destination environment, an application using CurlAsyncHTTPClient might first process an internal request (Request A) that requires mutual TLS authentication to access a restricted backend service. Once completed, the handle is returned to the pool. When a user subsequently triggers a request (Request B) to an external, untrusted, or attacker-controlled server, the backend scheduler may assign the same handle. Because the handle retains the SSLCERT and SSLKEY parameters, the outgoing connection to the attacker-controlled server will execute an mTLS handshake, automatically presenting the client certificate to the external target.

In proxy scenarios, the process follows a similar flow. An application routes administrative or premium traffic through an authenticated corporate proxy using a high-privilege credential. When the application subsequently routes a standard user request through an unauthenticated public proxy, the active handle retains the Proxy-Authorization header value. The unauthorized proxy captures the header during connection establishment, obtaining the base64-encoded credentials of the primary corporate proxy.

Impact Assessment

The impact of credential leakage is classified as Medium (CVSS 5.9). Although the base CVSS score is moderate due to high attack complexity, the actual consequences in enterprise deployments can be severe.

In modern service architectures, client TLS certificates are frequently used as a primary authentication factor to access protected APIs, internal administrative portals, or sensitive databases. If these certificates are leaked to external servers, an attacker can capture the certificate chain. Depending on the configuration of the certificate authority and the target services, this exposure can allow the attacker to authenticate as the client, leading to unauthorized data access.

Furthermore, client certificates frequently contain metadata within their Subject Common Name (CN) or Alternative Names, such as internal server names, IP addresses, or domain structures. Access to this metadata allows an attacker to map internal network layouts. Similarly, leaked proxy credentials can allow unauthorized users to tunnel traffic through enterprise proxy servers, bypassing access controls and incurring significant routing fees.

Remediation and Defensive Strategies

The primary remediation strategy is upgrading the Tornado installation to version 6.5.7 or later. The update implements code changes that explicitly clear unused parameters during request setup, preventing state persistence across requests.

If upgrading is not immediately possible, you can implement several workarounds to mitigate exposure. The first workaround is switching the application's HTTP client backend from CurlAsyncHTTPClient to Tornado's native python-based client, SimpleAsyncHTTPClient. Because the native client does not use libcurl or pool persistent native handles, it is entirely unaffected by this state leakage flaw.

# Configure Tornado to use the safe native client backend
from tornado.httpclient import AsyncHTTPClient
AsyncHTTPClient.configure("tornado.simple_httpclient.SimpleAsyncHTTPClient")

If your application requires the CurlAsyncHTTPClient for advanced networking features, you can mitigate the risk by isolating client instances based on security context. Instead of using a single global client, instantiate separate client pools for different classes of requests. For example, use one dedicated client instance exclusively for mTLS connections, another for authenticated proxy connections, and a third for standard outbound requests. This isolation ensures that a recycled handle is never reused across different security boundaries.

Technical Appendix

CVSS Score
5.9/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N

Affected Systems

Tornado Web Framework (CurlAsyncHTTPClient)

Affected Versions Detail

Product
Affected Versions
Fixed Version
tornado
Tornado
<= 6.5.66.5.7
AttributeDetail
CWE IDCWE-200, CWE-672
Attack VectorNetwork (AV:N)
Attack ComplexityHigh (AC:H)
CVSS Score5.9 (Medium)
Exploit StatusPoC (Proof of Concept) Available
KEV StatusNot Listed

MITRE ATT&CK Mapping

T1552Unsecured Credentials
Credential Access
T1552.004Private Keys
Credential Access
T1083File and Directory Discovery
Discovery
CWE-200
Exposure of Sensitive Information to an Unauthorized Actor

The program exposes sensitive information to an actor that is not authorized to have access to that information.

Vulnerability Timeline

Vulnerability details coordinated and published under GHSA-PW6J-QG29-8W7F.
2026-06-15
Official repository security advisory and patch released in Tornado version 6.5.7.
2026-06-15
Public release of Proof of Concept scripts demonstrating credential leakage.
2026-06-15

References & Sources

  • [1]GitHub Security Advisory GHSA-pw6j-qg29-8w7f
  • [2]Tornado Security Advisory
  • [3]Affected Source File (v6.5.6)

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•2 days ago•CVE-2026-11748
6.9

CVE-2026-11748: Unauthenticated LDAP Injection in Central Dogma Server Authentication

An LDAP injection vulnerability exists in the centraldogma-server-auth-shiro module of LY Corporation Central Dogma before version 0.84.0. The search logic dynamically constructs LDAP search filters by interpolating user-provided usernames without escaping RFC 4515 metacharacters. Unauthenticated remote attackers can leverage this flaw to bypass authentication, enumerate directory hierarchies, and access unauthorized resources.

Alon Barad
Alon Barad
13 views•6 min read
•2 days ago•CVE-2026-11746
9.4

CVE-2026-11746: Use of Hard-coded ZooKeeper Replication Secret 'ch4n63m3' in Central Dogma Server

CVE-2026-11746 is a critical vulnerability in Central Dogma Server prior to version 0.84.0, where an embedded ZooKeeper replication secret silently falls back to a publicly known, hard-coded default string ('ch4n63m3'). Remote attackers with access to the replication network can authenticate as legitimate cluster peers, potentially leading to unauthorized data exposure, state manipulation, or complete cluster takeover.

Amit Schendel
Amit Schendel
8 views•6 min read
•2 days ago•CVE-2026-56665
4.2

CVE-2026-56665: Logical Validation Bypass in ZITADEL External JWT Identity Provider

A logical verification flaw in ZITADEL's external JWT Identity Provider validation allows attackers to bypass session expiration checks. If an incoming JWT lacks the 'exp' claim, the system skips validation entirely, creating an indefinitely valid session. This issue has been addressed in versions 3.4.12 and 4.15.2.

Alon Barad
Alon Barad
10 views•7 min read
•2 days ago•CVE-2026-59149
6.5

CVE-2026-59149: Sibling Directory Path Traversal in Mockoon Backend Server

CVE-2026-59149 identifies a directory traversal vulnerability in `@mockoon/commons-server`, the backend mock-server library powering the Mockoon application. The flaw occurs in the path containment validation logic used during raw file response generation. An unauthenticated attacker can exploit this weakness to retrieve arbitrary files from sibling directories sharing a common prefix with the designated static base directory.

Amit Schendel
Amit Schendel
15 views•5 min read
•2 days ago•CVE-2026-59148
8.8

CVE-2026-59148: Unauthenticated Administrative API and CORS Misconfiguration in Mockoon

An in-depth analysis of CVE-2026-59148, a high-severity flaw in Mockoon where unauthenticated administrative endpoints and a wildcard Cross-Origin Resource Sharing (CORS) policy allow remote execution, state poisoning, and credential theft.

Alon Barad
Alon Barad
16 views•6 min read
•2 days ago•CVE-2026-56666
4.8

CVE-2026-56666: Account Takeover via Improper Email Verification in ZITADEL Federated Identity Handler

An improper authentication vulnerability (CWE-287) in ZITADEL's external identity provider handler before version 4.15.3 allows remote attackers to perform complete account takeover. When auto-linking by email is enabled, ZITADEL verifies that the local target account has a verified email address but fails to verify if the external provider confirmed ownership of that same email. Attackers can exploit this by registering an unverified account with a victim's email address on a permissive external provider, leading to unauthorized account binding and persistent access.

Amit Schendel
Amit Schendel
8 views•7 min read