ImageMagick CLAHE漏洞分析:无符号下溢和除零错误导致内存越界与进程崩溃

本文详细分析了ImageMagick CLAHE实现中的安全漏洞,包括无符号整数下溢导致的越界指针运算和除零错误,这些漏洞可导致内存损坏、进程崩溃和拒绝服务攻击。漏洞影响多个Magick.NET版本,文章提供了复现步骤和修复建议。

ImageMagick CLAHE漏洞分析:无符号下溢和除零错误导致内存越界与进程崩溃

漏洞概述

ImageMagick的CLAHE(对比度受限自适应直方图均衡)实现中存在单一根本原因漏洞——当tile宽度/高度变为零时,会产生两种不同但相关的不安全行为。

漏洞存在于ImageMagick的MagickCore/enhance.c文件中的CLAHEImage()函数。

漏洞详情

无符号整数下溢 → 越界指针运算(OOB)

  • 位置:MagickCore/enhance.c,约第609行
  • 测试版本:7.1.2-8(本地ASan/UBSan构建)

易受攻击的代码

1
2
enhance.c: 609
p += (ptrdiff_t) clahe_info->width * (tile.height - 1);

根本原因: 如果tile.height == 0,则(tile.height - 1)下溢到UINT_MAX。与clahe_info->width相乘产生接近SIZE_MAX的巨大值。将此值加到p会导致指针算术下溢。

除零错误

  • 文件/位置:MagickCore/enhance.c,约第669-673行
  • 测试版本:7.1.2-8(本地ASan/UBSan构建)

易受攻击的代码

1
2
3
4
5
6
enhance.c: 669-673
if ((image->columns % tile_info.width) != 0)
   tile_info.x=(ssize_t) (tile_info.width-(image->columns % tile_info.width));
 tile_info.y=0;
 if ((image->rows % tile_info.height) != 0)
   tile_info.y=(ssize_t) (tile_info.height-(image->rows % tile_info.height));

根本原因: 计算默认tile尺寸后缺少输入验证/边界检查。如果tile_info.width或tile_info.height为0,会触发除零错误。零值可通过以下方式到达此点:

  • 精确tile:CLI命令clahe 0x0!(!强制直接使用零)
  • 小图像上的自动tile:当请求的tile为0(无!)时,代码从图像尺寸派生默认值(例如dim » 3)。对于dim < 8的图像,除非被钳制,否则结果为0。

复现步骤

无符号下溢

环境: 启用AddressSanitizer和UndefinedBehaviorSanitizer构建。

命令

1
./magick xc:black -clahe 0x0 null:

输出

1
2
MagickCore/enhance.c:609:6: runtime error: addition of unsigned offset overflowed
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior MagickCore/enhance.c:609:6 in CLAHEImage

除零错误

环境:启用ASan/UBSan的构建。

命令

1
./magick -size 16x2 gradient: -type TrueColor -depth 8 -clahe 0x0! null:

说明:没有清理器的情况下,进程可能仅以Aborted终止(仍然是DoS)。

影响

  • 主要影响:拒绝服务——在处理 crafted 参数或小图像时崩溃或持续资源耗尽(内存/缓存抖动)。攻击者可通过CLI或API使用clahe 0x0!或上传非常小的图像到使用ImageMagick的服务来轻易触发。
  • 次要影响(理论上的):OOB内存访问和内存损坏可能与其他漏洞结合以实现更严重的结果;然而,这些PoC单独未证明可靠的代码执行。

建议的修复代码片段

在CLAHEImage()中,在tile_info计算之后但在任何除法/模运算/指针算术之前应用:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
if (exact_tiles_requested && (tile_info.width == 0 || tile_info.height == 0)) {
  ThrowMagickException(exception, GetMagickModule(), OptionError,
                       "CLAHEInvalidTile", "%lux%lu",
                       (unsigned long) tile_info.width,
                       (unsigned long) tile_info.height);
  return (Image *) NULL;
}

if (!exact_tiles_requested) {
  tile_info.width  = (tile_info.width  == 0) ? MagickMax((size_t)1, image->columns >> 3) : tile_info.width;
  tile_info.height = (tile_info.height == 0) ? MagickMax((size_t)1, image->rows    >> 3) : tile_info.height;
}

if (tile_info.width == 0 || tile_info.height == 0) {
  ThrowMagickException(exception, GetMagickModule(), OptionError,
                       "CLAHEInvalidTile", "%lux%lu",
                       (unsigned long) tile_info.width,
                       (unsigned long) tile_info.height);
  return (Image *) NULL;
}

ssize_t tile_h_minus1 = (ssize_t)tile_info.height - 1;
if (tile_h_minus1 < 0) {
  ThrowMagickException(exception, GetMagickModule(), OptionError,
                       "CLAHEInvalidTile", "%lux%lu",
                       (unsigned long) tile_info.width,
                       (unsigned long) tile_info.height);
  return (Image *) NULL;
}
p += (ptrdiff_t) clahe_info->width * tile_h_minus1;

受影响版本

多个Magick.NET包版本<= 14.9.0受到影响,包括:

  • Magick.NET-Q16-HDRI-OpenMP-arm64
  • Magick.NET-Q16-HDRI-OpenMP-x64
  • Magick.NET-Q16-HDRI-arm64
  • Magick.NET-Q16-HDRI-x64
  • 以及其他多个变体

参考链接

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计