CVE-2026-15307 Overview
CVE-2026-15307 affects Django 5.2 before 5.2.17 and 6.0 before 6.0.8. GeoDjango spatial lookups optimistically parse the right-hand-side value as a raster by passing it to the django.contrib.gis.gdal.GDALRaster constructor. Any value used in a spatial lookup against a GeometryField or RasterField reaches this constructor, including untrusted input. A dict or JSON string is opened in write mode, allowing an attacker to write a file with a chosen name and contents through a file-backed GDAL driver. Any other string is treated as a datasource, enabling outbound network requests through a GDAL virtual filesystem handler.
Critical Impact
Writing a file to a location later imported by the application can result in remote code execution. Authenticated staff users with view permission on spatial models can trigger the flaw through the Django admin changelist query string.
Affected Products
- Django 5.2 versions before 5.2.17
- Django 6.0 versions before 6.0.8
- Unsupported Django series (5.1.x, 5.0.x, 4.2.x) were not evaluated and may also be affected
Discovery Timeline
- Vulnerability reported by Bence Nagy, localhost-detect, and kimchunbok_
- 2026-08-04 - Django security release published and CVE-2026-15307 published to NVD
- 2026-08-05 - Last updated in NVD database
Technical Details for CVE-2026-15307
Vulnerability Analysis
The flaw resides in GeoDjango's spatial lookup handling under django.contrib.gis. When a value is filtered against a GeometryField or RasterField, Django optimistically passes it to GDALRaster to check whether it represents a raster. This constructor accepts multiple input types with different semantics. A Python dict, or a str holding its JSON representation, causes GDAL to open the datasource in write mode regardless of the constructor's write=False default. An attacker can supply JSON specifying a driver, a filename, and payload contents, causing GDAL to create that file on disk.
A plain str input is instead treated as a datasource path. GDAL resolves prefixes such as /vsicurl/ and /vsis3/, triggering outbound network requests through virtual filesystem handlers. The vulnerability is classified under CWE-73: External Control of File Name or Path.
Root Cause
GeoDjango calls GDALRaster(value) on lookup inputs without validating the type or contents. The default write=False flag is overridden when the input is a dict or JSON, so file-backed GDAL drivers create attacker-controlled files. String inputs fall through to GDAL's virtual filesystem layer, which supports network transports.
Attack Vector
Any code path that reaches a spatial lookup with untrusted input is exploitable. The advisory specifically calls out the Django admin changelist query string, which a staff user with only view permission on a spatial model can manipulate. Writing a .py file, template, or configuration into a directory later imported or executed by the application yields remote code execution.
# Django security patch introducing DisallowedRasterLookup
# Source: https://github.com/django/django/commit/115ffd0463a765ab1cc93de18e94b5459b8a300e
from django.contrib.gis.gdal.srs import SpatialReference, SRSException
from django.contrib.gis.geometry import json_regex
from django.core.exceptions import SuspiciousOperation
from django.utils.encoding import force_bytes, force_str
from django.utils.functional import cached_property
class DisallowedRasterLookup(SuspiciousOperation):
"""
Types that force GDALRaster to open in write mode (dict) or values that
could be virtual filesystem paths (str) are not allowed in lookup contexts.
Instead, wrap values in GDALRaster explicitly.
"""
The patch adds a DisallowedRasterLookup exception (a SuspiciousOperation subclass) and blocks dict inputs and strings matching the VSI_FILESYSTEM_PREFIX in spatial lookup code paths.
Detection Methods for CVE-2026-15307
Indicators of Compromise
- Unexpected files created in application-writable directories, especially .py, .pyc, or template files under sys.path-reachable paths
- Django admin changelist requests containing JSON payloads with driver, name, or datapointer keys in spatial field filter parameters
- Query-string values against GeometryField or RasterField filters that begin with /vsicurl/, /vsis3/, /vsigs/, or other GDAL VSI prefixes
- Outbound network connections initiated by the Django process to domains not tied to normal application behavior
Detection Strategies
- Search Django access logs for admin URLs containing spatial field lookups with {, }, or /vsi sequences in filter parameters
- Instrument or wrap django.contrib.gis.gdal.GDALRaster calls to log the input type and value during audit periods
- Correlate staff-user admin activity with file-creation events under the application's working directory
Monitoring Recommendations
- Alert on SuspiciousOperation exceptions in Django application logs after patching, as they may indicate exploitation attempts against the new DisallowedRasterLookup guard
- Monitor GeoDjango-enabled hosts for new files written to MEDIA_ROOT, STATIC_ROOT, or any directory listed in sys.path
- Track egress connections from Python worker processes to unexpected destinations, which may indicate abuse of GDAL VSI handlers
How to Mitigate CVE-2026-15307
Immediate Actions Required
- Upgrade Django to version 5.2.17 or 6.0.8 as published in the Django security releases
- Review staff-user permissions on models exposing GeometryField or RasterField in the Django admin and revoke unnecessary view access
- Audit filesystem locations writable by the Django process for unexpected files created since GeoDjango was enabled
Patch Information
Django released fixes across supported branches in commits 115ffd0 (5.2.x), 208f80c (6.0.x), and 39b3e2d (6.1.x). The patches introduce DisallowedRasterLookup and reject dict inputs and strings with GDAL VSI prefixes in spatial lookup contexts. Applications relying on raster values in lookups must now wrap them explicitly in GDALRaster.
Workarounds
- If immediate patching is not possible, remove or restrict Django admin access to models that expose GeometryField or RasterField filters
- Place a web application firewall rule in front of Django admin URLs that blocks query strings containing /vsi prefixes or JSON-encoded values in spatial field parameters
- Run the Django application process under a minimal filesystem sandbox that prevents writes outside a dedicated data directory not listed in sys.path
# Upgrade Django to a patched release
pip install --upgrade "Django>=5.2.17,<6.0" # for 5.2.x deployments
pip install --upgrade "Django>=6.0.8" # for 6.0.x deployments
# Verify the installed version
python -c "import django; print(django.get_version())"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

