Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2025-62821 — Microsoft HEIF Extension (msheif_store.dll) OOB-read | Kitploit
도구/GitHubGitHub/hyunjungg/cve-2025-62821
Memory ForensicsVulnerability AnalysisExploitationReverse EngineeringFuzzingBinary Analysis
GitHubhyunjungg/cve-2025-62821

CVE-2025-62821

Microsoft HEIF Extension (msheif_store.dll) OOB-read

저장소 보기
10개월 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

Microsoft HEIF Image Extensions (msheif_store.dll): CopyPixels 경로에서 소스 버퍼 미할당으로 인한 범위 초과 읽기 및 액세스 위반 (DoS)

WIC를 통해 조작된 HEIF 이미지를 디코딩할 때, msheif 코덱은 1바이트 소스 버퍼를 할당하지만 이후 CopyPixels 중에 해당 버퍼에서 매우 많은 양의 픽셀 데이터를 복사하려고 시도합니다. 복사는 stride × ROI 높이에서 파생된 크기로 memmove를 사용하며, 소스 버퍼의 실제 길이를 검증하지 않습니다. 이로 인해 범위 초과 읽기와 즉각적인 액세스 위반(충돌)이 발생합니다. 실제로 악성 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`15fb1754     heif_decoder!HEIF_FuzzMemory+0x9b [C:\\\\Users\\\\test\\\\source\\\\repos\\\\heif_decoder\\\\heif_decoder\\\\heif_decoder.cpp @ 357]
0a 0000005d`6a9af830 00007ff6`15fb3039     heif_decoder!wmain+0x394 [C:\\\\Users\\\\test\\\\source\\\\repos\\\\heif_decoder\\\\heif_decoder\\\\heif_decoder.cpp @ 524]
0b 0000005d`6a9afaa0 00007ff6`15fb2ee2     heif_decoder!invoke_main+0x39 [D:\\\\a\\\\_work\\\\1\\\\s\\\\src\\\\vctools\\\\crt\\\\vcstartup\\\\src\\\\startup\\\\exe_common.inl @ 91]
0c 0000005d`6a9afaf0 00007ff6`15fb2d9e     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`15fb30ce     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 (src에서 vmovdqu 로드)

c. 버퍼 크기

  • CHEIFItemInfoEntry_GetDataSize가 조기 "성공 크기 0" 경로를 취하도록 유발하는 조작된 HEIF(예: 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) )
            {
            // sizeBuffer를 업데이트하지 않고 성공(0/S_OK) 반환
    	      // 호출자는 이를 길이=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. 호출자 수집: 해당 도우미에 대한 xref를 사용하여 모든 호출자 함수를 나열합니다.

    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

도구 다운로드