Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Payloads that caused a DOS attack (GIF / JPEG) #2758

Closed
4 tasks done
ErazerBrecht opened this issue Jun 26, 2024 · 5 comments · Fixed by #2759
Closed
4 tasks done

Payloads that caused a DOS attack (GIF / JPEG) #2758

ErazerBrecht opened this issue Jun 26, 2024 · 5 comments · Fixed by #2759

Comments

@ErazerBrecht
Copy link

ErazerBrecht commented Jun 26, 2024

Prerequisites

  • I have written a descriptive issue title
  • I have verified that I am running the latest version of ImageSharp
  • I have verified if the problem exist in both DEBUG and RELEASE mode
  • I have searched open and closed issues to ensure it has not already been reported

ImageSharp version

3.1.4

Other ImageSharp packages and versions

Environment (Operating system, version and so on)

Windows 11
Alpine 3.19

.NET Framework version

.NET 8

Description

Hello,

GIF

When using a certain GIF we are seeing OOM's.
When debugging this locally I allocate +- 20Gb when handling that specific payload 5 times in parallel.

Payload: https://github.com/ErazerBrecht/ImageSharp.Payloads/blob/master/ImageSharp.Payloads.Gif/funnyanim.gif
Source code: https://github.com/ErazerBrecht/ImageSharp.Payloads/blob/master/ImageSharp.Payloads.Gif/Program.cs

image

When changing the source code to use the 'earth.gif' it works as intended and there is no spike in memory allocation.

JPEG

We have some payloads that cause resource amplification. There are very small <1460 bytes but when using Imagesharp they result into a very big result (biggest one goes to >40Mb). This only happens when specifying a quality level in the JPG encoder.
When not using one the payloads throw on a DividebyZeroException.

Payloads
https://github.com/ErazerBrecht/ImageSharp.Payloads/blob/master/ImageSharp.Payloads.Jpeg/83.jpg
https://github.com/ErazerBrecht/ImageSharp.Payloads/blob/master/ImageSharp.Payloads.Jpeg/92.jpg
https://github.com/ErazerBrecht/ImageSharp.Payloads/blob/master/ImageSharp.Payloads.Jpeg/93.jpg
Source code: https://github.com/ErazerBrecht/ImageSharp.Payloads/blob/master/ImageSharp.Payloads.Jpeg/Program.cs

image

Payloads were created by: https://app.intigriti.com/profile/whatevicanhaz

Sincerely,
Brecht

Steps to Reproduce

See:
https://github.com/ErazerBrecht/ImageSharp.Payloads

Images

No response

@JimBobSquarePants
Copy link
Member

Hello @ErazerBrecht, thank you for this.

Upon reviewing the issues, I was unable to open the folder containing a copy of your repository on my Windows 11 machine, as File Explorer would instantly crash. It's unclear how you were able to view the output there.

However, I was able to conduct an investigation on an older MacBook.

Regarding the GIF issue, I have opened a pull request which has been successfully tested against your image. It's important to note that your test code has a memory leak due to not disposing of images after allocation.

For the JPEG issue, the DividebyZeroException you mentioned is unclear to me. The encoder in your sample code seems to replicate the default encoder options, and since the encoder only encodes the decoded pixel buffer, the input appears to be irrelevant. Further clarification is needed on this matter.

The increase in JPEG encoded output size is to be expected. The images provided have been modified to specify large dimensions, such as 59787x511 pixels. These dimensions are taken from the jpeg header, and we attempt to decode to a buffer that matches these dimensions. Consequently, when encoding, we are working with a pixel buffer that reflects the full manipulated dimensions, resulting in the encoding of a significantly larger amount of data.

@ErazerBrecht
Copy link
Author

Hey @JimBobSquarePants
Already thanks for taking a look at this.

Yeah I can just use the file explorer on Windows 11.
Here are the payloads in a ZIP so hopefully that works.

funnyanim.gif.zip
funnyjpegs.zip

What I mean is that if I swap this part of the code:

using var output = new MemoryStream();
var encoder = new JpegEncoder { Quality = 75 };
image.Save(output, encoder);
Console.WriteLine($"Input: {input.Length} vs Output: {output.Length}");

To this:

using var output = new MemoryStream();
var encoder = new JpegEncoder();
image.Save(output, encoder);
Console.WriteLine($"Input: {input.Length} vs Output: {output.Length}");

Than it throws the exception:

Unhandled exception. System.DivideByZeroException: Attempted to divide by zero.
   at SixLabors.ImageSharp.Formats.Jpeg.JpegEncoderCore.WriteDefineQuantizationTables(JpegQuantizationTableConfig[] configs, Nullable`1 optionsQuality, JpegMetadata metadata, Span`1 tmpBuffer)
   at SixLabors.ImageSharp.Formats.Jpeg.JpegEncoderCore.Encode[TPixel](Image`1 image, Stream stream, CancellationToken cancellationToken)
   at SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder.Encode[TPixel](Image`1 image, Stream stream, CancellationToken cancellationToken)
   at SixLabors.ImageSharp.Formats.ImageEncoder.EncodeWithSeekableStream[TPixel](Image`1 image, Stream stream, CancellationToken cancellationToken)
   at SixLabors.ImageSharp.Formats.ImageEncoder.Encode[TPixel](Image`1 image, Stream stream)
   at SixLabors.ImageSharp.Image.EncodeVisitor.Visit[TPixel](Image`1 image)
   at SixLabors.ImageSharp.Image`1.Accept(IImageVisitor visitor)
   at SixLabors.ImageSharp.Advanced.AdvancedImageExtensions.AcceptVisitor(Image source, IImageVisitor visitor)
   at SixLabors.ImageSharp.Image.Save(Stream stream, IImageEncoder encoder)
   at Program.<<Main>$>g__SaveLoadImage|0_0(String fileName) in I:\POC\ImageSharp.Payloads\ImageSharp.Payloads.Jpeg\Program.cs:line 32
   at Program.<Main>$(String[] args) in I:\POC\ImageSharp.Payloads\ImageSharp.Payloads.Jpeg\Program.cs:line 14

For me the exception is better since it prevents a malicious actor of uploading the file.
We fixed it in our own code by checking if the output ImageSharp is 10x bigger than the input, if this is the case we don't allow the image (only if the result is bigger than x amount of Mb).

@JimBobSquarePants
Copy link
Member

JimBobSquarePants commented Jun 27, 2024

Thanks for the update @ErazerBrecht I think understand what must be happening with the jpeg encoder now.

You can see the progress of the PR here #2759

I've solved the GIF problem by refactoring the LZW decoder to work line by line. This limits allocations for the index buffer to 64K preventing the massive allocation you were seeing.

I'll have a look at the JPEG issue in the morning. I believe my inability to view the images in your repository has something to do with my usage of the new dev drive. I can see them in my standard drive.

P.S you can use the Image.Identify(...) methods to determine the decoded dimensions of an image and also configure memory allocation limitations which will throw exceptions when exceeded.

@JimBobSquarePants JimBobSquarePants linked a pull request Jun 28, 2024 that will close this issue
4 tasks
@JimBobSquarePants
Copy link
Member

@ErazerBrecht I've added a fix for the DividebyZeroException to the PR. There are no fixes for the output file size since that is simply how large a true encoded JPEG for those dimensions would be. This is how any library that can open the images would behave.

@JimBobSquarePants
Copy link
Member

Fixed with 3.1.5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants