Quantcast
Channel: Issues for Drupal core
Viewing all articles
Browse latest Browse all 296658

Code execution via Twig templates (including inline)

$
0
0

Originally reported by samuel.mortenson on February 20, 2017 to security.drupal.org #161890

Clear by the Drupal Security team for public resolution as a critical bug.

Problem/Motivation

Summary: Users with access to Twig files or inline Twig templates can abuse the Drupal's support for render arrays in Twig to execute arbitrary code.

===

Views has an arbitrary code execution vulnerability.

Replication steps:

  1. Enable Views
  2. As a user with the "Administer views" permission, create a new View of User Fields (any view with result rows will work)
  3. Add a "Custom text" field
  4. Enter the text "{{ {"#lazy_builder": ["shell_exec", ["touch /tmp/hellofromviews"]]} }}"
  5. Click "Update preview"
  6. See that the file "/tmp/hellofromviews" exists on your machine

This is possible as the "render_var" function is available to Twig entered through Views. As a result, users can enter arbitrary render arrays, which allows for code execution through callbacks like #lazy_builder, #process, #pre_render, #post_render, #access_callback, and potentially other keys. For this example, #lazy_builder was used as it had the most flexible callback method. It's also worth noting that "shell_exec" is used in the example as "eval" is not a function, and as a result can not be called as a part of call_user_func().

Proposed resolution

We have to basically prevent render arrays from being written directly in any template or assigned to a variable in twig and then rendered.

To accomplish this we wrap each render array in an object (extending ArrayObject that implements \Drupal\Core\Render\RenderableInterface) and override methods that would mutate the render array (like setting the value at an index).

This allows us to also make the logic in \Drupal\Core\Template\TwigExtension more consistent, since these variables are now objects that implement RenderableInterface and are treated like any other object.

=======

Range of suggested resolutions that were considered:

  • Original suggestion by samuel.mortenson:

    Instead of filtering dangerous render arrays entered this way in Views, I would suggest blocking render_array and \Drupal\Core\Template\TwigNodeVisitor outright, in a way that other modules could emulate if they too wanted to allow end-users to enter Twig through the UI.

    The attached patch adds two new services, twig_strict and twig.strict_extension, which can be used to process Twig with a restricted set of components (functions, filters, etc.). As of right now the only known vulnerability is render_var, I have only restricted that function.

    That service, in turn, can be enabled by users of the "inline_template" element, by passing the option "#strict_mode => TRUE". In the patch I had Views turn this on by default for all Views Plugins that use advanced rendering.

  • A suggestion by pwolanin:

    We are possibly doing this since render arrays are not object that can be cast to string.

    It looks like we have to basically prevent render arrays from being written directly in any template or assigned to a variable in twig and then rendered.

    Can Twig distinguish variables passed in verses created inside the template? We could possibly wrap each render array in an object (like ArrayObject that implements \Drupal\Core\Render\RenderableInterface) but might need to make it immutable inside Twig which could break existing templates or parts of the render flow.

  • Another suggestion by pwolanin that samuel.mortenson proved was not sufficient:

    We could recursively flag all render arrays going into twig as variables by setting a value for a # property that could not be set by someone creating render array in inline twig or even in a twig template with set.

    From Samuel:

    Here's an example of working around this idea in Twig, assuming "safe_render_array" is passed to Twig and already marked as safe.

    {# Add malicious render array key to existing safe array #}
    {% set safe_render_array = safe_render_array|merge({'#lazy_builder': ["shell_exec", ["touch /tmp/hellofromviews"]}) %}
    {# Copy #_safe_to_render key from existing safe array to new array, to mock that it was already marked as safe #}
    {% set nasty_array = {"#lazy_builder": ["shell_exec", ["touch /tmp/hellofromviews"]], '#_safe_to_render': safe_render_array['#_safe_to_render']} %}
    

    A real world example may be more complex, but this should illustrate the problem with relying on an array key.

  • A further suggestion

    webform for 8.x (accidentally?) disables "#lazy_builder" by adding extra properties to all render arrays. Depending on what the valid use case is for that, maybe we could do the same in Twig.

Remaining tasks

Get feedback from people using Twig on how elements of render arrays are accessed and used.

Decide on a secure approach

Implement the fix

Fix failing tests

Add more test coverage

User interface changes

none

API changes

May be some BC-breaking changes to how render arrays may be accessed or modified in templates.

Data model changes

none


Viewing all articles
Browse latest Browse all 296658

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>