From f8fb77052fd2a89b6ad018f50c5a89c4c3e33e62 Mon Sep 17 00:00:00 2001 From: "Joel Sallow (/u/ta11ow)" <32407840+vexx32@users.noreply.github.com> Date: Tue, 24 Mar 2020 13:51:55 -0400 Subject: [PATCH] Fix detection regex in web cmdlets (#12099) --- .../BasicHtmlWebResponseObject.Common.cs | 2 +- .../WebCmdlets.Tests.ps1 | 44 +++++++++++++++++++ .../WebListener/Controllers/DosController.cs | 17 ++++--- 3 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs index 54950f841..fd4c4b412 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs @@ -232,7 +232,7 @@ namespace Microsoft.PowerShell.Commands if (s_imageRegex == null) { - s_imageRegex = new Regex(@"]*>", + s_imageRegex = new Regex(@"]*?>", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 index ba1c5e43e..d588674d6 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 @@ -1900,7 +1900,51 @@ Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" { } } + Context "Regex Parsing" { + + It 'correctly parses an image with id, class, and src attributes' { + $dosUri = Get-WebListenerUrl -Test 'Dos' -query @{ + dosType = 'img-attribute' + } + + $response = Invoke-WebRequest -Uri $dosUri + $response.Images | Should -Not -BeNullOrEmpty + } + } + Context "Denial of service" -Tag 'DOS' { + It "Image Parsing" { + Set-ItResult -Pending -Because "The pathological regex runs fast due to https://github.com/dotnet/runtime/issues/33399. Fixed in .NET 5 preview.2" + $dosUri = Get-WebListenerUrl -Test 'Dos' -query @{ + dosType='img' + dosLength='5000' + } + $script:content = '' + [TimeSpan] $timeSpan = Measure-Command { + $response = Invoke-WebRequest -Uri $dosUri + $script:content = $response.content + $response.Images | out-null + } + + $script:content | Should -Not -BeNullOrEmpty + + # pathological regex + $regex = [RegEx]::new(']*>') + + [TimeSpan] $pathologicalTimeSpan = Measure-Command { + $regex.Match($content) + } + + $pathologicalRatio = $pathologicalTimeSpan.TotalMilliseconds/$timeSpan.TotalMilliseconds + Write-Verbose "Pathological ratio: $pathologicalRatio" -Verbose + + # dosLength 4,000 on my 3.5 GHz 6-Core Intel Xeon E5 macpro produced a ratio of 12 + # dosLength 5,000 on my 3.5 GHz 6-Core Intel Xeon E5 macpro produced a ratio of 21 + # dosLength 10,000 on my 3.5 GHz 6-Core Intel Xeon E5 macpro produced a ratio of 75 + # in some cases we will be running in a Docker container with modest resources + $pathologicalRatio | Should -BeGreaterThan 5 + } + It "Charset Parsing" { $dosUri = Get-WebListenerUrl -Test 'Dos' -query @{ dosType='charset' diff --git a/test/tools/WebListener/Controllers/DosController.cs b/test/tools/WebListener/Controllers/DosController.cs index 7966ab39b..864bb3aa8 100644 --- a/test/tools/WebListener/Controllers/DosController.cs +++ b/test/tools/WebListener/Controllers/DosController.cs @@ -29,33 +29,38 @@ namespace mvc.Controllers } StringValues dosLengths; - Int32 dosLength =1; + Int32 dosLength = 1; if (Request.Query.TryGetValue("dosLength", out dosLengths)) { Int32.TryParse(dosLengths.FirstOrDefault(), out dosLength); } string body = string.Empty; - switch(dosType) + switch (dosType) { case "img": contentType = "text/html; charset=utf8"; body = ""; + break; case "charset": contentType = "text/html; charset=melon"; body = " { - var httpContext = (HttpContext) state; - httpContext.Response.ContentType = contentType; - return Task.FromResult(0); + var httpContext = (HttpContext)state; + httpContext.Response.ContentType = contentType; + return Task.FromResult(0); }, HttpContext); Response.ContentLength = Encoding.UTF8.GetBytes(body).Length;