Write-up: OctoberCMS Authenticated RCE (CVE-2022-21705)

Join us in the discovery and exploitation of an authenticated remote code execution vulnerability in OctoberCMS

Welcome back, as you may recall we had the opportunity of looking at OctoberCMS back in May 2021, where we found an authenticated remote code execution vulnerability (CVE-2021-32649), which we’ve written about here.

This write-up covers another authenticated remote code execution vulnerability, which we’ve looked into in December 2021.

Revisiting OctoberCMS

The page editor of OctoberCMS allows users to manage template and page contents. The following editor tabs are available, both of which are protected with security mechanisms:

  • Markup Tab: Protected by a sandboxed Twig environment
  • Code Tab: Protected by the “Safe Mode” configuration option

As outlined in our previous post, these mechanisms should prevent untrusted users from executing arbitrary PHP via the page editor.

This write-up dives deeper into the way the page content is structured and how a malicious user, having access to the “Markup” editor tab, could still bypass the security measures in place, in order to execute arbitrary code.

This time around, instead of trying to bypass Twig’s sandbox, as we did in the last finding, we were interested in achieving arbitrary code execution through the “Markup” tab without relying on Twig.

How pages are stored

We’ve set out to further analyze how the separation of “Markup” and “Code” is handled in the backend and how OctoberCMS stores page contents on disk. For this purpose, we set up a local OctoberCMS lab running version 1.0.473.

The first page, which did not have content in the “Code” tab looks like this:

title = "page_without_code"
url = "/page_without_code"
description = "description_field"
meta_title = "meta_title"
meta_description = "meta_description"
is_hidden = 0

The second page, which did include content in the “Code” tab looked like that:

title = "page_with_code"
url = "/page_with_code"
description = "description_field"
meta_title = "meta_title"
meta_description = "meta_description"
is_hidden = 0

After comparing these two files and hunting down the callchain, from the point when a page is first saved until it reaches the actual file write call, we learned that our POST data is restructured into three sections via a call to SectionParser::render.

The sections written to disk are separated by double equal signs == and are split up like so:

  1. Settings: stores page metadata (title, URL, description, etc.)
  2. Code: stores the contents of the “Code” tab
  3. Markup: stores the contents of the “Markup” tab

After having learned more about the way pages are structured and how the access to the “Code” field is restricted when “Safe Mode” is enabled, we looked for ways to smuggle our code into the right section.

Experimenting with Sections

We asked ourselves - what if we just made our own code section, does the SectionParser, or any other component responsible for writing or updating the page content, prevent against this?

We reconfiguring OctoberCMS and re-enabled “Safe Mode”, which now restricts access to the “Code” section:

Safe Mode enabled

We then began modifying the page to include a fake code section before our actual markup section was appended. We ended up using the following test payload:

// I can haz code section?
markup_content, nothing to see here.

After the page was updated and the code editor reloaded, the “Code” tab now included our injected code:

Populated code section

After our initial testing, we created one final page and populated the “Markup” tab with the following contents:


function onInit() {
Hello World.

Saving and previewing this page ended up triggering our injected payload:

Payload triggered


After discovering this flaw we contacted OctoberCMS and disclosed the vulnerability, which soon after was assigned CVE-2022-21705. The vulnerability has since been patched and affected versions prior to 1.0.474, 1.1.10 and 2.1.27 of OctoberCMS.


  • 2021-12-24: Vulnerability discovered
  • 2022-01-18: Vulnerability reported to vendor
  • 2022-01-19: Vendor confirmed receipt of email
  • 2022-01-21: Vendor confirming the vulnerability, patch released, CVE assigned
  • 2022-03-04: Write-up released