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
- 以及其他多个变体
参考链接