Laravel Debug模式下远程代码执行漏洞分析​

Laravel Debug模式下远程代码执行漏洞分析​

1、前言

2021年1月13日,百度云安全团队监测到有国外安全研究人员披露了Laravel远程代码执行漏洞详情,当Laravel<=8.4.2、ignition组件版本<=2.5.1并且开启了Debug模式情况下,攻击者可通过php://filter构造特定的日志文件,并触发Phar反序列化导致任意代码执行漏洞,漏洞危害较大,建议及时升级Laravel以及ignition组件到最新版本。

2、漏洞环境搭建

git clone github.com/laravel/lara #下载laravel源码

cd laravel

git checkout -b e849812 #切换到存在漏洞的分支

composer install #composer安装依赖

composer require facade/ignition==2.5.1 #下载存在漏洞版本组件

安装好后在根目录执行 mv .env.example .env,.env.example中默认设置了APP_DEBUG=true,此时启动apache2服务器即可。

为了复现原作者所述当变量未定义时出现的报错情况,需要在resource/views/ hello.blade.php中设置一个简单的模板。

并在routes/web.php中添加路由

访问/public/index.php/hello即出现变量未定义的报错信息。

3、漏洞分析

点击Make variable optionl并抓包,请求的接口为/_ignition/execute-solution

通过搜索路由,对应的处理类为ExecuteSolutionController,由于此处类对象被当做函数调用,因此会调用ExecuteSolutionController类的__invoke方法。

在__invoke中,先调用ExecuteSolutionRequest->getRunnableSolution()获取solution对象,随后调用solution对象的run方法。

但此时需要知道solution对象对应的类,因此跟进到getRunnableSolution方法,发现solution对象来自getSolution方法的返回,并且必须是RunnableSolution类的实例,也就是说solution对象的类必须是RunnableSolution类的子类。跟进getSolution方法,发现类来源于传递的solution参数,而solution的值为Facade\Ignition\Solutions\MakeView VariableOptionalSolution恰好继承于RunableSolution接口,满足上述条件。

跟进到MakeViewVariableOptionalSolution的run方法,可以看到$parameters参数被传递到makeOptional参数处理,并且viewFile参数对应的文件会被file_get_contents方法读取,并将$username替换为$username ? ‘’,并且由于后续会做Token化比对,因此没办法直接通过控制variableName参数做一些突破。

但由于viewFile可控,并且会经过file_get_contents方法处理,如果能够上传或者写入Phar文件,是可以利用phar://实现反序列化的。而此时我们唯一可控部分内容的文件是laravel的日志文件,默认目录是storage/logs/laravel.log。

此时就算能控制一部分日志内容,但可控内容前后有日期、堆栈信息等太多不可控内容,没办法直接写入Phar文件。这时候,漏洞原作者巧妙的利用了php://filter伪协议,该协议能够灵活地对文件内容进行多种编解码。

利用base64解码能够在有一部分干扰内容情况下仍然解码成功,但如果有=等字符则可干扰正常解码。此时作者想到了利用convert.iconv.utf-16le.utf-8,可以将其他干扰内容转换成非ascii,而非ascii字符再经过base64解码即可变成空。但utf-16le编码中会有一些空字符,传递到file_get_contents方法中会报错。因此还需要利用convert.quoted-printable-encode可以将空字符编码为=00,再使用与之相对的decode方法即可解决这个问题。

因此整体的调用链条如下:

ExecuteSolutionController->__invoke()

->ExecuteSolutionRequest->getRunnableSolution()->getSolution()

->MakeViewVariableOptionalSolution->run()

4、漏洞利用

在了解上述原理后,想要利用该漏洞需要有几个步骤:

1)清空laravel.log

设置viewfile=php://filter/write=convert.base64-decode|convert.base64-decode|convert.base6 4-decode/resource=/var/www/html/laravel/storage/logs/laravel.log

并多执行几次,利用base64解码失败输出为空的特性将日志置空。

2)向日志中插入特定的Payload

利用phpggc项目选择特定的反序列化链条生成Payload,并根据上述分析做三重对应的编码。

3)日志内容解码

利用convert.quoted-printable-decode、convert.iconv.utf-16le.utf-8、convert.base64-decode三种方式解码,确保日志中仅存Phar文件。

4)触发Phar反序列化

当Phar文件被部分文件系统函数处理时触发反序列化,此处利用Larvavel特定依赖monolog的反序列化利用链实现任意代码执行。

参考:ambionics.io/blog/larav

推荐阅读

发布于 2021-01-15 16:51