CVE-2026-87890
Executive Summary
CVE-2026-87890 is a Server-Side Request Forgery (SSRF) flaw in Django's spatial lookups, arising from an incomplete fix for the earlier CVE-2026-15307. An attacker who can supply bytes values to the affected lookups can force the Django process to issue network requests to attacker-chosen destinations by referencing an external raster source in a crafted VRT (GDAL Virtual Format) document. Django 6.1 before 6.1.2, 6.0 before 6.0.9, and 5.2 before 5.2.18 are affected; unsupported series (5.1.x, 5.0.x, 4.2.x) were not evaluated and may also be vulnerable. Impact is limited-confidentiality information disclosure via SSRF (CVSS 5.3), with the Django process acting as a network pivot — no public PoC, exploit code, or Nuclei template is available, and the issue is not in CISA KEV.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Technical Analysis
The vulnerability class is CWE-918 (Server-Side Request Forgery), confirmed by the NVD description. The flaw is an incomplete fix for a prior SSRF issue, CVE-2026-15307, in Django's spatial lookups. Spatial lookups are the query components that operate on geographic/geometric fields via GeoDjango and its GDAL/GEOS backend stack.
The attack mechanism described is specific: an attacker "who can supply bytes values" can "cause the Django process to make network requests via a crafted VRT document referencing an external raster source." In GDAL, VRT (Virtual Raster) is an XML-based format that can reference external raster sources by URI/path. The vulnerability arises because a complete fix should have rejected or constrained the processing of attacker-supplied raster input so that it cannot trigger outbound fetches of external sources. The incomplete fix left a path where a bytes value (raw binary raster/VRT content supplied through a spatial lookup) is parsed and, because it contains a VRT <SourceFilename>-style external reference, causes the server to dereference that external resource over the network.
The key technical facts derivable from the description:
- Trigger point: Django spatial lookups accepting attacker-controlled
bytes. - Payload: a crafted VRT document embedding a reference to an external raster source.
- Effect: the Django process itself makes the outbound network request (SSRF), not the client.
- Relationship: this CVE is the evasion of the CVE-2026-15307 remediation — the same root cause class, incompletely closed.
Insufficient technical detail is available to name the exact lookup classes, parameters, or code paths; the NVD description does not provide them, and they must not be invented. What can be stated confidently is that any code path where attacker-supplied binary geospatial data reaches the spatial lookup / GDAL parsing layer is in scope. The CVSS vector (AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N) indicates remote exploitation with no privileges or user interaction, unchanged scope, and limited confidentiality impact only — consistent with SSRF used to reach internal-only resources reachable from the Django host.
Exploitation Scenarios
No public PoC is available; the attack chain below is inferred strictly from the vulnerability class (SSRF via crafted VRT raster reference) and the NVD description. No specific function, parameter, or endpoint names are asserted because the description does not provide them.
Reconnaissance / target identification. An attacker identifies a Django deployment exposing the geographic/spatial feature set (GeoDjango), where request data can reach spatial lookups that accept binary raster input. Targeted fingerprinting keys on Django version disclosure and on application endpoints that accept file/blob uploads consumed by spatial queries.
Triggering the flaw class. The attacker crafts a VRT document — an XML raster virtualization file whose purpose is to reference an external raster source — and supplies its bytes to the vulnerable spatial lookup. Because the fix for CVE-2026-15307 was incomplete, the parser does not adequately restrict external source dereferencing, so the lookup processes the reference rather than rejecting it.
Payload / execution method. The VRT's external raster source reference is the payload. It is not the reference itself that is dangerous; the danger is that the Django server, running with its own network position, resolves and fetches that source. The attacker selects a target: an internal-only HTTP service, a cloud metadata endpoint, a file/URL reachable solely from the server's network segment, or an external listener the attacker controls to confirm blind SSRF.
Post-exploitation impact. Consistent with CVSS C:L / I:N / A:N, the primary impact is information disclosure: reading responses from internal services the attacker cannot reach directly, confirming internal network topology, or obtaining credentials/config exposed on an internal endpoint. The SSRF can also serve as a foothold for lateral reconnaissance. No integrity or availability impact is scored, and there is no evidence in the supplied data of remote code execution, so claims of arbitrary command execution would be unsupported.
Impact Assessment
Affected systems are Django deployments at versions 6.1 prior to 6.1.2, 6.0 prior to 6.0.9, and 5.2 prior to 5.2.18, specifically those that expose spatial-lookup functionality (GeoDjango) to untrusted input. Unsupported series 5.1.x, 5.0.x, and 4.2.x were not evaluated and may additionally be affected; organizations running those branches cannot assume immunity.
Blast radius is bounded by the environment's network topology, not by Django itself. Because the request originates from the Django process, any resource that process can reach — internal APIs, admin planes, cloud instance metadata, databases on an internal subnet — becomes reachable in a read-only sense. CVSS 5.3 with scope unchanged (S:U), confidentiality low (C:L), and no integrity or availability impact (I:N/A:N) defines a moderate-severity exposure. The absence of public exploit code, a Nuclei template, and CISA KEV listing lowers immediate opportunistic risk, but the flaw is a repeat of a previously exploited pattern (CVE-2026-15307) and incomplete-fix regressions are frequently rediscovered by researchers. Public-facing GeoDjango applications handling untrusted geospatial data should treat this as a priority though not emergency fix.
Detection Guidance
Sigma Rule
title: Suspicious VRT Raster External Reference via Django Spatial Lookups
id: 6f0e8a2c-3b41-4d9e-9c2a-7d5f1b8e4a10
status: experimental
description: Detects HTTP request bodies containing VRT (Virtual Raster) XML that reference an external raster source, indicative of CVE-2026-87890 SSRF exploitation against Django spatial lookups.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-87890
author: valtersit.com Threat Intelligence
date: 2026/01/01
logsource:
category: webserver
detection:
selection_vrt_marker:
- 'cs-body|contains': 'VRTDataset'
- 'cs-body|contains': 'SourceFilename'
selection_uri_scheme:
- 'cs-body|contains': 'http://'
- 'cs-body|contains': 'https://'
- 'cs-body|contains': 'file://'
selection_external_ref:
- 'cs-body|contains': 'relativeToVRT'
condition: selection_vrt_marker and selection_uri_scheme
fields:
- clientip
- cs-method
- cs-uri-stem
- cs-body
falsepositives:
- Legitimate geospatial upload workflows that intentionally reference external raster sources
level: medium
tags:
- attack.t1190
- cwe.918
YARA Rule
rule CVE_2026_87890_VRT_SSRF
{
meta:
description = "Detects crafted VRT raster documents embedding external raster source references used to trigger SSRF in Django spatial lookups (CVE-2026-87890)"
author = "valtersit.com Threat Intelligence"
date = "2026-01-01"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2026-87890"
cwe = "CWE-918"
strings:
$vrt_root = "VRTDataset" ascii wide nocase
$src_file = "SourceFilename" ascii wide nocase
$rel_vrt = "relativeToVRT" ascii wide nocase
$http = "http://" ascii
$https = "https://" ascii
$fileuri = "file://" ascii
$meta_aws = "169.254.169.254" ascii
$band = "VRTRasterBand" ascii wide nocase
condition:
$vrt_root and $band and $src_file and $rel_vrt and
( any of ($http, $https, $fileuri) or $meta_aws )
}
Suricata IDS Rule
alert http any any -> any any (msg:"CVE-2026-87890 Django Spatial Lookup VRT SSRF Attempt"; flow:established,to_server; http.method; content:"POST"; http.request_body; content:"VRTDataset"; content:"SourceFilename"; content:"relativeToVRT"; pcre:"/(http|https|file):\/\//Ri"; reference:cve,2026-87890; reference:url,nvd.nist.gov/vuln/detail/CVE-2026-87890; classtype:attempted-recon; sid:202687890; rev:1; metadata:created_at 2026_01_01, updated_at 2026_01_01, mitre_tactic_id T1190, mitre_tactic_name Exploit_Public-Facing_Application;)
Elastic Detection
sequence by host.name
[ network where http.request.method == "POST" and
http.request.body.content : "VRTDataset" and
http.request.body.content : "SourceFilename" ]
[ network where http.response.status_code >= 200 and
http.response.status_code < 300 and
http.request.body.content : ("http://" or "https://" or "file://") ]
Splunk SPL Query
index=web OR index=proxy
(sourcetype=access_combined OR sourcetype=stream:http)
"VRTDataset" ("SourceFilename" OR "relativeToVRT")
| rex field=_raw "(?<external_ref>(https?|file)://[^\s\"<>]+)"
| eval cve="CVE-2026-87890"
| stats count min(_time) as first_seen max(_time) as last_seen
values(external_ref) as external_targets
values(clientip) as source_ips by cve host
| where count > 0
| convert ctime(first_seen) ctime(last_seen)
Wazuh OSSEC Rule
<group name="web,django,cve-2026-87890,">
<rule id="2026878" level="10">
<if_sid>31100,31101,31108</if_sid>
<match>VRTDataset</match>
<match>SourceFilename</match>
<match>relativeToVRT</match>
<description>CVE-2026-87890: Possible Django spatial lookup SSRF via crafted VRT raster document referencing an external source.</description>
<mitre>
<id>T1190</id>
</mitre>
<group>cwe_918,ssrf,</group>
</rule>
<rule id="2026879" level="12">
<if_sid>2026878</if_sid>
<regex>(https?|file)://</regex>
<description>CVE-2026-87890: VRT document contains external URI reference - probable SSRF exploitation attempt.</description>
<mitre>
<id>T1190</id>
</mitre>
</rule>
</group>
Mitigation & Patch Guidance
Patch confidence: 0/4 (UNPATCHED — no patch evidence from any tracked source).
Immediate mitigations.
- Treat all attacker-supplied binary raster/geospatial input as untrusted. Where feasible, disallow user-controlled
bytesfrom reaching spatial lookup paths that trigger GDAL VRT parsing. - Apply network egress controls on the Django host: block outbound requests from the application tier to internal-only ranges, link-local/cloud metadata addresses, and non-allowlisted external hosts. This directly blunts the SSRF effect even with the flaw present.
- Place GeoDjango processing behind an isolated worker/segment with a restrictive egress policy, so a successful VRT dereference cannot pivot into sensitive internal networks.
- Monitor for the VRT indicators in the detection rules above and alert on any application-tier request to cloud metadata or non-allowlisted internal endpoints.
- Treat all attacker-supplied binary raster/geospatial input as untrusted. Where feasible, disallow user-controlled
Patch / upgrade steps.
- The NVD description states the issue is fixed in Django 6.1.2, 6.0.9, and 5.2.18. Upgrade to the nearest fixed release on your supported branch: 6.1.x -> 6.1.2+, 6.0.x -> 6.0.9+, 5.2.x -> 5.2.18+.
- For unsupported branches (5.1.x, 5.0.x, 4.2.x), there is no evaluated fix; plan migration to a supported, fixed branch.
- Because this is an incomplete-fix regression for CVE-2026-15307, verify that any earlier mitigations applied for that CVE are superseded by the newer release rather than relied upon. Obtain patch confirmation from the official Django release notes/advisory before closing remediation.
Long-term hardening.
- Institute an egress allowlist by default for application servers, with explicit exceptions reviewed regularly — SSRF classes recur and egress control is the durable control.
- Require SSRF-safe outbound fetching patterns for any component that dereferences user-supplied URIs or formats that embed them (VRT, other GDAL virtual formats).
- Maintain an asset inventory of GeoDjango/spatial-feature-enabled applications so regressions in spatial lookups are rapidly scoped.
- Track the CVE-2026-15307 -> CVE-2026-87890 lineage and audit for sibling incomplete fixes in the same code area.
- Enable dependency/version alerting for Django security releases so fixed versions are adopted promptly.
References
- NVD — CVE-2026-87890: https://nvd.nist.gov/vuln/detail/CVE-2026-87890
- NVD — CVE-2026-15307 (original SSRF; this CVE is the incomplete fix): https://nvd.nist.gov/vuln/detail/CVE-2026-15307
- Django project security releases and advisories: https://www.djangoproject.com/weblog/ and https://docs.djangoproject.com/en/dev/releases/security/
- MITRE CWE-918 — Server-Side Request Forgery: https://cwe.mitre.org/data/definitions/918.html
- CISA Known Exploited Vulnerabilities catalog (CVE-2026-87890 not listed as of this report): https://www.cisa.gov/known-exploited-vulnerabilities-catalog
No public PoC, exploit code, or Nuclei template was identified in the supplied data. No threat actor campaign or in-the-wild exploitation evidence is associated with this CVE in the available data; the issue was credited to researcher "sicksec" per the NVD description.
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.