Adobe Commerce (Magento) CVE-2022-24086 : Return Of The Text Interpolation

Adobe Commerce (Magento) CVE-2022-24086 : Return Of The Text Interpolation

In our latest blog post, we'll be discussing a vulnerability that - while not new - is shrouded in mystery. Ranging from bizarre and false PoCs circulating on Twitter, to incomplete analysis on dark corners of the web.

As part of our Attack Surface Management capabilities delivered through the watchTowr Platform, we conduct a thorough analysis of vulnerabilities in technology that are likely to be prevalent across our clients' attack surfaces. This allows us to quickly identify and validate vulnerable systems across large attack surfaces, enabling us to respond rapidly to potential threats.

The vulnerability we'll be discussing today has been formally identified as 'CVE-2022-24086', a vulnerability in Adobe Commerce (or as it is also known, Magento).

Regular readers might remember a previous post on the topic of the Text4Shell bug (and subsequent follow-up), where we talked about string interpolation and how it can be leveraged to achieve code execution. Today's post speaks of another problem caused, at the root, by the same thing - unsafe string interpolation.

CVE-2022-24086, is an "Improper Input Validation Vulnerability" in Adobe Commerce 2.4.3-p1, as advised by MITRE. If abused and exploited correctly, it allows remote code execution without any user interaction, and worse, Adobe advise that they are aware of in-the-wild exploitation. Clearly a pretty serious bug.

Adobe Commerce is a hugely popular e-commerce platform. While on the surface, it appears simple - a CMS with some added cart/checkout functionality - the truth is that it is a large, complex application, allowing advanced operations such as templating items based on properties, management of multiple sites, and a particularly comprehensive customisation and templating engine.

But How Does It Work?

This customisation engine allows almost everything imaginable, from changing text elements on the website all the way to writing custom plugins. To see how it works, we can simply take a look at some of the default pages, which use the engine extensively. I picked vendor/magento/theme-frontend-luma/Magento_Email/email/footer.html which contains a simple example:

<p class="address">
	{{var store.formatted_address|raw}}
</p>
A simple variable substitution

When we look at the webpage, we see the formatted store address. Magento has processed the line inside the curly braces, and produced some output.

While this is a simple example, the language used for this customisation is surprisingly complex (see the documentation) and allows for an almost limitless range of behaviour beyond simple text substitution. Typically, though, this functionality not exposed to end-users directly - rather, site staff use the templating engine to configure the store itself. For example, a salesperson could devise an email template to be sent to prospective buyers, and use variable substitution to insert the particulars of the recipient along with a product they may be interested in.

There are a few exceptions to this 'staff-only' rule, however, and this is where things start to get interesting, as they allow an unprivileged attacker to make use of the templating engine.

One such exception is in the 'Wishlist' module, which is designed to allow an end-user of the store to create a list, of items they wish for, which can then be shared via email. What makes it interesting to us is one seemingly-minor detail - the templating engine runs on the outgoing email before it is sent. Let's explore this functionality a little bit more.

But Why?

Firstly, we'll do a simple variable substitution. We create a wishlist on a Magento site, and then request that the wishlist is shared via email to an address we control. Since we can specify the body of the email we send, we'll be specifying the body to be hello {{var store.frontend_name}} world. Let's see what's actually sent in the email:

root@host:/# postcat -qb 85FC5CBD35| grep hello
m test test:</h3>=0A            hello Main Website Store world=0A      =20=

As any astute reader can see, the injected variable was substituted with the name of the store - "Main Website Store".

This confirms we've got access to the system templating engine as a non-administrative user. But what can we do with it? Well, maybe we could experiment and see if we could read out sensitive information such as database credentials. But why? Why limit ourselves? It turns out we can set our sights even higher than this, due to the (perhaps unintended?) power of the templating engine.

Arbitrary Code Execution

As you'll recall from previous mentions of string interpolation bugs, the 'smoking gun' that really enables useful exploitation is the ability to execute arbitrary commands from an interpolated value. This is at the core of any bug in this class, and CVE-2022-24086 is no different, although slightly more complicated to figure out.

The functionality hinges on two functions exposed to the templating engine - getTemplateFilter and addAfterFilterCallback. The first will allow us to get a reference to a 'filter' object, which is used to transform incoming data before processing, and the second allows us to attach a PHP function to it (!). This is intended to be used by plugin developers to sanitise and transform user input, deferring processing to PHP code either in the Magento application itself or an administrator-provided PHP-language plugin.

It's actually easier to demonstrate this than it is to explain it, so here's a quick example:

{{var this.getTemplateFilter().filter('test')}}{{var this.getTemplateFilter().addAfterFilterCallback(system).filter(touch${IFS}/tmp/test)}}
Note the use of the ${IFS} operator to avoid whitespace in the argument. 

This will obtain a reference filter named 'test', and then attach a callback to it, instructing it to execute the 'system' PHP command with the arguments 'touch /tmp/test'. Running this query yields evidence that our command has executed on the server:

root@host:/bitnami/magento# ls /tmp/test -l
-rw-rw-r-- 1 daemon daemon 0 Feb 26 21:07 /tmp/test

Good news for attackers, bad news for defenders.

In the real-wordl, this is much more nefarious - within the watchTowr Platform, we deliver a second-stage payload - and all the bad guys out there could be delivering reverse shells. Indeed, this is what we've seen when the bug is exploited in the wild.

It's worth noting that the 'wishlist' module is not the only module to expose the templating engine to untrusted users. We've noted that across our client base, and in attacks seen in-the-wild, exploitation is usually best aimed at the checkout process rather than the 'wishlist' module - perhaps since the wishlist might not be present on all installations?

Fingerprinting EOL'ed installations

For many people, this is the end of the story - the bug has been analysed, a PoC has been created, and we have a good understanding of what's going on. But for us here at watchTowr, the story is just beginning - we secure our clients and detect exploitable deployments of Adobe Commerce before they are exploited, which means we must detect at scale as rapidly as possible.

While we are not here to arm everyone, there are trivially identifiable exploitable instances, such as those running End-Of-Life versions of Magneto. Since no patch is available, these installations are almost certainly vulnerable.

As a demonstration, let's take a look at some hosts on the Internet and see how we can detect these EOL'ed versions, and also look at the kind of version distribution we see.

We start off with a quick Shodan search for the X-Magento-Vary header, which Magento helpfully emits on every HTTP response. This search detects 12,500 Magento installs, and to keep things manageable, we'll confine our work to the first 1000.

The obvious solution is to observe the Magneto version number, but Magneto makes this very difficult to do. Rather than reveal the full version of the software (such as 1.2.3p4), Magneto exposes only the major and minor versions. This is done via the /Magneto_version endpoint. While this is not a high level of detail, it allows us to quickly detect instances running 2.3 and below (at the time of writing, anything below 2.4.4 is EOL).

Note that since this is a single HTTP response, it is very easy for us to scale (for example, via a Nuclei template).

Running this on our 1000 hosts gives the following breakdown:

  • Magneto/2.1: 12 (1.2%)
  • Magneto/2.2: 40 (4%)
  • Magneto/2.3: 212 (21%)
  • Magneto/2.4: 302 (30%)
  • Other: 566 (56%)

Our single request has quickly determined that a whopping 25% of hosts are running an outdated version of Magneto and require further attention.

The remaining 75% of installations aren't necessarily safe, however. As I mentioned, versions below 2.4.4 are (at the time of writing) EOL. A number of hosts running these versions may be hiding in the 30% of hosts which report "Magneto/2.4".

Magneto is very averse to revealing this information, however, and so we are forced to turn to more 'sneaky tricks' - in this case, fingerprinting static resources. After some investigation, we found that one change introduced in 2.4.4 is the upgrade of the tinymce package, from version 4 to version 5. This seemingly-unrelated change is actually very easy to detect - we can simply requested the tinymce static resource ( /static/adminhtml/Magento/backend/en_US/tiny_mce_5/tinymce.min.js) and if it is not present, we know that the host is running a version lower than v2.4.4, and is thus EOL'd. Let's take a look at how these versions are present on our 1000-host sample:

  • tinymce 4 present (probably < 2.4.4): 415 hosts (41%)
  • tinymce 5 present (probably <= 2.4.4): 230 hosts (23%)
  • None present (indeterminate): 355 (35%)

This has found a further 41% of our hosts which are running EOL'd software and are thus almost certainly of importance to the owners.

Conclusion

This kind of 'interpolation' bug is interesting from a technical standpoint, but is also often difficult to detect in real-world codebases. This instance is particularly damaging due to its pre-auth exploitability, and the huge installation base of Adobe Commerce - combined with the the valuable data typically held

We took a look at the bug itself, showing how it can be exploited (and, by extension, how you can confirm if your hosts are vulnerable). While we used the 'Whislist' functionality as an attack vector, in-the-wild attacks use the more generic 'Checkout' functionality.

Finally, we've also demonstrated how to perform a fast sweep of the public Internet, and found that around a whopping 40% of hosts are running outdated software. Hopefully, they are honeypots and not production instances!

At watchTowr, we believe continuous security testing is the future, enabling the rapid identification of holistic high-impact vulnerabilities that affect your organisation.

If you'd like to learn more about the watchTowr Platform, our Continuous Automated Red Teaming and Attack Surface Management solution, please get in touch.