Jul 13, 2026·6 min read·26 visits
Unsanitized user-controlled metadata fields rendered via the safe filter in GeoNode templates allow unauthenticated or low-privilege users to store malicious script payloads. Upon administrative review, the script executes within the victim session, permitting silent account takeover.
A comprehensive security analysis of CVE-2024-27091, a stored cross-site scripting (XSS) vulnerability in the GeoNode geospatial content management system. Unsanitized metadata fields rendered with Django's safe template filter permit stored JavaScript execution, leading directly to admin account takeover via CSRF token theft and silent profile email modification.
GeoNode is a specialized geospatial content management system used globally to manage, catalog, and publish spatial databases. Because GeoNode hosts administrative functions and map publishing controls, its users often possess elevated system permissions. The application exposes an extensive metadata interface allowing users to document layers, maps, and documents. These metadata parameters accept rich-text formatting to facilitate highly structured documentation.
This documentation structure exposes a distinct attack surface. Unauthenticated or low-privilege registered users can submit custom metadata configurations via the platform's REST API or UI forms. Fields such as abstracts, purposes, and keywords are saved directly to the underlying PostgreSQL database. The application's failure to sanitize these parameters before database insertion or HTML rendering creates a stored cross-site scripting (XSS) pathway.
When a high-privilege user or administrator browses to the metadata details of a poisoned resource, the payload executes. Since the script executes within the victim's validated origin, it bypasses authorization restrictions without relying on cookie extraction. This vulnerability is cataloged as CWE-79, representing improper neutralization of input during web page generation.
The root cause of CVE-2024-27091 resides in GeoNode's integration of Django's template engine. Django templates are secure by default because they automatically HTML-escape dynamic variables. However, developers can override this safety feature using the |safe filter. The |safe filter explicitly instructs the rendering engine that the variable contents are pre-sanitized and can be rendered directly as raw markup.
In affected versions, the template file geonode/templates/metadata_detail.html used the |safe filter on multiple user-controlled attributes. This included resource.abstract, keyword.name, resource.constraints_other, resource.purpose, resource.data_quality_statement, and resource.supplemental_information. No backend sanitization step occurred before the templates rendered these fields, making them highly vulnerable to payload injection.
Because the rich-text WYSIWYG editor's client-side protections were the only barrier against script injection, attackers could easily bypass them. An attacker could issue direct API requests or bypass frontend input validation entirely. The backend did not enforce server-side validation or tag parsing, allowing database tables to store dangerous raw HTML wrappers.
The vulnerability was patched in commit e53bdeff331f4b577918927d60477d4b50cca02f by implementing a server-side HTML sanitization layer. The developers introduced a custom Django template filter named sanitize_html in geonode/base/templatetags/sanitize_html.py. This filter relies on the nh3 Python library, which leverages the Rust-based ammonia library to guarantee performant and secure HTML purification.
Below is the implementation of the custom filter from the official patch:
from django import template
from django.conf import settings
import nh3
from django.utils.encoding import force_str
register = template.Library()
@register.filter(name="sanitize_html")
def sanitize_html(value):
value = force_str(value)
nh3_config = getattr(
settings,
"NH3_DEFAULT_CONFIG",
{
"tags": {"b", "a", "img", "p", "ul", "li", "strong", "em", "span"},
},
)
return nh3.clean(value, **nh3_config)The custom filter parses the HTML structure and strips all tags and attributes not defined in the safe whitelist. Elements like <script>, <iframe>, and event handlers (e.g., onerror) are completely removed. In the metadata templates, all instances of user-controlled variables were modified to chain this sanitization filter ahead of the |safe filter:
<!-- Vulnerable Template Implementation -->
<dd>{{ resource.abstract|safe }}</dd>
<!-- Patched Template Implementation -->
<dd>{{ resource.abstract|sanitize_html|safe }}</dd>An attacker can exploit this vulnerability to achieve full account takeover of any platform administrator who views the compromised metadata page. Although GeoNode sets session cookies with the HttpOnly and Secure attributes, which blocks direct extraction via document.cookie, the execution of arbitrary JavaScript within the application's origin allows attackers to perform complex state-changing actions on behalf of the victim.
The attack begins when the victim loads the detail page of a modified resource. The malicious script executes and immediately reads the anti-CSRF token directly from the DOM, specifically targeting the csrfmiddlewaretoken hidden input field or parsing it from the local cookie store. Because the browser automatically attaches the victim's session cookies to requests sent to the same origin, the script is authorized to perform state-changing HTTP requests.
Once the CSRF token is obtained, the script initiates a background POST request to the email management endpoint (e.g., /account/email/change/). The payload contains the stolen CSRF token and updates the primary email address to an inbox controlled by the attacker. Since this request executes seamlessly in the background, the administrator receives no immediate alert. The attacker then triggers a standard password reset request for the admin account, receiving the reset token via the newly registered email address to complete the takeover.
The security impact of CVE-2024-27091 is classified as medium-severity with a CVSS v3.1 base score of 6.1. This score reflects an attack vector requiring low complexity and zero initial privileges, but relying heavily on user interaction. The scope change (S:C) highlights that the script execution context can compromise resource parameters beyond the immediate data layer.
A successful compromise can lead to a full takeover of administrative sessions. In a geospatial environment, an administrative takeover provides the adversary with total access to maps, imagery datasets, secure databases, and internal infrastructure configurations. Attackers could manipulate geographical boundary data, delete spatial databases, or use the compromised platform to launch downstream attacks against public GIS map users.
While the EPSS score of 0.00376 indicates a low probability of immediate automated exploitation in the wild, targeted environments face a high threat. This flaw acts as a silent privilege escalation bottleneck. Security operations should treat it as high risk, particularly in federal, academic, or corporate spatial systems where GeoNode serves as a central data repository.
The primary remediation path for CVE-2024-27091 is upgrading all GeoNode deployments to version 4.2.3 or later. This upgrade modifies the system dependencies to require the Rust-backed nh3 HTML sanitizer, ensuring all stored user-supplied metadata fields are processed before rendering. For systems running older, unpatched releases where an immediate upgrade is not feasible, administrators must apply manual code-level mitigations.
To implement a manual hotfix, security engineers should copy the custom sanitize_html.py tag filter file from the official patch commit and install the nh3 library (pip install nh3). All template files referencing |safe filters must be reviewed. Apply the |sanitize_html filter preceding any |safe filter as demonstrated in the patch diff.
In addition to patching, deploying a strong Content Security Policy (CSP) is highly recommended. Enforcing a policy such as Content-Security-Policy: default-src 'self'; script-src 'self'; prevents inline scripts and event handlers from executing. This defense-in-depth measure completely neutralizes the threat of stored XSS, even if sanitization is bypassed or omitted on future templates.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
GeoNode GeoSolutions Group | >= 3.2.0, < 4.2.3 | 4.2.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 (Improper Neutralization of Input During Web Page Generation) |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 | 6.1 (Medium) |
| Exploit Status | Proof of Concept / Code Analysis Available |
| EPSS Score | 0.00376 (Percentile: 29.75%) |
| KEV Status | Not Listed |
The software does not neutralize or incorrectly neutralizes user-controlled input before it is placed in output that is used as a web page that is served to other users.
CVE-2026-9318 is a stored cross-site scripting (XSS) vulnerability affecting Jazzband tablib versions prior to 3.10.0. The flaw is located in the HTML export functionality of multi-sheet Databook objects. Due to raw f-string interpolation, unsanitized sheet titles containing malicious script tags are rendered directly as HTML, allowing arbitrary client-side code execution in a victim's browser.
CVE-2026-54917 is a critical path traversal and authorization bypass vulnerability affecting the S3 and Iceberg REST catalog gateways in SeaweedFS. By explicitly disabling canonical path cleaning in the gorilla/mux routing system, relative path segments such as '..' are allowed to bypass routing constraints and access control checks. When these paths are collapsed server-side by the backend filer, they resolve to folders outside the authorized bucket boundary, allowing unauthorized cross-bucket access.
An out-of-bounds read vulnerability in the SCTP SACK chunk parser of SIPSorcery leads to Denial of Service (DoS) or silent internal state corruption due to lack of boundary validation on incoming chunk elements.
An uncaught exception vulnerability exists in SIPSorcery's TurnServer component, where unauthenticated malformed UDP packets can crash the core UDP receive loop, resulting in a persistent Denial of Service.
A critical use-after-free vulnerability in Microsoft QUIC allows unauthenticated remote attackers to disclose sensitive system memory over the network. The vulnerability is caused by a race condition during rapid connection termination and asynchronous packet retransmission.
CVE-2026-62899 is a security feature bypass vulnerability in the Microsoft .NET runtime environment on non-Windows platforms. The flaw manifests as an HTTP Request/Response Smuggling vulnerability (CWE-444) within the managed implementation of the System.Net.HttpListener class. This allows unauthenticated remote attackers to desynchronize request boundaries when the backend .NET application is hosted behind an upstream reverse proxy.