-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
werkzeug misdecodes multipart line breaks if 64kb chunk ends in the middle of final CR+LF #3065
Description
The following minimalist flask application and upload code causes a wrongly decoded multipart payload to be saved. The application file upload.py and test case testcase are in the attached zip file reproducer.zip.
- run application:
mkdir upload ; python3 -m flask --app upload.py run -p 32269 - upload the test case (from a separate shell, of course):
netcat 127.0.0.1 32269 < testcase
Behold the fact that the testcase ends in:
000100f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00010100: 0000 0000 0000 000d 0a2d 2d30 3633 6166 .........--063af
00010110: 6334 6362 6534 3661 3734 3035 3930 3938 c4cbe46a74059098
00010120: 3831 3433 3937 3165 3262 642d 2d0d 0a 8143971e2bd--..
whereby the payload should end with a bunch of zeros.
What happens is that the downloaded file has a stray \x0d at the end, leading to a file of length 65432 bytes, while the correct length should be 65431 bytes:
0000ff80: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000ff90: 0000 0000 0000 000d ........
My understanding of the cause is that the code here splits the input in chunks of size 65536. Its state machine is updated by what it sees. If the chunk is broken in the middle of the final line break (which is precisely what happens here), the quite tolerant regexp here will recognize a single \n as a legitimate line break marker, and thus let \r be part of the payload.
Environment:
>>> import werkzeug
>>> import sys
>>> import importlib
>>> from importlib.metadata import version
>>> sys.version
'3.13.9 (main, Oct 15 2025, 14:56:22) [GCC 15.2.0]'
>>> version('werkzeug')
'3.1.3'
This bug was observed in the field with the cado-nfs software and nailed down by @akruppa.