Aug 26, 2026·6 min read·11 visits
An authenticated staff user can bypass multi-tenancy access controls in OpenWISP IPAM to export IP allocation tables of other organizations by targeting the custom subnet export view with a specific identifier.
A broken object-level authorization (BOLA) vulnerability exists in the Django Admin custom export view of OpenWISP IPAM. This flaw allows a multi-tenancy restricted staff user to export subnets and associated IP addresses belonging to different organizations by supplying a targeted subnet identifier in the export request.
OpenWISP IPAM is an IP address management system designed for multi-tenant deployments, providing strict logical separation between network architectures of different organizations. The administrative interface leverages Django Admin to allow designated operators to manage subnets, IP leases, and allocation structures. Within this framework, secure multi-tenancy relies on restricting visibility and access permissions based on the active user's associated organization.
A critical authorization bypass vulnerability, identified as GHSA-x287-5c68-36wp, exists within the custom subnet export feature of the Django Admin interface. The export feature is designed to allow staff members to download complete CSV reports of the IP addresses assigned to their managed subnets. However, the endpoint handling this request fails to validate that the requested subnet belongs to an organization the authenticated user is authorized to manage.
This failure results in a Broken Object-Level Authorization (BOLA) vulnerability, classified as CWE-639. An authenticated staff user can bypass multi-tenancy isolation and access detailed IP space reports of other tenants simply by specifying an unauthorized subnet identifier in the export request. This flaw leads to unauthorized data disclosure of internal network topologies, which could be leveraged to map target networks.
The root cause of this vulnerability lies in the implementation of the export_view method within openwisp_ipam/admin.py. The OpenWISP administrative classes inherit from MultitenantAdminMixin, which intercepts standard Django Admin query requests to restrict results according to the organization of the requesting user. While this mixin secures standard database queries returned by the get_queryset method, it does not automatically enforce constraints on custom-defined views unless explicitly invoked.
In the vulnerable implementation, the export_view method processes the subnet export request using a user-controlled parameter, subnet_id, extracted from the URL. Instead of querying the database through the filtered queryset provided by the administrative mixin, the code passes the subnet_id directly to the Subnet().export_csv() method. This direct database interaction bypasses the security context established by MultitenantAdminMixin.
By omitting the tenant-filtering layer, the endpoint permits an insecure direct object reference. Since Django handles model lookup inside the raw export_csv call without checking ownership boundaries, the application retrieves and outputs records for any valid UUID. This flaw allows a restricted user to bypass the logical partitions of the multi-tenant architecture and access data across the entire database.
The vulnerability is resolved by modifying how the target subnet instance is fetched in the export_view handler. Rather than delegating lookup entirely to the underlying export_csv method, the patched code retrieves the subnet object through get_object_or_404, forcing the query to evaluate within the context of the user's filtered queryset.
Below is the comparison between the vulnerable code path and the corrected implementation:
# Vulnerable Code Path
def export_view(self, request, subnet_id):
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = 'attachment; filename="ip_address.csv"'
writer = csv.writer(response)
# Direct model method execution bypasses get_queryset authorization checks
Subnet().export_csv(subnet_id, writer)
return response
# Patched Code Path
def export_view(self, request, subnet_id):
# Enforces authorization by scoping lookup within self.get_queryset(request)
subnet = get_object_or_404(self.get_queryset(request), pk=subnet_id)
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = 'attachment; filename="ip_address.csv"'
writer = csv.writer(response)
# Uses authorized object identifier
Subnet().export_csv(subnet.id, writer)
return responseBy querying self.get_queryset(request), the system restricts the search space to objects associated with the authenticated user's organization. If an attacker submits a subnet_id belonging to a foreign tenant, the get_queryset call will exclude the object, and get_object_or_404 will raise an HTTP 404 error, blocking further execution.
Here is the flow of the unauthorized request before and after the patch:
To exploit this vulnerability, an attacker must first obtain valid credentials for a staff account on the target OpenWISP instance. While this requires authenticated status, the attacker does not need high-level global administrative privileges. The attack complexity is low, as the exploit only requires sending an HTTP GET request to the vulnerable endpoint with the targeted subnet UUID.
The targeted URL follows the pattern /admin/ipam/subnet/<subnet_id>/export/. Because OpenWISP uses UUIDs for model identifiers, an attacker must obtain or predict the target subnet_id. In scenarios where UUIDs are exposed via logs, client-side metadata, or predictable generation patterns, an attacker can construct the payload and execute the request.
The official regression test demonstrates the vulnerability mechanics. An administrator associated only with org1 attempts to trigger the export view on subnet2 belonging to org2. Prior to the patch, this request returned an HTTP 200 OK status code along with the complete CSV data of the unauthorized subnet. Following the integration of the patch, the system correctly denies access and returns an HTTP 404 response.
The impact of this vulnerability is limited to unauthorized information disclosure, resulting in a CVSS v3.1 score of 7.1. An attacker can download detailed mappings of any subnet in the system, exposing the IP addresses, associated hostnames, mac addresses, and descriptions of internal network nodes. This structural data provides a blueprint of the organization's internal architecture.
No integrity or availability impact exists, as the vulnerable view is read-only and does not permit modification or deletion of database records. However, because OpenWISP is often used by network service providers to manage infrastructure across distinct corporate clients, the breakdown of multi-tenant boundaries represents a critical threat to data isolation agreements.
Furthermore, additional REST API endpoints may remain susceptible to similar validation gaps if they bypass the filtered queryset logic. Specifically, views such as ExportSubnetView.post() handling /api/v1/subnet/{S}/export/, AvailableIpView.get(), and RequestIPView.post() must be evaluated. If these API views resolve subnets through direct object queries rather than authorization-scoped querysets, similar BOLA exposures could still be exploited.
Remediation requires upgrading the openwisp-ipam package to version 1.2.1 or later, which implements the secure get_object_or_404 verification step. This update closes the direct object access vector across the Django Admin export interface.
If an immediate package upgrade is not feasible, a manual hotfix can be applied directly to the codebase. Organizations must locate the installed openwisp_ipam/admin.py file within their environment and modify the export_view method to enforce queryset scoping. Ensure the get_object_or_404 utility is imported from django.shortcuts before replacing the vulnerable lookup line.
For long-term defensive engineering, developers must avoid executing model operations directly on user-provided primary keys without first validating ownership. In multi-tenant systems, all database operations initiated from user-facing views must route through a security-enforcing interface layer, such as an authorized queryset manager or serialization validator, to prevent regression vulnerabilities.
| Product | Affected Versions | Fixed Version |
|---|---|---|
openwisp-ipam OpenWISP | < 1.2.1 | 1.2.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-639 |
| Attack Vector | Network |
| CVSS Score | 7.1 (High) |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
| Impact | Information Disclosure |
A heap-based buffer overflow vulnerability in Pillow PyPI binary wheels (versions 11.1.0 to 12.x) allows remote code execution or application crash via crafted JPEG 2000 files processed by the bundled libopenjp2 library.
CVE-2026-109618 (dubbed PHP2Shell) is a critical vulnerability in the PHP engine core resulting from improper out-of-memory exception handling via longjmp(). When request allocation fails mid-processing, non-local control flow bypasses cleanup procedures, leaving memory structures uninitialized or in a use-after-free state during request shutdown.
CVE-2026-109619 (PHP2Shell) is a critical Use-After-Free vulnerability in the core PHP engine caused by improper global state cleanup during out-of-memory exception handling across persistent worker requests.
In Python virtualenv prior to version 21.14.9, activation script generators and configuration writers use Path.exists() before writing files. Because Path.exists() resolves symbolic links, it returns False for dangling symlinks. Consequently, virtualenv skips unlinking pre-planted symbolic links or hard links, writing data directly to target locations outside the intended virtual environment directory.
A remote code execution vulnerability exists in Microsoft .NET due to untrusted data deserialization within the PresentationCore component. The flaw stems from implicit auto-conversion flags during clipboard data retrieval operations.
An out-of-bounds write vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio allows unauthenticated remote attackers to execute arbitrary code via crafted pixel-shader bytecode or auto-converted clipboard payloads.