Hello again, it's been a while since we've last had the pleasure of deep diving into open source projects and audit them for vulnerabilities. Prior to this one, we examined Plone, where we've discovered an authenticated RCE vulnerability, which was assigned CVE-2021-32633. You can find the write-up for it over here.

How it all started

Back in May 2021, previously published vulnerabilities for OctoberCMS sparked our interest and motiviated us to take a closer look at the project with the goal of finding security issues.

A series of security advisories (CVE-2020-15247, CVE-2020-262231), reported by ka1n4t, were our initial starting point. The advisories outlined that OctoberCMS may allow the execution of arbitrary PHP code by bypassing Twig's sandbox and "Safe Mode" restriction:

A bypass of CVE-2020-15247 (fixed in 1.0.469 and 1.1.0) was discovered that has the same impact as CVE-2020-15247:

An authenticated backend user with the cms.manage_pages, cms.manage_layouts, or cms.manage_partials permissions who would normally not be permitted to provide PHP code to be executed by the CMS due to cms.enableSafeMode being enabled is able to write specific Twig code to escape the Twig sandbox and execute arbitrary PHP.

The advisory further mentions that...

This is not a problem for anyone that trusts their users with those permissions to normally write & manage PHP within the CMS by not having cms.enableSafeMode enabled, but would be a problem for anyone relying on cms.enableSafeMode to ensure that users with those permissions in production do not have access to write & execute arbitrary PHP.

In essence, users have the ability to edit template or page contents with access to a sandboxed Twig environment via the "Markup" tab in the page editor. The "Code" tab accepts executable PHP code, but is restricted by the Safe Mode configuration option:

image

The Attack Surface

Having these protection mechanisms in mind, we set out to analyze the restrictions put in place for an authenticated user with access to the "Markup" editor tab and "Safe Mode" enabled - the same exact attack scenario as in the above mentioned advisories.

Our main point of concern was the way the two vulnerabilities were resolved. The SecurityPolicy, which was introduced in the first patch in order to resolve CVE-2020-15247, and was later updated to fix CVE-2020-262231, raised our suspicions.

The SecurityPolicy implemented by OctoberCMS is based on a blocklist approach in order to restrict access to certain methods and properties in the sandboxed Twig environment. After CVE-2020-15247 and CVE-2020-262231 were resolved, the SecurityPolicy was updated, restricting access to seven methods:

<?php
...
final class SecurityPolicy implements SecurityPolicyInterface
{
    /**
    * @var array List of forbidden methods.
    */
    protected $blockedMethods = [
        // \October\Rain\Extension\ExtendableTrait
        'addDynamicMethod',
        'addDynamicProperty',

        // \October\Rain\Support\Traits\Emitter
        'bindEvent',
        'bindEventOnce',

        // Eloquent & Halcyon data modification
        'insert',
        'update',
        'delete',
    ];

    ...

Due to the small number of methods that have been restricted with this policy, we suspected that alternative ways of exploiting this attack scenario may exist.

Enter The Sandbox

We've started digging around the Twig context in order to discover what was available to us in the restricted environment. To make our lives easier, we've modified OctoberCMS to load Twig's debug extension (even when OctoberCMS is not running in debug mode) so we could use Twig's dump function. Invoking dump() without parameters dumps an array of available variables in Twig's _context, which looked like so:

image

Iterating over the elements of this and dumping them one at a time, revealed a list of potential methods, which we could call in the restricted Twig environment. Here's a short excerpt of those classes:

We carefully analyzed most of these classes to determine if any of the exposed methods could be abused to achieve our goal of executing arbitrary code.

After digging through the Page, Layout and Theme classes, we've discovered that the Controller class exposes the getTwig() method. It returns a reference to the Twig\Environment, which in turn exposes the registerUndefinedFilterCallback($callable) method - a method you do not want to be accessible to untrusted users.

The registerUndefinedFilterCallback() method can be used to register a malicious callback function (exec, passthru, system, etc.) which once registered can be invoked by calling an undefined filter.

Exploitation

In order to verify the exploitability via this.controller.getTwig(), we've created a new page containing the following markup:

image

A malicious callback is registered via a call to registerUndefinedFilterCallback() and then triggered via the call to getFilter(). The call to getFilter() attempts to lookup the undefined 'id' filter which in turn passes it to the undefined filter callback passthru.

The combination of these calls boils down to invoking passthru('id'), as can be verified by saving and then previewing the just created page:

image

Our suspicions were confirmed, the previous patches to prevent the execution of code by untrusted users via Twig were not sufficient. Arbitrary code execution was achieved by successfully bypassing Twig's sandbox.

Conclusion

After reporting our finding to OctoberCMS a patch was pushed which would simply restrict access to the getTwig() method on this.controller, as well as the write method (yet another way of bypassing the sandbox).

The patch can be found here: 167b592

    protected $blockedMethods = [
-       // \October\Rain\Extension\ExtendableTrait
+       // Prevent manipulating Twig itself
+       'getTwig',

+       // Prevent dynamic methods and props
        'addDynamicMethod',
        'addDynamicProperty',

-       // \October\Rain\Support\Traits\Emitter
+       // Prevent binding event logic
        'bindEvent',
        'bindEventOnce',

        // Eloquent & Halcyon data modification
        'insert',
        'update',
        'delete',
+       'write',
    ];

We were still not convinced that simply updating the blocklist (SecurityPolicy) with a new entry would prevent similar bypass attempts in the future, nor did we know for sure how these issues will be handled in newer versions of OctoberCMS.

This issue affected OctoberCMS prior to v1.0.473 and v1.1.6 and was assigned CVE-2021-32649.

Timeline

  • 2021-05: Vulnerability discovered and reported
  • 2021-05: Vendor acknowledges vulnerability, CVE requested
  • 2021-05-12: CVE-2021-32649 reserved
  • 2021-05-29: Vendor releases patch
  • 2022-01-14: CVE-2021-32649 published
  • 2022-02-25: Write-up released