Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
cve-2023-5217-poc — Um PoC para acionar o CVE-2023-5217 a partir da interface WebCodecs ou MediaRecorder do navegador. | Kitploit
Ferramentas/GitHubGitHub/ut-security/cve-2023-5217-poc
Análise de VulnerabilidadesExploraçãoExploração de Aplicações WebFuzzingAprendizado e EducaçãoExploração de Binários
GitHubut-security/cve-2023-5217-poc

cve-2023-5217-poc

Um PoC para acionar o CVE-2023-5217 a partir da interface WebCodecs ou MediaRecorder do navegador.

Ver Repositório
163há 2 anosAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2023-5217: Estouro de Pilha no Encoding VP8 do libvpx — PoC

A CVE-2023-5217 é uma vulnerabilidade do libvpx explorada ativamente que foi encontrada por Clément Lecigne do Threat Analysis Group do Google e estava sendo usada contra o Chrome.

Este repositório mostra como acionar a CVE-2023-5217 no navegador usando as APIs WebCodecs e MediaRecorder. A CVE-2023-5217 permite um estouro de buffer na pilha (heap) com comprimento controlado do estouro e sobrescrita de um pequeno valor repetido de 4 bytes. Atualmente não se sabe como a CVE-2023-5217 foi explorada em ataques reais.

Na época da divulgação pública, havia dois patches no libvpx e um no Chromium que corrigiam a CVE-2023-5217. Os patches do libvpx incluíam a desativação de alterações no número de threads do VP8 e um teste para encoding multithread. O patch do Chromium desativou o ajuste do número de threads no WebCodecs.

Problema Subjacente no libvpx v1.13.0 Ugly Duckling

Resumo

O libvpx é uma biblioteca que lida com encoding e decoding de VP8/VP9.

A questão central da CVE-2023-5217 é que reduzir o número de threads enquanto se aumenta a altura do quadro em uma sessão de encoding VP8 do libvpx causa um estouro linear na pilha (heap) com comprimento controlado e sobrescrita controlada de um pequeno valor repetido de 4 bytes. A diferença na altura do quadro controla o comprimento da sobrescrita, e a nova largura do quadro controla o valor de 4 bytes que é escrito repetidamente. Essa vulnerabilidade pode ser explorada várias vezes para gravar continuamente diferentes valores pequenos de 4 bytes, reduzindo a altura em cada configuração subsequente.

Detalhes

O encoder VP8 do libvpx mantém um array chamado mt_current_mb_col que armazena a coluna atual em que uma thread do encoder está trabalhando. Esse array só é alocado se houver mais de uma thread, e seu tamanho é uma função de mb_rows, onde mb_rows = frame_height >> 4 e o frame_height é arredondado para cima até o múltiplo de 16 mais próximo.

root@kitploit:~
// https://github.com/webmproject/libvpx/blob/6512f994da13e2f27e6a7bd449efee0a374b55b7/vp8/common/alloccommon.c#L85
// A largura e a altura são arredondadas para cima até um múltiplo de 16 e depois atribuídas a `mb_rows` e `mb_cols`
int vp8_alloc_frame_buffers(VP8_COMMON *oci, int width, int height) {
    ...
    // Arredonda a largura/altura para cima até o múltiplo de 16 mais próximo
    if ((width & 0xf) != 0) width += 16 - (width & 0xf);
    if ((height & 0xf) != 0) height += 16 - (height & 0xf);
    ...
    oci->mb_rows = height >> 4;
    oci->mb_cols = width >> 4;
    ...
}
// https://github.com/webmproject/libvpx/blob/6512f994da13e2f27e6a7bd449efee0a374b55b7/vp8/encoder/onyx_if.c#L1232
// Este trecho mostra a alocação de `mt_current_mb_col`
void vp8_alloc_compressor_data(VP8_COMP *cpi) {
  ...
  // Só aloca se tivermos mais de 1 thread
  if (cpi->oxcf.multi_threaded > 1) {
    int i;

    vpx_free(cpi->mt_current_mb_col);
    // sizeof(*cpi->mt_current_mb_col) é 4
    CHECK_MEM_ERROR(&cpi->common.error, cpi->mt_current_mb_col,
                    vpx_malloc(sizeof(*cpi->mt_current_mb_col) * cm->mb_rows));
    for (i = 0; i < cm->mb_rows; ++i)
      vpx_atomic_init(&cpi->mt_current_mb_col[i], 0);
  }
  ...
}

Quando o libvpx termina de codificar um quadro, ele armazena o número de colunas codificadas mais mt_sync_range em mt_current_mb_col.

root@kitploit:~
// https://github.com/webmproject/libvpx/blob/6512f994da13e2f27e6a7bd449efee0a374b55b7/vp8/encoder/onyx_if.c#L1212
// Trecho onde o valor de mt_sync_range é definido, com base na largura.
void vp8_alloc_compressor_data(VP8_COMP *cpi) {
    ...
#if CONFIG_MULTITHREAD
  if (width < 640) {
    cpi->mt_sync_range = 1;
  } else if (width <= 1280) {
    cpi->mt_sync_range = 4;
  } else if (width <= 2560) {
    cpi->mt_sync_range = 8;
  } else {
    cpi->mt_sync_range = 16;
  }
#endif
  ...
}
// https://github.com/webmproject/libvpx/blob/6512f994da13e2f27e6a7bd449efee0a374b55b7/vp8/encoder/encodeframe.c#L560
// Função onde o valor escolhido pelo atacante é gravado
static void encode_mb_row(...) {
  ...
  const int nsync = cpi->mt_sync_range; // Este valor é definido em vp8_alloc_compressor_data
  vpx_atomic_int rightmost_col = VPX_ATOMIC_INIT(cm->mb_cols + nsync);
  ...
  if (vpx_atomic_load_acquire(&cpi->b_multi_threaded) != 0) {
    current_mb_col = &cpi->mt_current_mb_col[mb_row];
  }
  ...
  if (vpx_atomic_load_acquire(&cpi->b_multi_threaded) != 0) {
    // current_mb_col é uma referência a mt_current_mb_col
    vpx_atomic_store_release(current_mb_col,
                             vpx_atomic_load_acquire(&rightmost_col));
  }
  ...
}

Para estourar mt_current_mb_col, precisamos de três configurações de encoding:

  1. configinit: Durante a inicialização, o libvpx define initial_width e initial_height. Esses são os limites máximos possíveis das configurações posteriores. O número de threads aqui não importa. Precisamos da configuração intermediária porque nenhuma combinação posterior de largura/altura pode ser maior do que aquela com a qual começamos.
  2. configvuln: Durante a reconfiguração, se mais de uma thread for usada, o libvpx cria a alocação vulnerável de mt_current_mb_col com base em configvuln.height. Essa nova altura deve ser menor que configinit.initial_height, caso contrário receberemos um erro.
  3. configattack: Durante a reconfiguração, se apenas uma thread for usada, o libvpx não realocará mt_current_mb_col, deixando-o em um estado vulnerável. O libvpx gravará repetidamente o valor (configattack.width >> 4) + 1 (onde 1 é a variável mt_sync_range e a largura é arredondada para cima até o múltiplo de 16 mais próximo) fora dos limites previamente alocados quando a seguinte condição for válida:
root@kitploit:~
\text{ceil}(\text{config}_{\text{init}}.\text{height}/16) \geq \text{ceil}(\text{config}_{\text{attack}}.\text{height}/16) \gt \text{ceil}(\text{config}_{\text{vuln}}.\text{height}/16)$$

Estouro de mt_current_mb_col

Mais concretamente, suponha que inicializamos uma configuração de encoding VP8 com configinit com width = 1200, height = 1200 e threads = 4. O ataque é o seguinte:

  1. configvuln reconfigura o encoder com width = 500 (512 arredondado para cima), height = 700 (704 arredondado para cima) e threads = 2. A variável mb_rows é definida como 704/16=44, e o array mt_current_mb_col é alocado com (44)*4 = 176 bytes. O valor gravado em mt_current_mb_col é 512/16 + 1 = 33.
  2. configattack reconfigura o encoder com width = 18 (32 arredondado para cima), height = 1000 (1008 arredondado para cima) e threads = 1. Como mt_current_mb_col só é realocado quando há mais de uma thread, ele permanece com o mesmo tamanho, mas mb_rows agora é definido como 1008/16 = 63. Quando o libvpx chama encode_mb_row, ele sobrescreverá (63-44)*4 = 68 bytes além da alocação de mt_current_mb_col, gravando repetidamente o valor 32/16 + 1 = 3, onde 32 é a largura arredondada para cima e 1 é o valor de mt_sync_range.
  3. Um atacante poderia reexplorar essa vulnerabilidade com uma altura menor do que a de configattack, mas ainda maior do que a de configvuln, para gravar outro valor. Por exemplo, um atacante poderia criar configattack' com width = 34 e height = 990, definindo mb_rows = 992/16 = 62 e mb_cols = 48/16 = 3, gravando apenas (62-44)*4 = 64 bytes além da alocação original o valor 4.

Exploração

Para explorar essa vulnerabilidade, um atacante precisa ser capaz de controlar a altura, a largura e o número de threads do encoding. Os dois primeiros são simples, mas o último exige encontrar lugares onde o número de threads de encoding é reconfigurado.

root@kitploit:~
// https://github.com/webmproject/libvpx/blob/67bfb41ed8598edfb25bd6f245f9c39a68808548/vp8/vp8_cx_iface.c#L301
static vpx_codec_err_t set_vp8e_config(VP8_CONFIG *oxcf,
 ...
  oxcf->multi_threaded = cfg.g_threads;

Firefox

No Firefox, podemos controlar o número de threads ajustando a área do quadro que estamos codificando no VP8TrackEncoder. Se a área do quadro for maior que 307.200 (um quadro 640x480) e a máquina tiver mais de 2 núcleos, mais de uma thread será usada.

root@kitploit:~
// https://searchfox.org/mozilla-central/source/dom/media/encoder/VP8TrackEncoder.cpp#97
nsresult CreateEncoderConfig(...) {
  ...
  int32_t number_of_cores = PR_GetNumberOfProcessors();
  if (aWidth * aHeight > 1920 * 1080 && number_of_cores >= 8) {
    config->g_threads = 4;  // 4 threads para > 1080p.
  } else if (aWidth * aHeight > 1280 * 960 && number_of_cores >= 6) {
    config->g_threads = 3;  // 3 threads para 1080p.
  } else if (aWidth * aHeight > 640 * 480 && number_of_cores >= 3) {
    config->g_threads = 2;  // 2 threads para qHD/HD.
  } else {
    config->g_threads = 1;  // 1 thread para VGA ou menos
  }
  ...

Descobrimos que a API MediaRecorder depende do VP8TrackEncoder, e podemos ajustar a largura e a altura alterando o tamanho do canvas que está sendo gravado. Veja a seção MediaRecorder abaixo sobre como chamar isso.

Chrome

O Chrome também ajusta o número de threads com base na área do quadro sendo codificada, ajustado pelo número de núcleos.

root@kitploit:~
// https://source.chromium.org/chromium/chromium/src/+/main:media/video/vpx_video_encoder.cc;l=84
EncoderStatus SetUpVpxConfig(...) {
  ...
  // Define o número de threads com base na largura da imagem e no número de núcleos.
  config->g_threads = GetNumberOfThreadsForSoftwareEncoding(opts.frame_size);
}

// https://source.chromium.org/chromium/chromium/src/+/main:media/base/video_encoder.cc;drc=f5bdc89c7395ed24f1b8d196a3bdd6232d5bf771;l=33
int GetNumberOfThreadsForSoftwareEncoding(gfx::Size frame_size) {
  int area = frame_size.GetCheckedArea().ValueOrDefault(1);
  // Padrão de 1 thread para menos que VGA.
  int desired_threads = 1;

  if (area >= 3840 * 2160) {
    desired_threads = 16;
  } else if (area >= 2560 * 1080) {
    desired_threads = 8;
  } else if (area >= 1280 * 720) {
    desired_threads = 4;
  } else if (area >= 640 * 480) {
    desired_threads = 2;
  }

  // Limita ao número de processadores/núcleos lógicos disponíveis.
  desired_threads =
      std::min(desired_threads, base::SysInfo::NumberOfProcessors());

  return desired_threads;
}

Esse caminho é exercitado pela API WebCodecs VideoEncoding, onde podemos modificar diretamente a largura/altura do encoding. Veja a seção WebCodecs para entender como isso funciona.

MediaRecorder

O arquivo mediarecorder.html mostra como criar uma sessão MediaRecorder a partir de um canvas e ajustar a largura/altura para acionar uma reconfiguração de encoding VP8 e disparar a CVE-2023-5217 em um navegador vulnerável. Ao ajustar os parâmetros de largura e altura do canvas, usamos um setTimeout para garantir que a sessão de encoding VP8 tenha tempo suficiente para reconfigurar. O parâmetro de timeout pode ser ajustado para maior confiabilidade.

Status

  • ✅ Firefox: Dispara um crash no renderizador do Firefox.
  • ❌ Navegadores Chromium: Navegadores baseados em Chromium não alteram o número de threads ao reconfigurar o encoder em uma sessão MediaRecorder [código].
  • ❌ Safari: O WebKit não suporta VP8 para sessões MediaRecorder [código].

Demonstração no Firefox

Para testar no Firefox, você pode usar o fuzzfetch para obter uma build ASAN anterior ao patch dessa CVE com o comando fuzzfetch --build 2023-09-27 -a e depois abrir mediarecorder.html diretamente.

Demonstração no Firefox

WebCodecs

Os arquivos webcodecs.html e webcodecs.js mostram como usar a API WebCodecs em um Worker para acionar a CVE-2023-5217 em um navegador vulnerável. Temos mais controle sobre as chamadas para codificar um quadro no WebCodecs do que no MediaRecorder, mas ainda dependemos de um timeout para executar cada uma das três etapas.

Status

  • ✅ Navegadores Chromium: Dispara um crash na aba. Este patch do Chromium para WebCodecs foi incluído na triagem inicial.
  • ❌ Firefox: O Firefox não suporta encoding via WebCodecs (o decoding é habilitado por trás de uma flag de configuração).
  • 🚧 Safari: O Safari suporta encoding via WebCodecs, mas não consegui obter nenhum crash.

Demonstração no Chromium

Para testar no Chromium, você pode usar o get_asan_chrome.py para obter uma versão vulnerável do Chrome com o comando python get_asan_chrome.py --version 117.0.5938.131. Depois, você precisará iniciar um servidor HTTP local com SSL. Veja gen_server_key.sh e server.py para gerar uma chave de servidor e iniciar um servidor. Então, basta abrir a página no Chromium vulnerável para ver o resultado.

Demonstração no Chromium

WebCodecs + MediaRecorder Combinados

Veja combined.html, que usa o MediaRecorder como fallback quando o WebCodecs não está disponível. Esse arquivo combinado seria usado para mirar tanto no Chrome quanto no Firefox com a mesma página.

Conclusão

Essa vulnerabilidade demonstra os desafios e perigos de expor bibliotecas de mídia complexas a um atacante remoto. Usando ferramentas como o RLBox, os navegadores podem isolar possíveis vulnerabilidades em bibliotecas de mídia. O Firefox já distribui isso em bibliotecas selecionadas.

Obrigado por ler! Contribuições são bem-vindas. Sinta-se à vontade para abrir uma Issue ou um PR com quaisquer outros insights. O que resta explorar é ver como essa pequena sobrescrita de 4 bytes pode levar à execução de código.

Agradecimentos ao Anand Balaji pelo feedback em um rascunho anterior.

Baixar ferramenta