GHSA-v76p-62qx-wwq2: High severity nuget/ImageSharp vulnerability
Summary
When decoding a tiled TIFF with fax compression (T4/T6/MH), DecodeTilesChunky allocates each tile buffer from TileWidth (ceil(TileWidthbpp/8)TileLength bytes) but constructs the fax decompressor with frame.Width: TiffDecompressorsFactory ignores the isTiled/tileWidth/tileHeight parameters entirely. The T4/T6/MH decompressors treat the full image width as the scanline length and advance (and really write, via read-modify-write bit ops) frame.Width bits per row, with no bounds check against the tile buffer. The very first tile therefore writes linearly out of bounds — about ImageWidth/8 bytes per row × TileLength rows into a TileWidth-sized buffer. With ImageWidth=4,000,000, TileWidth=16, TileLength=16 this writes ~2 MB past a 32-byte buffer and kills the process deterministically; a T6 all-white variant advances the bit offset by >512 MB silently, showing an alarm-free heap-corruption window for the same defect. A crafted file fully controls the OOB length per tile and works with perfectly legal per-row run codes (no overlong runs needed).
Verified at commit 5cd4d0d26a82a9549f297a237aea9cf665bddff8 (main; latest release v4.1.0, the supported major).
Details
Root cause: a size mismatch between tile buffer allocation and decompressor width, because the factory drops the tile parameters.
- Allocation: TiffDecoderCore.cs#L792-L794 — bytesPerTileRow = RoundUpToMultipleOfEight(tileWidthbitsPerPixel); tile buffer = bytesPerTileRowtileLength bytes (32 bytes in the PoC) - Mismatch: TiffDecoderCore.cs#L797 — CreateDecompressor<TPixel>(frame.Width, ..., isTiled: true, tileWidth, tileLength) passes the full frame width - Factory drops tile params: TiffDecompressorsFactory.cs#L56-L64 — T4/T6/MH decompressors receive only width (= frame.Width); isTiled/tileWidth/tileHeight ignored - OOB write sink: T4TiffCompression.cs#L69-L119, T6TiffCompression.cs#L76-L108 — per-row advance of this.width bits via BitWriterUtils.WriteBits with no buffer-length check (BitWriterUtils.cs#L51); note TiffDecoderCore.cs:829-831 later reads the buffer in bytesPerTileRow strides, confirming the protocol expects tile-width rows
Attack surface: Image.Load(stream) on an attacker-supplied tiled TIFF (TiffDecoderCore.DecodeImageWithTiles → DecodeTilesChunky). Default configuration; only requirement is a standard tiled TIFF header (TileWidth=16, TileLength=16) + Compression=3 (T4; T6 also constructible).
Suggested remediation: 1. Short-term: when isTiled, construct the T4/T6/MH decompressor with tileWidth (not frame width), or clip row writes to the caller-provided buffer length. 2. Root fix: bound the write side of BitWriterUtils (pass remaining bits), and validate every fax row advance against buffer capacity on both tiled and strip paths. 3. Regression tests: Compression=2/3/4 × tiled with TileWidth < ImageWidth, including a T6 black-pixel row (forces real WriteBit).
PoC
Full PoC posted as the first comment below: Program.cs (driver), poc-tiled-t4.tif (crafted file, ~9.8 KB, base64 inline), README.
1. Build a small console project referencing src/ImageSharp/ImageSharp.csproj and run it against the crafted file (or call Image.Load on it from any host). 2. Observed with ImageWidth=4,000,000, TileWidth=16, TileLength=16, T4 with 400 makeup codes + EOL per row:
tile payload 9642 bytes; rows write ~2,048,000 bytes into a 32-byte buffer Fatal error. System.AccessViolationException: Attempted to read or write protected memory. at SixLabors.ImageSharp.Formats.Tiff.Compression.BitWriterUtils.WriteBits(Span1<Byte>, IntPtr, IntPtr, Byte) at ...T4TiffCompression.WritePixelRun(...) at ...T4TiffCompression.Decompress(...) Aborted (core dumped); exit=134
3. Controls: the same file with Compression=None decodes normally (container is fine); a T6 all-white-rows variant advances >512 MB of bit offset without a real write (silent corruption window) before tripping on a directory-level TileOffsets count check.
Impact
- What it is: out-of-bounds write (CWE-787). For any service decoding untrusted tiled TIFFs: remote, default-configuration, deterministic process crash (DoS), plus a heap OOB write whose per-tile length and row width are attacker-tunable — a potential code-execution surface. This is a vulnerability in the library itself, in scope of your SECURITY.md. - Who is impacted: applications decoding untrusted TIFF with SixLabors.ImageSharp at the current major (verified on main past v4.1.0); tiled + fax-compressed files are the trigger, which ordinary TIFF writers can produce.
---
---
Reported by Kimi Security Team (bug-report@moonshot.ai).
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
nuget/ImageSharpto a version that resolves this vulnerability.Fixed in 4.1.1 - Compensating control
Fix the TIFF fax decompression path by bounding BitWriterUtils write operations with the remaining buffer capacity and validating every T4/T6/MH fax-row advance against the tile buffer capacity on both tiled and strip paths; as a short-term code change for tiled input, construct the T4/T6/MH decompressor with tileWidth instead of frame.Width, or clip row writes to the caller-provided buffer length.
Event History
Frequently Asked Questions
Which image inputs are affected?
The issue is triggered when ImageSharp decodes tiled TIFF files that use fax compression: T4, T6, or MH. It depends on a mismatch between the image width and tile width, so crafted tiled inputs with a much larger ImageWidth than TileWidth are particularly dangerous.
Does exploitation require authentication, user interaction, or malformed fax run codes?
No authentication or user interaction is required according to the supplied severity vector. The crafted TIFF can use legal per-row fax run codes; exploitation does not require overlong runs.
What is the likely impact if an affected TIFF is processed?
The decoder can write beyond the allocated tile buffer, causing deterministic process termination in one demonstrated case. The data also describes a heap-corruption window where a T6 all-white input advances the bit offset by more than 512 MB without an alarm.
How can I determine whether my deployment may be exposed?
Deployments that decode untrusted TIFF files with ImageSharp should check whether they accept tiled TIFFs using T4, T6, or MH compression. The issue was verified on main at commit 5cd4d0d26a82a9549f297a237aea9cf665bddff8, and the supplied data identifies v4.1.0 as the latest release in the supported major version.