| Gói / sản phẩm | Phiên bản bị ảnh hưởng | Phiên bản đã vá |
|---|---|---|
| ImageSharp | ≥ 3.0.0, < 3.2.0≥ 4.0.0, < 4.1.1 | 3.2.04.1.1 |
| imagesharp | ≥ 3.0.0, < 3.2.0≥ 4.0.0, < 4.1.1 | 3.2.04.1.1 |
When decoding a fax-compressed strip TIFF (Compression=3 / Group 3 1D, or Compression=2 / Modified Huffman), the CCITT decompressors write decoded runs through BitWriterUtils.WriteBits/WriteBit/WriteZeroBit, which advance and write bits via Unsafe.Add with read-modify-write semantics — without ever comparing the write position against the target buffer length. The strip buffer is sized ImageWidth × RowsPerStrip (8 bytes in the PoC), but two independent defects let an attacker write tens of millions of bits past it: (a) T4 only increments rowsWritten when an EOL code is read, and one ReadNextRun accumulates unlimited makeup codes (+2560 px per 12-bit code) into a single RunLength that WritePixelRun then writes in one shot; (b) Modified Huffman writes before validating (pixelsWritten > Width is checked at :90-93, after the write at :56-63), so the overflow completes even though an exception is thrown later. One crafted ~90 KB file writes ~19.2 MB linearly past an 8-byte buffer and deterministically kills the process; the write offset and length are fully attacker-controlled (classic heap-corruption primitive on the managed heap).
Verified at commit 5cd4d0d26a82a9549f297a237aea9cf665bddff8 (main; latest release v4.1.0, the supported major). A related tiled-path variant (tile-buffer width mismatch) was reported separately as GHSA-v76p-62qx-wwq2 — this report covers the distinct strip-path root cause.
Root cause: the bit-writing sink has no bounds check, and neither decompressor constrains run lengths against the strip buffer.
WriteBit), :58 (WriteZeroBit — also read-modify-write, so even all-white runs really write), :11 (WriteBits)rowsWritten only increments on EOL; consecutive makeup codes accumulate into one RunLength, then WritePixelRun writes it in fullBạn đang dùng một trong các sản phẩm này? Quét site để biết có đang lộ phiên bản có lỗ hổng không.
Quét site miễn phíCalculateStripBufferSize = width × bpp/8 × rowsPerStripAttack surface: Image.Load(stream) on an attacker-supplied strip TIFF (TiffDecoderCore.DecodeStripsChunky → TiffDecompressorsFactory.Create → Decompress). Default configuration, no authentication, no user interaction — a plain strip TIFF (far more common than the tiled variant) with Compression=2 or 3 and a chain of CCITT makeup codes.
Relationship to GHSA-v76p-62qx-wwq2: that report is the tiled variant — a caller-side width mismatch (TiffDecompressorsFactory drops tile parameters) that overflows with perfectly legal run codes. This report is the strip variant — the callee-side missing bounds check plus T4's EOL-only row accounting and MH's write-before-validate. Fixing the caller mismatch does not address this vector; bounding BitWriterUtils addresses both (see remediation).
Suggested remediation:
BitWriterUtils.WriteBits/WriteBit/WriteZeroBit against buffer.Length*8 (return bool / throw ImageFormatException) — do not rely on caller discipline.(bitsWritten + RunLength) <= buffer.Length*8 before writing; abort T4 when accumulated rows exceed stripHeight instead of waiting for the loop to end naturally.pixelsWritten > Width check before the actual write.Full PoC posted as the first comment below: Program.cs (driver), poc-tiff-t4.tif + poc-tiff-mh.tif (crafted files, ~90 KB each, base64 inline), README.
Build a console project referencing src/ImageSharp/ImageSharp.csproj, run it against either crafted file (or call Image.Load on it).
PoC layout: TIFF (II, 42), ImageWidth=64, ImageLength=1, BitsPerSample=1, Photometric=WhiteIsZero, RowsPerStrip=1; strip data = EOL(12bit) + 60000× white makeup 2560 (000000011111) + white terminating code (000111) — declared run ≈ 153,600,001 px ≈ 19.2 MB into an 8-byte buffer.
Observed (both variants):
strip payload 90003 bytes, claimed run ~153,600,001 px = 19,200,000 bytes into 8-byte buffer
Fatal error. System.AccessViolationException: Attempted to read or write protected memory.
at SixLabors.ImageSharp.Formats.Tiff.Compression.BitWriterUtils.WriteBits(Span`1<Byte>, IntPtr, IntPtr, Byte)
at ...ModifiedHuffmanTiffCompression.Decompress(...)
at SixLabors.ImageSharp.Formats.Tiff.TiffDecoderCore.DecodeStripsChunky[Rgba32](...)
Aborted (core dumped); exit=134
The T4 variant crashes identically at T4TiffCompression.WritePixelRun → BitWriterUtils.WriteBits.
Control: an equivalent file without the makeup chain (legal EOL-delimited rows) decodes normally — the crash comes from the oversized runs, not the container.
Reported by Kimi Security Team ([email protected]).