
Extensão HEIF da Microsoft (msheif_store.dll) leitura fora dos limites
Ao decodificar uma imagem HEIF criada através do WIC, o codec msheif aloca um buffer de origem de 1 byte, mas posteriormente tenta copiar uma quantidade muito grande de dados de pixel desse buffer durante o CopyPixels. A cópia usa memmove com um tamanho derivado de stride × altura ROI, sem validar o comprimento real do buffer de origem. Isso causa uma leitura fora dos limites e uma Violação de Acesso imediata (crash). Na prática, abrir ou visualizar o HEIF malicioso causa uma negação de serviço em qualquer consumidor WIC que roteie através das Extensões de Imagem HEIF.
Nota de nomenclatura: Nomes de variáveis como roi_height são inferidos da desmontagem para legibilidade e não refletem nomes de símbolos do fornecedor.
| Produto | Microsoft HEIF Image Extensions |
|---|---|
| Módulo | msheif_store.dll |
| SO | Windows 11 |
| Versão da extensão | 1.2.22.0 (Microsoft.HEIFImageExtension_1.2.22.0_x64__8wekyb3d8bbwe\x64\msheif_store.dll) |
a. Descrição detalhada da vulnerabilidade
Este bug é agravado independentemente se a largura da imagem/ROI for mal interpretada (por exemplo, 0x01000100 = 16.777.472), inflando o stride e o tamanho final da cópia. No entanto, a vulnerabilidade principal é que o comprimento da origem não é verificado em relação ao comprimento de cópia calculado.
(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. Fluxo de código a partir da entrada
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
Chamadas internas resolvidas (renomeadas para clareza):
CWICHeifDecoderFrame::CopyPixels
→ CWICHeifBitmapSourceBase::CopyPixels_Transform
→ CWICHeifBitmapSourceBase::CopyPixels_Base
→ sub_7FFD8411FC40 // issues memmove
→ memmove // AV (vmovdqu load from src)
c. Tamanho do buffer
HEIF criado que faz com que CHEIFItemInfoEntry_GetDataSize siga um caminho precoce de “sucesso com tamanho 0” (por exemplo, quando sub_7FFD8408B898 falha e a função retorna sucesso com sizeBuffer = 0).
Buffer de origem: Alocado via MFCreateMemoryBuffer(0) ⇒ 1 byte.
Tamanho da cópia: Derivado como copy_size = stride * abs(roi_height). Com largura mal interpretada (por exemplo, 0x01000100) e BPP típico, o stride é enorme, levando a um copy_size muito grande.
Chamada vulnerável:
__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) {
// No validation that 'src' contains at least (stride * abs_roi_height) bytes
memmove(dst, src, (size_t)abs_roi_height * stride); // AV here
}
}
}
Por que a origem tem 1 byte (caminho de alocação):
__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;
}
// cbMaxLength may still be 0 if GetDataSize returned success early
result = MFCreateMemoryBuffer(cbMaxLength[0], &ppBuffer);
if ( result < 0 )
{
goto LABEL_34;
}
...
}
__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) )
{
// returns success (0/S_OK) without updating sizeBuffer
// caller treats this as 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. Correções sugeridas
No caminho CopyPixels, valide que o buffer de origem tenha pelo menos stride * abs(roi_height) bytes (e que src_stride/src_height atenda ou exceda a região solicitada). Caso contrário, falhe com WINCODEC_ERR_INSUFFICIENTBUFFER (ou equivalente).
e. Metodologia de análise (reconstrução de símbolos)
Para tornar o fluxo de chamadas revisável, reconstruímos os nomes das funções internas no IDA a partir das próprias strings de registro do codec.
Objetivo. Identificar nomes de funções legíveis para rotinas-chave (…CopyPixels_Base, …CopyPixels_Transform, CHEIFStreamReader::ReadItemData, etc.) para alinhar a pilha de crash com o código descompilado.
Procedimento:
Encontre o auxiliar de registro: Na visualização do descompilador dos chamadores próximos ao crash, localize um pequeno auxiliar que é invocado com um argumento de string constante semelhante a um nome de função (por exemplo, "CWICHeifBitmapSourceBase::CopyPixels_Base").
Colete os chamadores: Use xrefs para esse auxiliar para listar todas as funções chamadoras.
Extraia nomes: Para cada chamador, abra a visualização descompilada e leia o segundo argumento do auxiliar (o literal de string). Essa string contém consistentemente o nome da função pretendido.
Renomeie chamadores: Em cada chamador, pressione N (Editar → Renomear) e defina o nome da função para a string recuperada (por exemplo, CWICHeifBitmapSourceBase::CopyPixels_Base).
Repita para quadros próximos até que toda a cadeia de crash tenha nomes significativos.
Validação. Após renomear, percorra novamente os locais de chamada e confirme que os tipos/comportamento dos argumentos correspondem aos rótulos (por exemplo, …CopyPixels_Base é onde a matemática stride/ROI e a eventual chamada memmove ocorrem).
pré-requisitos : Extensões de Imagem HEIF da Microsoft Store instaladas/habilitadas (necessárias para decodificação HEIC/HEIF).
Passos para reproduzir
Baixe e salve o arquivo PoC anexado (../access_violation_0000xxxxxxxxx461_00000xxxxxxxx6B0_1.heif) em uma pasta local.
Certifique-se de que as Extensões de Imagem HEIF estejam instaladas (abra a Microsoft Store → pesquise “Extensões de Imagem HEIF” → Instalar).
Abra o arquivo PoC com o Microsoft Fotos (clique duas vezes no arquivo se o Fotos for o padrão, ou Clique com o botão direito → Abrir com → Fotos).
Link de download do software
Extensões de Imagem HEIF (Microsoft Store) https://apps.microsoft.com/detail/9pmmsr1cgpwg?hl=en-US&gl=US