ImageMagick CLAHE 漏洞深度解析:无符号下溢与除零引发的内存越界与拒绝服务攻击

本文深入分析了ImageMagick图像处理库中CLAHE功能的一个高危漏洞(CVE-2025-62594)。该漏洞源于分块宽度或高度为零时,会引发无符号整数下溢和除零错误,进而导致越界指针运算和进程崩溃,可能被用于拒绝服务攻击。

CVE-2025-62594: ImageMagick CLAHE 漏洞分析

漏洞概述

ImageMagick 的 CLAHE 实现中存在一个根本原因——分块宽度/高度变为零——导致了两种不同但相关的不安全行为。

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

漏洞细节

1. 无符号整数下溢 → 越界指针运算

  • 位置: 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 上会导致指针算术下溢。

2. 除零错误

  • 文件/位置: 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_info.widthtile_info.height 为 0,将触发除零错误。
    • 零值可以通过以下途径到达此处:
      • 精确分块设置: 命令行使用 clahe 0x0!! 强制使用字面值零)。
      • 微小图像的自动分块: 当请求的分块为 0(无 !)时,代码会从图像尺寸推导默认值(例如,dim >> 3)。对于尺寸小于 8 的图像,结果为零,除非被钳制。

复现步骤

无符号下溢

  • 环境: 启用 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
    
  • 不同尺寸测试: 使用更大尺寸的图像(如 2000x2000, 4000x4000, 8000x8000)会导致显著的内存消耗和内存区域损坏迹象,最终可能使进程崩溃(DoS)。

除零错误

  • 环境: 启用 ASan/UBSan 的构建。
  • 命令示例:
    1
    
    ./magick -size 16x2 gradient: -type TrueColor -depth 8 -clahe 0x0! null:
    
  • 输出示例: 未启用 sanitizer 时,进程可能仅以 Aborted 终止(仍构成 DoS)。

影响

  • 主要影响: 拒绝服务——当通过 CLI 或 API 处理构造的参数或微小图像时,导致进程崩溃或持续的资源耗尽(内存/缓存抖动)。攻击者可以轻易通过 clahe 0x0! 或向使用 ImageMagick 的服务上传非常小的图像来触发。
  • 次要(理论)影响: 越界内存访问和内存损坏可能与其他漏洞结合,导致更严重的后果;然而,仅从这些 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;

关于 exact_tiles_requested 的说明:如果 CLI/Wand 解析器已经暴露了是否使用了 !,则使用该信息。如果没有,则在解析时添加一个标志,以便 CLAHEImage 能够知道 0 是字面值还是自动计算值。

致谢

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