Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2025-62821 — Microsoft HEIF 扩展 (msheif_store.dll) 越界读取 | Kitploit
工具/GitHubGitHub/hyunjungg/cve-2025-62821
内存取证漏洞分析漏洞利用逆向工程模糊测试二进制分析
GitHubhyunjungg/cve-2025-62821

CVE-2025-62821

Microsoft HEIF 扩展 (msheif_store.dll) 越界读取

查看仓库
11个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

Microsoft HEIF Image Extensions (msheif_store.dll): CopyPixels 路径中源缓冲区分配不足导致越界读取和访问冲突(DoS)

在通过 WIC 解码特制的 HEIF 图像时,msheif 编解码器分配了一个 1 字节的源缓冲区,但随后在 CopyPixels 期间尝试从该缓冲区复制大量像素数据。复制过程使用 memmove,其大小由 stride × ROI height 计算得出,而未验证源缓冲区的实际长度。这会导致越界读取和立即的访问冲突(崩溃)。实际上,打开或预览恶意的 HEIF 文件会导致任何通过 HEIF Image Extensions 路由的 WIC 消费者发生拒绝服务。

命名说明:像 roi_height 这样的变量名是从反汇编中推断出来的,以提高可读性,并不反映供应商的符号名称。

产品Microsoft HEIF Image Extensions
模块msheif_store.dll
操作系统Windows 11
扩展版本1.2.22.0 (Microsoft.HEIFImageExtension_1.2.22.0_x64__8wekyb3d8bbwe\x64\msheif_store.dll)
  1. 根本原因分析

    a. 漏洞详细描述

    • 解码流水线根据图像几何计算出一个很大的 memmove 复制长度(例如 copy_size = stride * abs(roi_height))。
    • 应该保存解码帧/项目数据的源缓冲区被分配为 1 字节,原因是:
      • CHEIFStreamReader_ReadItemData 调用 CHEIFItemInfoEntry_GetDataSize 来计算项目的数据长度。
      • 在某个特定的失败分支上,CHEIFItemInfoEntry_GetDataSize 返回成功,但将计算出的大小保留为 0。
      • 然后 CHEIFStreamReader_ReadItemData 调用 MFCreateMemoryBuffer(0, &ppBuffer)。根据 MF 的行为,这会生成一个最小分配为 1 字节的缓冲区。
    • 在下游,CopyPixels 验证了目标缓冲区的大小,但在调用 memmove 之前没有验证源缓冲区的长度与请求的复制大小。
    • 结果:memmove(dst, src, large_size) 远远超出了 1 字节的 src 读取,在向量化加载时触发故障。

    如果图像/ROI 宽度被错误解析(例如 0x01000100 = 16,777,472),则会独立加剧此错误,从而扩大 stride 和最终的复制大小。然而,核心漏洞是源长度未与计算出的复制长度进行校验。

root@kitploit:~
(7de0.78e4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
msheif_store!DllCanUnloadNow+0x228b1d:
00007ffd`841bc45d c5fe6f02        vmovdqu ymm0,ymmword ptr [rdx] ds:000002b0`65be6ff0=c0

0:000> !heap -p -a rdx
    address 000002b065be6ff0 found in
    _DPH_HEAP_ROOT @ 2b05bed1000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                             2b062c2f2d8:      2b065be6ff0                1 -      2b065be6000             2000
    ...
    00007ffe75b90563 MFPlat!operator new+0x0000000000000023
    00007ffe75b83d36 MFPlat!MFCreateMemoryBuffer+0x0000000000000056
    00007ffd840157b1 msheif_store!DllCanUnloadNow+0x0000000000081e71
		...

0:000> r r8
r8=0000000001000100

b. 输入到输出的代码流程

root@kitploit:~
0:000> k
 # Child-SP          RetAddr               Call Site
00 0000005d`6a9aed98 00007ffe`6fdefcc2     msheif_store!DllCanUnloadNow+0x228b21
01 0000005d`6a9aeda0 00007ffe`6fcb3adc     msheif_store!DllCanUnloadNow+0x18c382
02 0000005d`6a9aee00 00007ffe`6fcb4513     msheif_store!DllCanUnloadNow+0x5019c
03 0000005d`6a9aeef0 00007ffe`6fcb96da     msheif_store!DllCanUnloadNow+0x50bd3
04 0000005d`6a9af0a0 00007fff`318551d5     msheif_store!DllCanUnloadNow+0x55d9a
05 0000005d`6a9af1a0 00007fff`318082eb     windowscodecs!CPyramidBase::CopyPixels+0xf5
06 0000005d`6a9af260 00007fff`31857e52     windowscodecs!CFormatConverter::CopyPixels+0x22b
07 0000005d`6a9af3e0 00007fff`31857e52     windowscodecs!CFormatConverterResolver::CopyPixels+0xc2
08 0000005d`6a9af480 00007ff6`15fb0f5d     heif_decoder!HEIF_LoadBitmapFromMemory+0x34d [C:\\\\Users\\\\test\\\\source\\\\repos\\\\heif_decoder\\\\heif_decoder\\\\heif_decoder.cpp @ 256]
09 0000005d`6a9af670 00007ff6`15fb04ab     heif_decoder!HEIF_FuzzMemory+0x9b [C:\\\\Users\\\\test\\\\source\\\\repos\\\\heif_decoder\\\\heif_decoder\\\\heif_decoder.cpp @ 357]
0a 0000005d`6a9af830 00007ff6`15fb1754     heif_decoder!wmain+0x394 [C:\\\\Users\\\\test\\\\source\\\\repos\\\\heif_decoder\\\\heif_decoder\\\\heif_decoder.cpp @ 524]
0b 0000005d`6a9afaa0 00007ff6`15fb3039     heif_decoder!invoke_main+0x39 [D:\\\\a\\\\_work\\\\1\\\\s\\\\src\\\\vctools\\\\crt\\\\vcstartup\\\\src\\\\startup\\\\exe_common.inl @ 91]
0c 0000005d`6a9afaf0 00007ff6`15fb2ee2     heif_decoder!__scrt_common_main_seh+0x132 [D:\\\\a\\\\_work\\\\1\\\\s\\\\src\\\\vctools\\\\crt\\\\vcstartup\\\\src\\\\startup\\\\exe_common.inl @ 288]
0d 0000005d`6a9afb60 00007ff6`15fb2d9e     heif_decoder!__scrt_common_main+0xe [D:\\\\a\\\\_work\\\\1\\\\s\\\\src\\\\vctools\\\\crt\\\\vcstartup\\\\src\\\\startup\\\\exe_common.inl @ 331]
0e 0000005d`6a9afb90 00007fff`3847e8d7     heif_decoder!wmainCRTStartup+0xe [D:\\\\a\\\\_work\\\\1\\\\s\\\\src\\\\vctools\\\\crt\\\\vcstartup\\\\src\\\\startup\\\\exe_wmain.cpp @ 17]
0f 0000005d`6a9afbc0 00007fff`3939c34c     KERNEL32!BaseThreadInitThunk+0x17
10 0000005d`6a9afbf0 00000000`00000000     ntdll!RtlUserThreadStart+0x2c

解析后的内部调用(为清晰起见重新命名):

root@kitploit:~
CWICHeifDecoderFrame::CopyPixels
 → CWICHeifBitmapSourceBase::CopyPixels_Transform
   → CWICHeifBitmapSourceBase::CopyPixels_Base
     → sub_7FFD8411FC40  // 发出 memmove
       → memmove         // AV (vmovdqu 从 src 加载)

c. 缓冲区大小

  • 特制的 HEIF 导致 CHEIFItemInfoEntry_GetDataSize 走一条早期的“成功但大小为 0”的路径(例如,当 sub_7FFD8408B898 失败且函数返回成功但 sizeBuffer = 0 时)。

  • 源缓冲区:通过 MFCreateMemoryBuffer(0) 分配 ⇒ 1 字节。

  • 复制大小:计算为 copy_size = stride * abs(roi_height)。由于错误解析的宽度(例如 0x01000100)和典型的 BPP,stride 巨大,导致非常大的 copy_size。

  • 易受攻击的调用:

    root@kitploit:~
    __int64 sub_7FFD8411FC40(__m128i* dst, __int64 a2, __m128i* src, int a4,
                             unsigned int stride, unsigned int abs_roi_height)
    {
      if (src && dst) {
        if (stride == a2 && stride == a4) {
          // 没有验证 'src' 是否至少包含 (stride * abs_roi_height) 字节
          memmove(dst, src, (size_t)abs_roi_height * stride); // 此处 AV
        }
      }
    }
    
  • 为什么源是 1 字节(分配路径):

    root@kitploit:~
    __int64 __fastcall CHEIFStreamReader_ReadItemData(__int64 *a1, __int64 *a2, IMFMediaBuffer **a3)
    {
      ppBuffer = 0i64;
      if ( v7(a2) )
      {
        *cbMaxLength = 0i64;
        result = (*(*a2 + 0xA0))(a2, cbMaxLength); // CHEIFItemInfoEntry_GetDataSize
        if ( result < 0 )
        {
          goto LABEL_10;
        }
        if ( *cbMaxLength > 0xC800000ui64 )
        {
          result = 0xC00D36BE;
          goto LABEL_23;
        }
        // 如果 GetDataSize 提前返回成功,cbMaxLength 可能仍为 0
        result = MFCreateMemoryBuffer(cbMaxLength[0], &ppBuffer);
        if ( result < 0 )
        {
          goto LABEL_34;
        }
        ...
    }
    
    root@kitploit:~
    __int64 __fastcall CHEIFItemInfoEntry_GetDataSize(__int64 a1, unsigned __int64 *sizeBuffer)
    {
    
      if ( sizeBuffer )
      {
        *sizeBuffer = 0i64;
        DataLocationInfo = (*(*v5 + 0x78i64))(v5, 0x6D657461i64, &v32);
        if ( DataLocationInfo >= 0 )
        {
          v13 = v32;
          v14 = *(v32 + 0xF0);
          if ( v14 )
          {
            if ( !sub_7FFD8408B898((v14 + 0xC8), (a1 - 0x78), &v33, v31) )
            {
            // 返回成功(0/S_OK)而不更新 sizeBuffer
            // 调用者将此视为 length=0 → MFCreateMemoryBuffer(0)
                DataLocationInfo = 0;
                goto LABEL_62;
              }
            v17 = v33;
            DataLocationInfo = CHEIFItemInfoEntry_GetDataLocationInfo(v5, v13, *(v33 + 16), &v29, &v30);
            if ( DataLocationInfo >= 0 )
            {
              for ( i = 0; ; ++i )
              {
                if ( i >= *v17 )
                {
                  *sizeBuffer = v6;
                  goto LABEL_62;
                }
                v21 = *(*(v17 + 8) + 24i64 * i + 16);
                }
                if ( v21 + v6 < v6 )
                  break;
                v6 += v21;
                DataLocationInfo = 0;
              }
    
             ...
             ...
    LABEL_62:
      sub_7FFD83F91AEC();
      return DataLocationInfo;
    }
    

d. 建议的修复

在 CopyPixels 路径中,验证源缓冲区是否至少包含 stride * abs(roi_height) 字节(并且 src_stride/src_height 是否满足或超过请求的区域)。如果不满足,则返回 WINCODEC_ERR_INSUFFICIENTBUFFER(或等效值)失败。

e. 分析方法(符号重建)

为了使调用流程可审查,我们根据编解码器自身的日志字符串,在 IDA 中重建了内部函数名称。

  • 目标:为关键例程(…CopyPixels_Base、…CopyPixels_Transform、CHEIFStreamReader::ReadItemData 等)识别可读的函数名称,以使崩溃堆栈与反编译代码对齐。

  • 步骤:

    1. 查找日志辅助函数:在崩溃点附近调用者的反编译视图中,找到一个小的辅助函数,它接受一个类似函数名称的常量字符串参数(例如 “CWICHeifBitmapSourceBase::CopyPixels_Base”)。

      • 在 IDA 中:打开崩溃处的函数,向上一个帧,检查函数入口处接受字符串字面量的辅助函数调用。
    2. 收集调用者:使用对该辅助函数的交叉引用列出所有调用者函数。

    3. 提取名称:对于每个调用者,打开反编译视图,读取辅助函数的第二个参数(字符串字面量)。该字符串一致地包含预期的函数名称。

    4. 重命名调用者:在每个调用者中,按 N(编辑 → 重命名)并将函数名称设置为恢复的字符串(例如 CWICHeifBitmapSourceBase::CopyPixels_Base)。

    5. 对附近的帧重复,直到整个崩溃链都有有意义的名称。

验证:重命名后,重新运行经过调用点,确认参数类型/行为与标签匹配(例如 …CopyPixels_Base 是 stride/ROI 计算和最终 memmove 调用发生的地方)。

  1. 概念验证

  • 先决条件:Microsoft Store 中已安装/启用 HEIF Image Extensions(需要用于 HEIC/HEIF 解码)。

  • 重现步骤

    1. 下载并将附带的 PoC 文件(../access_violation_0000xxxxxxxxx461_00000xxxxxxxx6B0_1.heif)保存到本地文件夹。

    2. 确保已安装 HEIF Image Extensions(打开 Microsoft Store → 搜索 “HEIF Image Extensions” → 安装)。

    3. 使用 Microsoft Photos 打开 PoC 文件(如果 Photos 是默认应用则双击文件,或右键单击 → 打开方式 → Photos)。

  • 软件下载链接

    HEIF Image Extensions (Microsoft Store) https://apps.microsoft.com/detail/9pmmsr1cgpwg?hl=en-US&gl=US

下载工具