Skip to content

Commit 108c221

Browse files
committed
Merged PR 21240: [6.0] MSRC 70023 - ASP.Net FormFeature.cs - DenialOfService
# ASP.Net FormFeature.cs - DenialOfService When parsing multi-part form data with FormFeature.cs, we do not honor ValueCountLimit when the content disposition is of an unknown type. Therefore an attacker could send multi-part form data where very part has invalid content disposition, and make us read indefinitely. ## Description When parsing multi-part form data with FormFeature.cs, we do not honor ValueCountLimit when the content disposition is of an unknown type. Therefore an attacker could send multi-part form data where very part has invalid content disposition, and make us read indefinitely. ## Customer Impact Prevents a potential Denial-of-service attack. ## Regression? - [ ] Yes - [x] No ## Risk - [ ] High - [x] Medium - [ ] Low We could have missed another potential version of this vulnerability ## Verification - [x] Manual (required) - [x] Automated Added a test, plus confirmed with a local repro that the pre-existing slowdown goes away after the change. ## Packaging changes reviewed? - [ ] Yes - [ ] No - [x] N/A ---- ## When servicing release/2.1 - [ ] Make necessary changes in eng/PatchConfig.props
1 parent 1e1c891 commit 108c221

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

src/Http/Http/src/Features/FormFeature.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ private async Task<IFormCollection> InnerReadFormAsync(CancellationToken cancell
184184
else if (HasMultipartFormContentType(contentType))
185185
{
186186
var formAccumulator = new KeyValueAccumulator();
187+
var nonFormOrFileContentDispositionCount = 0;
187188

188189
var boundary = GetBoundary(contentType, _options.MultipartBoundaryLengthLimit);
189190
var multipartReader = new MultipartReader(boundary, _request.Body)
@@ -259,7 +260,11 @@ private async Task<IFormCollection> InnerReadFormAsync(CancellationToken cancell
259260
}
260261
else
261262
{
262-
System.Diagnostics.Debug.Assert(false, "Unrecognized content-disposition for this section: " + section.ContentDisposition);
263+
if (nonFormOrFileContentDispositionCount++ >= _options.ValueCountLimit)
264+
{
265+
throw new InvalidDataException($"Unrecognized Content-Disposition. Form value count limit {_options.ValueCountLimit} exceeded.");
266+
267+
}
263268
}
264269

265270
section = await multipartReader.ReadNextSectionAsync(cancellationToken);

src/Http/Http/test/Features/FormFeatureTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,12 @@ private class MockRequestBodyPipeFeature : IRequestBodyPipeFeature
165165
InvalidContentDispositionValue +
166166
"\r\n" +
167167
"\r\n" +
168+
"Foo\r\n";
169+
170+
private const string MultipartFormFileNonFormOrFileContentDispositionValue = "--WebKitFormBoundary5pDRpGheQXaM8k3T\r\n" +
171+
"Content-Disposition:x" +
172+
"\r\n" +
173+
"\r\n" +
168174
"Foo\r\n";
169175

170176
private const string MultipartFormWithField =
@@ -468,6 +474,30 @@ public async Task ReadFormAsync_ValueCountLimitExceeded_Throw(bool bufferRequest
468474
Assert.Equal("Form value count limit 2 exceeded.", exception.Message);
469475
}
470476

477+
[Theory]
478+
[InlineData(true)]
479+
[InlineData(false)]
480+
public async Task ReadFormAsync_NonFormOrFieldContentDisposition_ValueCountLimitExceeded_Throw(bool bufferRequest)
481+
{
482+
var formContent = new List<byte>();
483+
formContent.AddRange(Encoding.UTF8.GetBytes(MultipartFormFileNonFormOrFileContentDispositionValue));
484+
formContent.AddRange(Encoding.UTF8.GetBytes(MultipartFormFileNonFormOrFileContentDispositionValue));
485+
formContent.AddRange(Encoding.UTF8.GetBytes(MultipartFormFileNonFormOrFileContentDispositionValue));
486+
formContent.AddRange(Encoding.UTF8.GetBytes(MultipartFormEnd));
487+
488+
var context = new DefaultHttpContext();
489+
var responseFeature = new FakeResponseFeature();
490+
context.Features.Set<IHttpResponseFeature>(responseFeature);
491+
context.Request.ContentType = MultipartContentType;
492+
context.Request.Body = new NonSeekableReadStream(formContent.ToArray());
493+
494+
IFormFeature formFeature = new FormFeature(context.Request, new FormOptions() { BufferBody = bufferRequest, ValueCountLimit = 2 });
495+
context.Features.Set<IFormFeature>(formFeature);
496+
497+
var exception = await Assert.ThrowsAsync<InvalidDataException>(() => context.Request.ReadFormAsync());
498+
Assert.Equal("Unrecognized Content-Disposition. Form value count limit 2 exceeded.", exception.Message);
499+
}
500+
471501
[Theory]
472502
[InlineData(true)]
473503
[InlineData(false)]

0 commit comments

Comments
 (0)