CVE-2026-106117 is a high-severity (CVSS 7.5) Out-of-bounds Write vulnerability in ImageSharp. A fix is available for ImageSharp — see the affected versions and patch details below.
ImageSharp: CCITT fax decompression (T4/Modified Huffman): unbounded WriteBits overflows strip buffer — heap OOB write in SixLabors.ImageSharp
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-106117.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
CVE-2026-106117 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 395,249 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
ImageSharp.NETImageSharpReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects NuGet packages — download data is not available via public APIs for these ecosystems.
Description
Summary
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.
Details
Root cause: the bit-writing sink has no bounds check, and neither decompressor constrains run lengths against the strip buffer.
- Unchecked write primitive: BitWriterUtils.cs#L51 (
WriteBit), :58 (WriteZeroBit— also read-modify-write, so even all-white runs really write), :11 (WriteBits) - T4 trigger: T4TiffCompression.cs#L75 and L109-L119 —
rowsWrittenonly increments on EOL; consecutive makeup codes accumulate into oneRunLength, thenWritePixelRunwrites it in full - MH trigger: ModifiedHuffmanTiffCompression.cs#L56-L63 — write happens before the width check at L90-L93
- Buffer size: TiffDecoderCore.cs#L932-L967 —
CalculateStripBufferSize= width × bpp/8 × rowsPerStrip
Attack 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:
- Bound
BitWriterUtils.WriteBits/WriteBit/WriteZeroBitagainstbuffer.Length*8(return bool / throwImageFormatException) — do not rely on caller discipline. - In the T4/MH decompress loops, validate
(bitsWritten + RunLength) <= buffer.Length*8before writing; abort T4 when accumulated rows exceed stripHeight instead of waiting for the loop to end naturally. - In Modified Huffman, move the
pixelsWritten > Widthcheck before the actual write. - Regression fuzz cases: Compression=2/3, EOL-less oversized makeup chains, edge widths; strip and tiled paths share the same constraint.
PoC
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 callImage.Loadon 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=134The 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.
Impact
- What it is: out-of-bounds write (CWE-787). For any service decoding untrusted TIFFs (image hosting/transcoding/thumbnails/CMS): reliable remote DoS (uncatchable fatal process crash), plus a heap OOB write with attacker-controlled offset and length — a realistic heap-corruption / potential code-execution surface. In scope of your SECURITY.md as a library vulnerability.
- Who is impacted: applications decoding untrusted TIFF with SixLabors.ImageSharp at the current major (verified on main past v4.1.0); plain strip TIFFs with Compression=2/3 are the trigger, which ordinary TIFF writers can produce.
Reported by Kimi Security Team ([email protected]).
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| .NETNuGet | ImageSharp | ≥ 3.0.0&&< 3.2.0 | 3.2.0dotnet add package ImageSharp --version 3.2.0 |
| .NETNuGet | ImageSharp | ≥ 4.0.0&&< 4.1.1 | 4.1.1dotnet add package ImageSharp --version 4.1.1 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for ImageSharp, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update ImageSharp to 3.2.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-106117 is resolved across your whole dependency graph.
Workarounds
Stop feeding it untrusted input: reject or quarantine files and payloads from unverified sources until you can upgrade, restrict accepted formats to the ones you actually need, and run the parsing or decoding step in a least-privileged sandbox or short-lived worker so a crash or corrupted read cannot reach the rest of the process.
Frequently Asked Questions
Is CVE-2026-106117 in your dependencies?
Find it across NuGet, including transitive dependencies.