PHP伪协议总结
前一篇对SSRF的总结,实际上里面也是涉及到一些协议的,这里也在自己之前学习PHP伪协议时的笔记基础上进行扩充,对PHP伪协议进行一下总结。
PHP 支持的伪协议
php伪协议,事实上是其支持的协议与封装协议。而其支持的协议有:
php:// — 访问各个输入/输出流(I/O streams) |
php:// 协议
使用条件:
不需要开启`allow_url_fopen` |
php://
访问各个输入/输出流I/O streams)
,在CTF中经常使用的是php://filter
和php://input
,php://filter
用于读取源码,php://input
用于执行php
代码。
下面对其进行详细介绍:
php://filter
CTF
中常用的伪协议,可以用来读取文件,是一种元封装器,设计用于数据流打开时的筛选过滤应用。其中一种最典型的利用方式如下:
index.php?file=php://filter/read=convert.base64-encode/resource=index.php |
上述代码意为使用base64
编码的形式将index.php
读取出来。对于上述利用方式具体每部分的含义,可以参照下图
在这里,涉及到一个过滤器的概念,PHP
过滤器用于验证和过滤来自非安全来源的数据,比如用户的输入。在这里之所以可以使用过滤器读取文件,相当于是将文件作为过滤器输入,获取其经过处理之后的数据流,而在PHP
中,过滤器有很多种,分别为:
字符串过滤器
string.rot13 |
- 示例-string.rot13
<?php |
- 示例-string.toupper
|
- 示例-string.tolower
<?php |
转换过滤器
如同string.*
过滤器,convert.*
过滤器的作用就和其名字一样。转换过滤器是PHP 5.0.0
添加的。
以常用的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.base64-encode & convert.base64-decode
<?php |
除此之外,还有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
参数作为从编码载荷中剥离的类型提示。
- 示例-convert.quoted-printable-encode & convert.quoted-printable-decode
|
压缩过滤器
正如名字所提到的,其作用也是类似,虽然在PHP
伪协议中,有压缩封装协议(zlib://, bzip2://, zip://)
,提供了在本地文件系统中 创建 gzip 和 bz2 兼容文件的方法,但不代表可以在网络的流中提供通用压缩的意思,也不代表可以将一个非压缩的流转换成一个压缩流。对此,压缩过滤器可以在任何时候应用于任何流资源。
另外,需要注意的是,压缩过滤器不产生命令行工具如gzip
的头和尾信息。只是压缩和解压数据流中的有效载荷部分。其中主要有zlib.* , bzip2.*
两类压缩过滤器:
zlib.deflate
(压缩)和zlib.inflate
(解压)实现了RFC 1951
中的的压缩算法。 zlib.*
压缩过滤器自 PHP
版本 5.1.0
起可用,在激活 zlib
的前提下。也可以通过安装来自PECL
的zlib_filter
包作为一个后门在5.0.x
版中使用。此过滤器在PHP 4
中不可用。
其中,deflate过滤器最多可以接受三个参数。分别为:
-
level
定义了压缩强度(1-9)。数字更高通常会产生更小的载荷,但要消耗更多的处理时间。存在两个特殊压缩等级:0(完全不压缩)和 -1(zlib 内部默认值,目前是 6)。 -
window
压缩回溯窗口大小,以二的次方表示。更高的值(大到 15 —— 32768 字节)产生更好的压缩效果但消耗更多内存,低的值(低到 9 —— 512 字节)产生产生较差的压缩效果但内存消耗低。目前默认的 window大小是 15。 -
memory
用来指示要分配多少工作内存。合法的数值范围是从 1(最小分配)到 9(最大分配)。内存分配仅影响速度,不会影响生成的载荷的大小。
下面为该压缩过滤器的示例:
- 示例-zlib.deflate和 zlib.inflate
|
- 示例-zlib.deflate简单参数用法
|
bzip2.compress
和bzip2.decompress
工作的方式与上面讲的zlib.*
过滤器相同。 自PHP 5.1.0
起可用,在激活 bz2
支持的前提下。也可以通过安装来自PECL
的bz2_filter
包作为一个后门在5.0.x
版中使用。此过滤器在PHP 4
中 不可用。
bzip2.compress
过滤器接受最多两个参数:
blocks
从 1 到 9 的整数值,指定分配多少个 100K 字节的内存块作为工作区。work
0 到 250 的整数值,指定在退回到一个慢一些,但更可靠的算法之前做多少次常规压缩算法的尝试。调整此参数仅影响到速度,压缩输出和内存使用都不受此设置的影响。将此参数设为 0 指示 bzip 库使用内部默认算法。
bzip2.decompress
过滤器仅接受一个参数,可以用普通的布尔值传递,或者用一个关联数组中的small
单元传递。当small
设为&true
; 值时,指示bzip
库用最小的内存占用来执行解压缩,代价是速度会慢一些。
下面为其使用示例:
- 示例-bzip2.compress和 bzip2.decompress
|
加密过滤器
加密过滤器为mcrypt.*
和mdecrypt.*
,使用libmcrypt
提供了对称的加密和解密。这两组过滤器都支持mcrypt
扩展库中相同的算法,格式为mcrypt.ciphername
,其中ciphername
是密码的名字,将被传递给mcrypt_module_open()
。有以下五个过滤器参数可用:
- 示例-用 3DES 将文件加密输出
|
- 示例-读取加密的文件
|
php://input
php://input是个可以访问请求的原始数据的只读流,将post请求中的数据作为PHP代码执行。当传进去的参数作为文件名变量去打开文件时,可以将参数php://input,同时post方式传进去值作为文件内容,供php代码执行时当做文件内容读取
利用条件:
allow_url_include = On |
利用姿势:
index.php?file=php://input |
需要注意的是,在PHP 5.6
之前php://input
打开的数据流只能读取一次; 数据流不支持seek
操作。 不过,依赖于SAPI
的实现,请求体数据被保存的时候, 它可以打开另一个php://input
数据流并重新读取。 通常情况下,这种情况只是针对 POST 请求,而不是其他请求方式,比如PUT
或者PROPFIND
。
在php://
伪协议中,除了上述两种在CTF
中常用的之外,还有一些其他的,比如下面这些:
- php://output 一个只写的数据流, 允许以
print
和echo
一样的方式 写入到输出缓冲区。 - php://fd 允许直接访问指定的文件描述符。 例如
php://fd/3
引用了文件描述符3
。 - php://memory/php://temp 一个类似文件包装器的数据流,允许读写临时数据。
- php://stdin/php://stdout/php://stderr 允许直接访问 PHP 进程相应的输入或者输出流
http(s):// 协议
用以访问HTTP(s)
网址,允许通过HTTP 1.0
的GET
方法,以只读访问文件或资源。 HTTP
请求会附带一个Host:
头,用于兼容基于域名的虚拟主机。 如果在php.ini
文件中或字节流上下文(context)
配置了user_agent
字符串,它也会被包含在请求之中。使用需要满足以下条件:
allow_url_fopen:on |
用法:
http://example.com |
- 示例-检测重定向后最终的 URL
|
ftp(s):// 协议
用以访问FTP(s) URLs
,允许通过FTP
读取存在的文件,以及创建新文件。 如果服务器不支持被动(passive)
模式的FTP
,连接会失败。
打开文件后你既可以读也可以写,但是不能同时进行。 当远程文件已经存在于ftp
服务器上,如果尝试打开并写入文件的时候, 未指定上下文(context)
选项overwrite
,连接会失败。 如果要通过FTP
覆盖存在的文件, 指定上下文(context)
的overwrite
选项来打开、写入。 另外可使用FTP
扩展来代替。
需要注意的是:如果设置了php.ini
中的from
指令,这个值会作为匿名(anonymous)ftp
的密码。
zlib:// & bzip2:// & zip:/ 协议
php
伪协议中的压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名,可修改为任意后缀(jpg png gif xxx)
等等。其使用条件为:
allow_url_fopen:off/on |
- 示例-使用
zip://
压缩phpinfo.txt
为phpinfo.zip
,压缩包重命名为phpinfo.jpg
,并上传
http://127.0.0.1/include.php?file=zip://home/test/WWW/phpinfo.jpg%23phpinfo.txt |
- 示例-使用
compress.bzip2://file.bz2
压缩phpinfo.txt
为phpinfo.bz2
并上传(同样支持任意后缀名)
http://127.0.0.1/include.php?file=compress.bzip2://home/test/WWW/phpinfo.bz2 |
- 示例-使用
compress.zlib://file.gz
压缩phpinfo.txt
为phpinfo.gz
并上传(同样支持任意后缀名)
http://127.0.0.1/include.php?file=compress.zlib://home/test/WWW/phpinfo.gz |
data:// 协议
自PHP>=5.2.0
起,可以使用data://
数据流封装器,以传递相应格式的数据。通常可以用来执行PHP
代码。使用条件为:
allow_url_fopen:on |
可以支持明文或编码,用法为:
data://text/plain,XXX |
- 示例-使用明文方式读取
phpinfo()
http://127.0.0.1/include.php?file=data://text/plain, php%20phpinfo(); |
- 示例-使用
base64
方式(CTF中可用以绕过waf)读取phpinfo()
http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b |
- 示例-打印
data://
的内容
<?php |
glob:// 协议
查找匹配的文件路径模式, 自PHP 5.3.0
起开始有效。下面为其基本用法:
<?php |
phar:// 协议
用以PHP归档
,数据流包装器自PHP 5.3.0
起开始有效,与zip://
类似,同样可以访问zip
格式压缩包内容,比如下面这个例子:
http://127.0.0.1/include.php?file=phar://home/test/WWW/phpinfo.zip/phpinfo.txt |
该伪协议在CTF
中比较常见,主要用于反序列化和文件包含,此文中只对其用于文件包含进行介绍,反序列化在后面将会单独拿出来讲。在文件包含中,该协议主要用于支持zip、phar
格式的文件包含,用法如下:
?file=phar://[压缩包文件相对路径]/[压缩文件内的子文件名] |
- 示例-配合文件上传漏洞,当仅可以上传zip格式时
index.php?file=phar://index.zip/index.txt |
- 示例-配合文件上传漏洞,当仅可以上传图片格式时,
phar://
不管后缀是什么,都会当做压缩包来解压。
index.php?file=phar://head.png/head.txt |
file:// 协议
用于访问本地文件系统,可以使用相对路径或绝对路径来访问文件系统文件,其使用样例为:
/path/to/file.ext |
该伪协议在CTF中通常用来读取本地文件,因为其在双off
的情况下也可以正常使用,不受allow_url_fopen
与allow_url_include
的影响。
- 示例-使用文件的相对路径和文件名
http://127.0.0.1/include.php?file=./phpinfo.txt |
- 示例-使用文件的绝对路径和文件名
http://127.0.0.1/include.php?file=file://home/test/WWW/phpinfo.txt |
ssh2:// 协议
Secure Shell 2
,默认没有激活,如果需要使用ssh2.*://
封装协议,必须安装来自PECL
的SSH2
扩展,主要形式有:
ssh2.shell:// |
该伪协议除了支持传统的URI
登录信息,ssh2
封装协议也支持通过URL
的主机(host)
部分来复用打开连接,用法如下所示:
ssh2.shell://user:pass@example.com:22/xterm |
下面为一个示例,用以从一个活动连接中打开字节流:
|
ogg:// 协议
音频流协议,用以读取OGG/Vorbis
格式的压缩音频编码,并能通过该伪协议写入或追加压缩音频数据,默认未激活,使用需要安装PECL
中的OGG/Vorbis
扩展。用法如下:
ogg://soundfile.ogg |
expect:// 协议
用以处理交互式的流,由expect://
封装协议打开的数据流PTY
提供了对进程stdio
、stdout
和stderr
的访问,默认未开启,使用须安装PECL
上的Expect
扩展。用法如下:
expect://command |
参考资料
PHP 支持和封装的协议
php:// 官方文档
PHP 可用过滤器列表
扩展阅读
php伪协议实现命令执行的七种姿势
PHP文件包含漏洞利用思路与Bypass总结
利用 phar 拓展 php 反序列化漏洞攻击面