MyBB Remote Code Execution Chain

by simon scannell carl smith|

MyBB Remote Code Execution

Today SonarSource is pleased to share with you a guest contribution to our Code Security blog series. The following blog post is authored by Simon Scannell and Carl Smith -two independent security researchers- joining us in sharing their findings of real world vulnerabilities and how they directly relate to Code Security. Over to you Simon and Carl!

Like all IT security enthusiasts, we love to grow our knowledge by looking through a variety of applications, and taking up some contests such as playing capture the flag. Lately, we decided to look at forum software to create a CTF challenge and detected a chain of serious vulnerabilities in MyBB, one of the most popular open source bulletin boards. In a followup to SonarSource’s recent series of posts about the challenges of regular expressions, we would like to share our code vulnerability findings in MyBB that are based on defective regexes.

Impact

MyBB forums with versions between and including 1.8.16 and 1.8.25 are affected by two vulnerabilities we discovered that can be chained together to achieve Remote Code Execution (RCE) without any prior access to a privileged account on default MyBB-configurations. The first vulnerability (Nested Auto URL persistent XSS — CVE-2021-27889) reported by us was an issue in the MyBB rendering process that enabled any unprivileged forum user of a MyBB board to embed Stored XSS payloads into threads, posts and even private messages. 

The second vulnerability (Theme properties SQL injection — CVE-2021-27890) that was reported by us was an SQL injection which led to RCE and could be triggered by any user with an active session in the administrator dashboard of a MyBB forum. 

A sophisticated attacker could develop an exploit for the Stored XSS vulnerability and then send a private message to a targeted administrator of a MyBB board. As soon as the administrator opens the private message, on his own trusted forum, the exploit triggers. An RCE vulnerability is automatically exploited in the background and leads to a full takeover of the targeted MyBB forum.

Technical Details

Persistent XSS in Nested Auto URL (CVE-2021-27889)

Modern forum software such as MyBB commonly enables unprivileged users to create posts or private messages containing images, videos, headlines, lists and so on. 

This feature must be carefully implemented, as untrusted users could abuse it to modify the contents of the forum in undesired ways if the constraints of this feature are not strict enough. Worst case, a user could gain the ability to inject arbitrary JavaScript code into the HTML documents served by the trusted forum.

In our experience, we have observed two approaches to implement this feature:

  1. Allow users to submit HTML tags and apply an allow or deny list to determine whether the input is sane and safe to display to other users.
  2. Use an existing or custom message format, such as Markdown for example, to create sane HTML outputs from inputs.

Both approaches come with their respective advantages and disadvantages. MyBB utilizes the second approach in their rendering process with a custom implementation of their MyCodes.

Here are 2 examples of how such a MyCode would look like and how it is converted:

[url]https://blog.sonarsource.com[/url]
 = <a href="https://blog.sonarsource.com">https://blog.sonarsource.com</a>
[b]Hello, World![/b] 
 = <strong>Hello, World!</strong>

Whenever a user creates for example a private message containing such codes, the MyBB parser encodes the entire input and then utilizes a regex to find and replace all MyCodes with their respective HTML code.

Problems in such parsers can occur when the regex patterns used to find and replace e.g. MyCodes are too relaxed which could lead to nested HTML tags being rendered, as is the case with the XSS we are breaking down here.

Another, less explicit, step of the MyBB rendering process is to automatically detect URLs which were not wrapped with the [URL] MyCode and to convert them into HTML links. The following snippet shows how the $message variable is passed to the mycode_auto_url() method of the renderer class:

mybb/inc/class_parser.php

 525         if($mybb->settings['allowautourl'] == 1)
 526         {
 527             $message = $this->mycode_auto_url($message);
 528         }
 529 
 530         return $message;

The $message variable in line 527 contains the already rendered HTML result of the user supplied message and thus must be carefully handled so that no HTML tags or attributes could be corrupted. The condition for this is that only URLs that are not part of an HTML tag are allowed to be converted into <a> tags.

MyBB utilized the following regex to try to securely parse only URLs that are not part of an HTML tag (the regex was simplified by stripping away all parts that were not relevant for understanding this vulnerability):

mybb/inc/class_parser.php

1618   $message = preg_replace_callback(
         "#<a\\s[^>]*>.*?</a>|([\s\(\)\[\>])(www|ftp)\.([\w|\d\-./]+)#ius", 
          array($this, 'mycode_auto_url_callback'), 
          $message);

The regex is divided by the | character into two alternatives:

  1. Match anything between <a> tags (<a\\s[^>]*>.*?</a>). In case of such a match, nothing is changed
  2. The URL match must begin with either a whitespace, parentheses or an opening square bracket ([) or a closing angle bracket (>) and is then followed by a URL (this is the simplified part). The logic here is that when a user-supplied MyCode is converted into HTML, although user controlled data can be embedded into an HTML attribute of the resulting HTML tag, it cannot contain any of these characters. 

As an example, when an [img] MyCode is converted to HTML, it could look like this:

[img]http://xyzsomething.com/image.png[/img]
 = <img src="http://xyzsomething.com/image.png" />

When such an image tag is constructed, the URL that is going to form the src attribute is stripped of all whitespaces and is HTML and URL encoded. The idea was that these transformations would remove all characters that could be matched by the second alternative of the regex which is used for the auto URL encode. Therefore the second part assumes that the first transformation has already cleaned the URL.

However, both URL encoding and HTML encoding do not modify parentheses (). Thus, it was possible to craft an [img] tag such as the one shown below, which invalidates this assumption thus confusing the second regex.

[img]http://xyzsomething.com/image?)http://x.com/onerror=alert(1);//[/img]

In the first step of the rendering process, the following <img> tag would have been created:

<img src="http://xyzsomething.com/image?)http://x.com/onerror=alert(1);//">

In the next step, the mycode_auto_url() method matches the second URL in the image as it begins with a closing parenthesis which has not been escaped or encoded. The final HTML that is created then looks like this:

<img src="http://xyzsomething.com/image?)<a href=" http:="" x.com="" 
 onerror="alert(1);//&quot;" target="_blank" rel="noopener" class="mycode_url">

As you can see, an <a> tag has been inserted into the existing <img> tag. Since both of these tags contain double quotes, they corrupt each other. Browsers such as Chrome or FireFox are going to construct a final <img> element containing an attacker controlled onerror event handler. This allows the attacker to execute arbitrary JavaScript code in the browser of a victim who reads the malicious post or private message.

SQL Injection in Theme Properties leading to RCE

The XSS vulnerability described in the previous section enables an attacker to target administrators of a MyBB forum. If the attacker succeeds in injecting malicious JavaScript code into the browser of an administrative user with an active session, he can perform arbitrary actions with admin privileges. MyBB actively prevents even administrator users from executing arbitrary PHP code on the underlying server, thus we will present an authenticated RCE vulnerability that can be reached with administrative privileges.

One of the features MyBB admins can access, is the theme manager of a MyBB forum. A MyBB theme consists of a list of key-value pairs. The key is a component of the current page, for example a welcome back message that should be displayed.

Here is an example of how MyBB then displays such a component:

eval('$modcplink = "'.$templates->get('header_welcomeblock_member_moderator').'";');

In above's example, the theme key header_welcomeblock_member_moderator is requested. The value of this theme component could then look like the following:

<div id='welcomeblock_back'><b>{$mybb->user['username']}</b></div>

This means that the final string that is passed to eval() would look like this:

$modcplink = "<div id='welcomeblock_back'><b>{$mybb->user['username']}</b></div>";

As you can see, the contents are enclosed in double quotes and the PHP variable {$mybb->user['username']} is interpolated into the string. The reason that this feature does not enable remote code execution (RCE) immediately is that MyBB escapes double quotes in template values when they are stored into the database. Thus, it is impossible to break out of the double quoted string. Another PHP trick exists, that could have resulted in RCE, which is that an attacker could have modified the template and added a $ to the variable that becomes string interpolated, like the following:

$modcplink = "<div id='welcomeblock_back'><b>${arbitrary_function()}</b></div>";

However, MyBB also prevents this unique PHP quirk by preventing administrators from inserting such interpolations. This meant that if we could find a bypass for MyBB’s filter, we could still execute arbitrary PHP code. We achieved this bypass through an SQL injection.

MyBB themes can be imported through XML files which contain a set of theme properties such as the image directory or the version. Additionally, a list of key value pairs is read where the name correlates to the key of the theme component and the value to the contents. Here is an example:

<?xml version="1.0" encoding="UTF-8"?>
<theme name="Theme Example" version="1405">
   <properties>
      <templateset><![CDATA[10]]></templateset>
      <imgdir><![CDATA[images/]]></imgdir>
      <logo><![CDATA[images/logo.png]]></logo>
   </properties>
   <stylesheets></stylesheets>
   <templates>
      <template name="header_welcomeblock_member_moderator" version="1404"><![CDATA[
         <div id='welcomeblock_back'><b>{$mybb->user['username']}</b></div>
      ]]></template>
   </templates>
</theme>

Whenever an administrator imports such a theme, the XML is parsed and the properties of the theme are stored into the database. As it turned out, the templateset property was susceptible to a second order SQL injection. 

When these themes are uploaded they are inserted into the database of the MyBB instance and are later used in other SQL queries without any sanitization.

We already touched on how the values of MyBB template components are passed to eval() calls, thus leading to arbitrary PHP code execution should an attacker be able to control the value of a theme property. The following paragraphs describe an SQL injection, which enables an attacker to inject malicious template codes into eval() calls.

At the beginning of each page load, MyBB fetches all possible template values from the database and stores them in a cache. The SQL query that fetches all template values, uses the templateset property, which is embedded unsanitized into the query string. Therefore an SQL injection vulnerability occurs:

$query = $db->simple_select("templates", "title,template",
    "title IN (''$sql) AND sid IN ('-2','-1','".$theme['templateset']."')",
    array('order_by' => 'sid', 'order_dir' => 'asc')

As shown, the query simply interposes the templateset attribute. With a malicious theme, one can control this attribute and let this cache function return attacker controlled values. Here is an example of such a crafted theme with a SQL injection payload:

<?xml version="1.0" encoding="UTF-8"?>
<theme name="Default" version="1821">
   <properties>
      <templateset>') AND 1=0 UNION SELECT title, '${passthru(\'ls\')}' from mybb_templates -- </templateset>
   </properties>
</theme>

The resulting SQL query looks like the following:

SELECT title, template FROM mybb_templates WHERE 
   title IN (‘header_welcomeblock_member_moderator’, ‘...’) AND SID IN (‘-2’, ‘-1’, ‘’) 
   AND 1=0 UNION SELECT title, '${passthru(\'ls\')}' from mybb_templates -- ’)

Through this SQL injection, it is possible to poison the template cache with attacker controlled values, which do not undergo any escaping or sanitization. As these template values that are now completely attacker controlled are passed to eval inside the double quoted string we can execute arbitrary PHP code within the ${...} syntax. As a result, an attacker can execute arbitrary PHP code and compromise the underlying server.

Timeline

DateEvent
22.02.2021Both vulnerabilities were reported to the MyBB team
22.02.2021The MyBB team acknowledges both vulnerabilities
04.03.2021The MyBB team proposes patches
05.03.2021We confirm the patches
10.03.2021MyBB releases patch version 1.8.26

Summary

Multi-step parsers and HTML renderers can always run into the risk of corrupting their own output, sometimes with security implications. We disclosed similar vulnerabilities in the past to WordPress and Magento. It is advisable to not let regexes grow too complex in these parsers, especially when they are meant to prevent security issues. We hope that the SonarSource community can learn from these mistakes to develop secure code and we enjoyed sharing our findings as a guest blog post at SonarSource. Last but not least, we would like to thank the MyBB team for their great cooperation on resolving these issues very quickly.