A equipe de pesquisa da ESET analisou a CVE-2025-50165, uma grave vulnerabilidade do Windows descrita para garantir a execução remota de código simplesmente abrindo um arquivo JPG especialmente criado - um dos formatos de imagem mais utilizados. A falha, encontrada e documentada pelo Zscaler ThreatLabz, despertou nosso interesse, pois a Microsoft avaliou sua gravidade como crítica, mas considerou sua possibilidade de exploração como menos provável. Nossa análise da causa raiz nos permitiu identificar o local exato do código defeituoso e reproduzir a falha. Acreditamos que o cenário de exploração é mais difícil do que parece.
Pontos principais desta publicação:
- Os pesquisadores da ESET disponibilizam uma análise completa da vulnerabilidade CVE-2025-50165, ilustrada com trechos de pseudocódigo;
- Destacamos nosso método para reproduzir a falha usando uma imagem JPG simples de 12 ou 16 bits e um exame do patch inicial lançado;
- A CVE-2025-50165 é uma falha no processo de codificação e compactação de uma imagem JPG, não em sua decodificação;
- Nossa conclusão explora e reavalia a capacidade de exploração e o cenário de ataque dessa falha.
Visão geral
Em 20 de novembro de 2025, o Zscaler ThreatLabz publicou um artigo documentando a descoberta da CVE-2025-50165, uma vulnerabilidade de alto impacto de execução remota de código presente no WindowsCodecs.dll. Essa biblioteca é a principal interface do Windows para o processamento e a manipulação dos formatos de arquivos de imagem mais comuns, como JPG, PNG, GIF e BMP, entre outros. As descobertas da Zscaler, bem como a descrição fornecida pela Microsoft, revelam que a falha decorre da desreferência de um ponteiro de função não inicializado no WindowsCodecs.dll, que faz parte do componente Windows Imaging. O primeiro conseguiu localizar o problema de desreferência dentro da função jpeg_finish_compress, que é chamada quando um fluxo de imagem JPG é compactado e (re)codificado. Quando se fala em vulnerabilidades no manuseio de imagens, geralmente o primeiro ponto de atenção são falhas de análise e decodificação que ocorrem no momento em que a imagem é renderizada. Diante disso, esse caminho de código vulnerável bastante específico nos levou a algumas perguntas que queríamos responder:
- Quais são as condições exatas para seguir o caminho do código vulnerável que leva à desreferência do ponteiro de função não inicializado?
- Quando o jpeg_finish_compress é chamado?
- Por que o ponteiro de função não é inicializado?
Como as imagens JPG são muito comuns na Web, queríamos respostas e, por isso, começamos investigando o código que causou a falha.
Local da falha
De acordo com a descrição da entrada CVE-2025-50165, as versões do WindowsCodecs.dll a partir de 10.0.26100.0 e antes de 10.0.26100.4946 são afetadas. Analisamos a versão vulnerável 10.0.26100.4768 (SHA-1: 5887D96565749067564BABCD3DC5D107AB6666BD) e, em seguida, realizamos uma comparação binária com a primeira versão corrigida 10.0.26100.4946 (SHA-1: 4EC1DC0431432BC318E78C520387911EC44F84FC). Depois de fazer o download dos símbolos correspondentes, examinamos a função de falha, jpeg_finish_compress. De acordo com uma string de versão presente no WindowsCodecs.dll - libjpeg-turbo versão 3.0.2 (build 20250529) - a DLL depende de uma implementação bastante antiga da biblioteca libjpeg-turbo (lançada em 24 de janeiro de 2024) para manipular imagens JPG. O repositório disponível publicamente nos permitiu mapear a maior parte do código binário e das estruturas relevantes para seus equivalentes no código-fonte. Por exemplo, a versão compilada do jpeg_finish_compress é muito semelhante ao seu equivalente no código-fonte disponível aqui, conforme mostrado na Figura 1.
Com base nas descobertas do Zscaler, a falha ocorre em jpeg_finish_compress+0xCC, que corresponde à linha 48 na Figura 1, ao desreferenciar um ponteiro de função localizado no deslocamento 0x10 de uma estrutura desconhecida(pub). De acordo com o código-fonte do libjpeg-turbo, isso corresponde a um ponteiro de função chamado compress_data_12. Para chegar a esse caminho específico, o membro data_precision do jpeg_compress_struct precisa ser definido como 12. Isso corresponde à profundidade de bits ou, em outras palavras, ao número de bits usados para descrever as cores. Essencialmente, o WindowsCodecs.dll falha quando tenta codificar uma imagem JPG com precisão de 12 bits.
Diferenciação de patches e análise de causa raiz
Usando o Diaphora, uma ferramenta de comparação binária, realizamos uma comparação entre a versão vulnerável 10.0.26100.4768 e a versão corrigida 10.0.26100.4946, conforme mostrado na Figura 2.
Surpreendentemente, a função de falha jpeg_finish_compress mencionada no artigo não está presente. No entanto, duas funções relacionadas à codificação foram alteradas: rawtransencode_master_selection e jinit_c_rawtranscode_coef_controller_turbo. A diferença entre as versões vulnerável e corrigida de rawtransencode_master_selection é mostrada na Figura 3.
A única diferença relevante parece ser o fato de que a função jinit_c_rawtranscode_coef_controller_turbo, que anteriormente estava embutida no corpo da função rawtransencode_master_selection, agora está separada. A análise da versão corrigida da função jinit_c_rawtranscode_coef_controller_turbo revela que o membro da estrutura compress_data_12, anteriormente não inicializado, agora está definido para apontar para uma função chamada rawtranscode_compress_output_16, conforme mostrado na Figura 4.
Observe que o campo compress_data_16, que também não foi inicializado na versão vulnerável, também está definido para apontar para rawtranscode_compress_output_16 na versão corrigida. Essa função é simplesmente uma função stub que chama rawtranscode_compress_output, o que pode indicar que não há código específico para lidar com imagens JPG de precisão de 12 ou 16 bits.
Reproduzindo a falha
Conforme mencionado no artigo da Zscaler, é possível compilar o trecho de código proposto pela Microsoft (https://learn.microsoft.com/en-us/windows/win32/wic/-wic-codec-jpegmetadataencoding#jpeg-re-encode-example-code) para decodificar e recodificar uma imagem JPG.
Depois que esse programa é compilado, a falha pode ser reproduzida fornecendo-se um arquivo JPG de 12 ou 16 bits. Analisando as amostras do repositório libjpeg-turbo, uma imagem de amostra de precisão de 12 bits está disponível para download em https://github.com/libjpeg-turbo/libjpeg-turbo/blob/main/testimages/testorig12.jpg. A alimentação dessa imagem no aplicativo de exemplo de recodificação resultou em uma falha exatamente no mesmo local mencionado no artigo da Zscaler. A Figura 5 mostra o contexto da falha durante uma sessão de depuração.
O valor hexadecimal repetido 0xBAADF00D apontado pelo endereço de memória é um valor mágico usado pelo heap do tempo de execução do C (CRT) quando um programa chama HeapAlloc para alocar memória. Ele marca a memória como não inicializada (acesse: https://www.nobugs.org/developer/win32/debug_crt_heap.html).
Conforme indicado anteriormente, ambas as versões analisadas do WindowsCodecs.dll parecem ser capazes de lidar com imagens JPG com precisão de 16 bits. Mas ao testar essas imagens, o aplicativo de recodificação falha ao fazer referência ao ponteiro de função compress_data_16, conforme observado na Figura 6.
Depois de reproduzir a falha, perguntamos se essa vulnerabilidade específica também estava presente no código-fonte da biblioteca libjpeg-turbo.
Explorando o código-fonte
Analisar os commits da libjpeg-turbo revelou que problemas semelhantes foram resolvidos em18 de dezembro de 2024, com o commit e0e18de, introduzindo a versão 3.1.1. Essencialmente, o commit garante que as estruturas sejam inicializadas com zero e que um erro seja gerado se um ponteiro for NULL. Acontece que todas as inicializações zero e verificações introduzidas por esse commit estão ausentes nas versões vulneráveis e corrigidas do WindowsCodecs.dll.
A mensagem de correção também indica outros possíveis caminhos de código vulneráveis e, o que é mais importante, que as falhas também podem ocorrer no processo de descompressão ao manipular uma imagem JPG, conforme destacado pelo diff do arquivo jdapistd.c, ilustrado na Figura 7.
Como a descrição do commit deixa claro, um aplicativo chamador só travaria, devido à desreferência de um ponteiro de função não inicializado, se alterasse incorretamente o campo data_precision após a chamada das rotinas jpeg_start_compress ou jpeg_start_decompress. Isso cria um cenário bastante específico e, em grande parte, irrealista, no qual um aplicativo que utiliza o WindowsCodecs.dll modificaria diretamente o estado das estruturas internas. Embora aplicações desse tipo possam existir, tudo indica que a API do componente de geração de imagens do Windows não permite esse tipo de comportamento.
Possibilidade de exploração
Conforme revelado por nossa análise de causa raiz, o problema central do CVE-2025-50165 reside no tratamento de imagens JPG pelo WindowsCodecs.dll com um valor de precisão de dados diferente do convencional e padrão de 8 bits. Os dois ponteiros de função específicos de precisão(compress_data_12 e compress_data_16) não foram inicializados durante o processo de compactação, criando dois caminhos de código vulneráveis que parecem ser acessíveis somente ao (re)codificar uma imagem JPG. A simples abertura e, portanto, a decodificação e a renderização de uma imagem especialmente criada não acionará a vulnerabilidade. No entanto, a função vulnerável jpeg_finish_compress pode ser chamada se a imagem for salva ou se um aplicativo host, como o aplicativo Microsoft Photos, criar miniaturas de imagens, conforme mostrado na Figura 8.
Para que um programa seja considerado vulnerável, ele precisa ter as seguintes características:
- use uma versão vulnerável do WindowsCodecs.dll;
- não trave ou aborte ao decodificar um arquivo JPG de 12 ou 16 bits;
- permite que a imagem seja recodificada.
Além disso, conforme mencionado pelos pesquisadores da Zscaler, um vazamento de endereço e controle suficiente sobre o heap são obrigatórios para explorar essa vulnerabilidade.
Considerações finais sobre a CVE-2025-50165
Embora o JPG seja um formato mais antigo, amplamente utilizado e possivelmente o mais popular em testes de fuzz, ainda é possível encontrar vulnerabilidades em determinados codecs. A análise do CVE-2025-50165 também reforça a importância de manter as bibliotecas de terceiros sempre atualizadas com as correções de segurança mais recentes.
A análise da causa raiz, combinada com a comparação de patches, mostrou-se uma abordagem extremamente eficaz para responder às nossas perguntas iniciais. Identificamos que o bug pode ser acionado quando o WindowsCodecs.dll codifica um fluxo JPG com precisão de 12 ou 16 bits, uma vez que os ponteiros de função específicos para essas precisões não eram inicializados nem validados antes de serem desreferenciados. Além disso, constatamos que esse comportamento ocorre no momento em que a imagem é salva ou quando uma miniatura é gerada a partir dela.
Essa investigação nos levou a uma conclusão alinhada à da Microsoft sobre o potencial de exploração da vulnerabilidade. Como o WindowsCodecs.dll é uma biblioteca, um aplicativo host só seria considerado vulnerável se permitisse a (re)codificação de imagens JPG e, ainda assim, apenas em cenários nos quais um invasor tivesse controle suficiente sobre o aplicativo, como a obtenção de vazamento de endereços ou a manipulação do heap. Considerando esses fatores, a exploração prática da falha parece pouco provável.
Por fim, vale destacar que, no momento da redação deste documento e de acordo com nossos testes, as versões mais recentes do WindowsCodecs.dll, como 10.0.22621.6133, SHA-1: 3F3767D05E5A91184005D98427074711F68D9950, já incorporam as alterações descritas no commit do libjpeg-turbo, corrigindo efetivamente a ausência de inicialização e a verificação inadequada dos ponteiros de função.




