CVE-2025-47287
Tornado DoS via excessive multipart form logging before 6.5.0.
- CVSS 7.5
- CWE-770
- Resource Management
- Remote
Tornado is a Python web framework and asynchronous networking library. When Tornado's “multipart/form-data“ parser encounters certain errors, it logs a warning but continues trying to parse the remainder of the data. This allows remote attackers to generate an extremely high volume of logs, constituting a DoS attack. This DoS is compounded by the fact that the logging subsystem is synchronous. All versions of Tornado prior to 6.5.0 are affected. The vulnerable parser is enabled by default. Upgrade to Tornado version 6.50 to receive a patch. As a workaround, risk can be mitigated by blocking `Content-Type: multipart/form-data` in a proxy.
- CWE
- CWE-770
- CVSS base score
- 7.5
- Published
- 2025-05-15
- OWASP
- A04 Design Flaws
- Orthogonal defect classification
- Interface
- Code defect classification
- Timing Issues
- Category
- Resource Management
- Subcategory
- Resource Exhaustion
- Accessibility scope
- Remote
- Impact
- Denial of Service (DoS)
- Affected component
- Tornado
- Fixed by upgrading
- Yes
Solution
Upgrade to Tornado version 6.5.0.
Vulnerable code sample
import tornado.ioloop
import tornado.web
import logging
import io
class UploadHandler(tornado.web.RequestHandler):
def post(self):
# VULNERABLE: This code is susceptible to path traversal
files = self.request.files.get('file')
if files:
for file in files:
try:
# Simulate processing the file (could involve parsing)
# The vulnerability is in how Tornado *attempts* to parse
# even malformed multipart/form-data, generating logs.
# This example just logs the filename. The *actual*
# vulnerability is in the Tornado internals. This is
# just representative of a handler that *uses* the
# vulnerable multipart parsing.
logging.warning(f"Received file: {file['filename']}")
# Simulate some expensive operation after the parse that exacerbates
# the DOS. Without this, it would just be a log flooding issue.
# This makes the server actually become unresponsive. This is
# also NOT the vulnerability. The vulnerability is the parsing.
# This just makes the overall DOS more severe.
for i in range(1000000):
pass
self.write("File uploaded successfully")
except Exception as e:
logging.error(f"Error processing file: {e}")
self.set_status(500)
self.write("Error uploading file")
else:
self.set_status(400)
self.write("No file uploaded")
def make_app():
return tornado.web.Application([
(r"/upload", UploadHandler),
])
if __name__ == "__main__":
logging.basicConfig(level=logging.WARNING)
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()Patched code sample
import logging
import tornado.ioloop
import tornado.web
import tornado.options
from tornado.options import define, options
define("port", default=8888, help="run on the given port", type=int)
class UploadHandler(tornado.web.RequestHandler):
def post(self):
# SECURE: This version prevents path traversal
try:
# In Tornado < 6.5.0, errors in multipart parsing would only
# log a warning and continue, potentially leading to excessive
# logging. Tornado >= 6.5.0 limits the number of errors
# that are logged during multipart parsing.
files = self.request.files.get('files', [])
if files:
for file_data in files:
filename = file_data['filename']
body = file_data['body']
self.write(f"Uploaded {filename} ({len(body)} bytes)\n")
else:
self.write("No files uploaded\n")
except Exception as e:
logging.error(f"Error processing upload: {e}")
self.send_error(500, message=str(e))
def main():
tornado.options.parse_command_line()
app = tornado.web.Application([
(r"/upload", UploadHandler),
])
app.listen(options.port)
logging.info(f"Listening on port {options.port}")
tornado.ioloop.IOLoop.current().start()
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
main()Payload
Content-Type: multipart/form-data; boundary=---------------------------12345
-----------------------------12345
Content-Disposition: form-data; name="field1"
value1
-----------------------------12345
Content-Disposition: form-data; name="field2"
value2
-----------------------------12345
Content-Disposition: form-data; name="field3"
value3
-----------------------------12345
Content-Disposition: form-data; name="field4"
value4
-----------------------------12345
Content-Disposition: form-data; name="field5"
value5
-----------------------------12345
Content-Disposition: form-data; name="field6"
value6
-----------------------------12345
Content-Disposition: form-data; name="field7"
value7
-----------------------------12345--INVALID
Cite this entry
@misc{vaitp:cve202547287,
title = {{Tornado DoS via excessive multipart form logging before 6.5.0.
}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2025},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-47287},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-47287/}}
}
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 ::
