CVE-2025-67724 Overview
CVE-2025-67724 is an input validation flaw in the Tornado Python web framework and asynchronous networking library. Versions 6.5.2 and below use the supplied HTTP reason phrase without escaping in both HTTP response headers and the default HTML error page. Attackers can pass untrusted data to the reason argument of RequestHandler.set_status or tornado.web.HTTPError to trigger cross-site scripting (XSS) or HTTP header injection. The maintainers fixed the issue in Tornado 6.5.3. The weakness is classified under CWE-79.
Critical Impact
Applications that forward user-controlled values into the Tornado reason argument can be coerced into reflecting attacker-supplied HTML or injecting arbitrary HTTP headers into responses.
Affected Products
- Tornado (tornadoweb) versions 6.5.2 and earlier
- Python applications relying on tornado.web.RequestHandler.set_status
- Python applications raising tornado.web.HTTPError with untrusted reason phrases
Discovery Timeline
- 2025-12-12 - CVE-2025-67724 published to NVD
- 2026-06-17 - Last updated in NVD database
Technical Details for CVE-2025-67724
Vulnerability Analysis
Tornado exposes a reason argument in RequestHandler.set_status(status_code, reason=None) and in tornado.web.HTTPError. The argument lets applications override the human-readable phrase on the HTTP status line, such as the Not Found in HTTP/1.1 404 Not Found. In versions 6.5.2 and earlier, Tornado writes this value directly into the response status line and into the default HTML error page without sanitization or escaping.
When the reason phrase contains carriage-return or line-feed sequences (\r\n), the framework emits attacker-controlled data as new HTTP headers, enabling response splitting. When the value contains HTML metacharacters and is rendered on the default error page, the browser interprets the payload as markup, producing reflected XSS. Exploitation requires an application to pass externally influenced data into the reason argument, so impact depends on how downstream code uses the API.
Root Cause
The root cause is missing neutralization of special characters ([CWE-79]) in a field the developer documentation previously described as free-form text. The framework treated reason as trusted operator input, so no encoding was applied when the string was serialized into either the HTTP status line or HTML error responses.
Attack Vector
Exploitation is remote and requires user interaction, such as clicking a crafted link that reaches a Tornado handler which propagates a query parameter, header value, or form field into set_status(..., reason=user_input) or raise HTTPError(status_code, reason=user_input). A payload containing <script> tags renders on the default error page. A payload containing \r\n sequences injects additional response headers.
:arg int status_code: Response status code.
:arg str reason: Human-readable reason phrase describing the status
- code. If ``None``, it will be filled in from
- `http.client.responses` or "Unknown".
+ code (for example, the "Not Found" in ``HTTP/1.1 404 Not Found``).
+ Normally determined automatically from `http.client.responses`; this
+ argument should only be used if you need to use a non-standard
+ status code.
Source: Tornado commit 9c163ae. The patch hardens tornado/web.py against invalid reason phrases and clarifies that the argument should only be supplied for non-standard status codes.
Detection Methods for CVE-2025-67724
Indicators of Compromise
- HTTP responses containing unexpected Set-Cookie, Location, or duplicated headers immediately after the HTTP status line
- Error responses returning script tags, event handlers, or angle brackets within the default Tornado error page body
- Access logs showing request parameters containing %0d%0a, <script, or other encoded HTML or CRLF sequences directed at endpoints that emit custom status codes
Detection Strategies
- Inventory Python dependencies and flag any project using tornado<6.5.3 via SBOM scanning or pip list output review
- Perform static analysis for call sites of RequestHandler.set_status and tornado.web.HTTPError where the reason keyword receives non-literal values
- Deploy web application firewall rules that block CRLF sequences and script payloads in query strings and headers reaching Tornado services
Monitoring Recommendations
- Alert on HTTP response bodies from Tornado services containing unescaped < or > characters on default error pages
- Monitor egress HTTP traffic for malformed status lines or additional headers appearing after non-standard status codes
- Correlate 4xx and 5xx responses with request payloads that include HTML or CRLF metacharacters to surface probing activity
How to Mitigate CVE-2025-67724
Immediate Actions Required
- Upgrade Tornado to version 6.5.3 or later across all Python environments and container images
- Audit application code for call sites that pass user-controlled data into the reason argument of set_status or HTTPError
- Rebuild and redeploy any container images that pin Tornado to 6.5.2 or earlier
Patch Information
The fix is delivered in Tornado 6.5.3. Review the GitHub Security Advisory GHSA-pr2v-jx2c-wg9f, the v6.5.3 release notes, and the upstream commit for implementation details.
Workarounds
- Remove the reason argument from application code and let Tornado derive the phrase from http.client.responses
- Restrict the reason value to a fixed allow-list of ASCII strings when a custom phrase is genuinely required
- Override write_error to render a sanitized error template instead of relying on the default HTML error page
# Upgrade Tornado to the patched release
pip install --upgrade 'tornado>=6.5.3'
# Verify installed version
python -c "import tornado; print(tornado.version)"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

