Don’t stop at alert(1): Demonstrate impact with low severity bugs

Evan Grant
Tenable TechBlog
Published in
4 min readJan 29, 2021

When trying to discover vulnerabilities in a web application, you may not always come across high or critical severity bugs, and only end up finding low-medium severity issues like cross-site scripting (XSS). When that is the case, it is worth seeing how far those bugs can take you, since low severity vulnerabilities can still have a large effect when leveraged as part of a more impactful attack chain.

Using Umbraco CMS as a test case, we will look at ways of turning a simple low severity post-auth stored XSS into a privilege escalation, and remote code execution, in order to demonstrate the potential impact of this low-severity bug a bit better than an alert(1) can.

Stored XSS to privilege escalation

Back in September we reported a number of issues in the Umbraco CMS cloud platform and Umbraco CMS itself. (See TRA-2020–59)

The example we will look at is a simple authenticated stored XSS vulnerability (CVE-2020–5809), exploitable by a low privileged user who has access to add/edit page content.

Specifically, a low privileged user on our example site can leave a stored XSS payload on any page they are able to edit by adding a malicious iframe using the TinyMCE rich-text editor.

Umbraco’s TinyMCE plugin, with tenable.com in an iframe
Umbraco’s TinyMCE plugin with tenable.com in an iframe

In order to demonstrate a potential attack with this XSS when reporting the issue to the vendor, we created the following payload which, if triggered by a victim admin, will add the attacker’s userid to the admin group, allowing them full control over the Umbraco CMS instance.

// grab xsrf token, use it in request header
// then send request to add attackerId to admin group
<iframe srcdoc="<script> var xhr = new XMLHttpRequest();
xhr.open('POST', '/umbraco/backoffice/UmbracoApi/Users/PostSetUserGroupsOnUsers?userGroupAliases=writer&userGroupAliases=admin&userIds=<attackerId>', true);
xhr.setRequestHeader('X-UMB-XSRF-TOKEN', document.cookie.match(/UMB-XSRF-TOKEN=[^;]+/g)[0].split('=')[1]);
xhr.send();</script>" width="0" height="0">
</iframe>

This is especially dangerous because Umbraco admins are able to install Umbraco packages/plugins, which could lead to code execution if the attacker were to install a malicious package.

In fact, let’s see if we can skip making ourselves admin, and just have the XSS install a malicious package for us.

Trick an admin in to popping calc

The first step is to figure out how to create a malicious Umbraco package which could execute arbitrary code when installed by an admin (or anyone with the required privileges). After downloading a number of packages and reading some Umbraco documentation, it became clear that we only need to create a zip archive containing 2 files:

  • A dll which implements the IPackageAction interface from Umbraco.Core.PackageActions.
  • A package.xml file which contains the necessary metadata and tells Umbraco to execute our package action on install.

We end up with a very simple class library which spawns calc.exe during the package installation. IPackageAction requires that we implement the Alias, Execute, and Undo interface members. Luckily, the implementations can be very minimal / bare bones.

The code for our package action, which will become popcalc.dll

For package.xml, there are a number of required metadata fields, but the important parts for our purposes are the files and actions sections, which tell Umbraco to copy popcalc.dll to the Umbraco bin directory, and execute the IPackageAction we just created. The respective sections look like this:

<files>
<file>
<guid>popcalc.dll</guid>
<orgPath>~/bin</orgPath>
<orgName>popcalc.dll</orgName>
</file>
</files>
<Actions>
<Action runat=”install” alias=”PopCalc” />
<Actions>

After zipping up the package.xml and popcalc.dll together, we need to create a javascript payload which performs the installation on behalf of the user who triggers the XSS.

Recording the traffic of a normal package installation in burp shows that we will need to make 5–6 separate requests to actually execute code (the requests make up a combination of uploading, importing and executing the package, and cleanup).

Additionally, we’ll need to turn our zipped package into a format we can work with in the payload. We could host it on an attacker-controlled server and grab it with a request, but in this case we converted the zip file to a large Uint8Array, which is then converted to a Blob object to be uploaded to Umbraco. The final javascript payload looks like this:

Not shown here (for clarity) is the declaration of pkg, a big uint8array containing our malicious package data

A low privileged user could post this in an article, and submit it to be approved by the admin or hope the admin happens across it. Around 10 seconds after the XSS is triggered, the code executes and calc is popped.

Test Scenario: An admin unknowingly triggers the XSS and pops calc

While this attack scenario is fairly limited (as it requires authentication, and for an admin to open the content in question), it is certainly possible and serves as a better demonstration of impact than a simple alert box.

Whether you’re trying to demonstrate impact to a vendor or bug bounty program to encourage a speedy fix, or you’re trying to leverage small bugs for big gains during a penetration test, it is always good practice to explore possible attack scenarios using any vulnerability you discover.

Find an interesting attack scenario, and turn lemons into lemonade.

Happy hacking!

--

--